From 466e229a30732092ecb51a50e45d8b3f8005f36a Mon Sep 17 00:00:00 2001 From: Igor Artamonov Date: Sun, 21 Jun 2020 21:55:57 -0400 Subject: [PATCH] problem: polkadot started to use string for subscription id; v0.2.1 --- build.gradle | 2 +- .../polkaj/apiws/DecodeResponse.java | 42 ++++++++++++------- .../polkaj/apiws/DefaultSubscription.java | 6 +-- .../polkaj/apiws/PolkadotWsApi.java | 22 +++++----- .../emeraldpay/polkaj/apiws/WsResponse.java | 8 ++-- .../polkaj/apiws/DecodeResponseSpec.groovy | 28 ++++++------- .../apiws/DefaultSubscriptionSpec.groovy | 12 +++--- .../polkaj/apiws/PolkadotWsClientSpec.groovy | 6 +-- .../polkaj/apiws/WsResponseSpec.groovy | 8 ++-- 9 files changed, 72 insertions(+), 62 deletions(-) diff --git a/build.gradle b/build.gradle index bb3317f..a46dc66 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ apply plugin: 'jacoco' allprojects { group = 'io.emeraldpay.polkaj' - version = "0.2.0" + version = "0.2.1" repositories { mavenLocal() diff --git a/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/DecodeResponse.java b/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/DecodeResponse.java index b5beb26..1dc85ca 100644 --- a/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/DecodeResponse.java +++ b/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/DecodeResponse.java @@ -20,10 +20,10 @@ public class DecodeResponse { private final ObjectMapper objectMapper; - private final TypeMapping subscriptionMapping; - private final TypeMapping rpcMapping; + private final TypeMapping subscriptionMapping; + private final TypeMapping rpcMapping; - public DecodeResponse(ObjectMapper objectMapper, TypeMapping rpcMapping, TypeMapping subscriptionMapping) { + public DecodeResponse(ObjectMapper objectMapper, TypeMapping rpcMapping, TypeMapping subscriptionMapping) { this.objectMapper = objectMapper; this.rpcMapping = rpcMapping; this.subscriptionMapping = subscriptionMapping; @@ -36,8 +36,8 @@ public WsResponse decode(String json) throws IOException { throw new IllegalStateException("Not an object"); } String method = null; - WsResponse.IdValue value = null; - Preparsed preparsed = new Preparsed(objectMapper); + WsResponse.IdValue value = null; + Preparsed preparsed = new Preparsed<>(objectMapper); while (parser.nextToken() != JsonToken.END_OBJECT) { if (parser.currentToken() == null) { throw new IllegalStateException("JSON finished before data received"); @@ -107,8 +107,8 @@ protected RpcResponseError decodeError(JsonParser parser) throws IOException { } - protected WsResponse.IdValue decodeSubscription(TypeMapping typeMapping, JsonParser parser) throws IOException { - Preparsed preparsed = new Preparsed(objectMapper); + protected WsResponse.IdValue decodeSubscription(TypeMapping typeMapping, JsonParser parser) throws IOException { + Preparsed preparsed = new Preparsed<>(objectMapper); while (parser.nextToken() != JsonToken.END_OBJECT) { if (parser.currentToken() == null) { throw new IllegalStateException("JSON finished before data received"); @@ -116,7 +116,7 @@ protected WsResponse.IdValue decodeSubscription(TypeMapping typeMapping, JsonPar String field = parser.currentName(); if ("subscription".equals(field)) { - preparsed.id = decodeNumber(parser); + preparsed.id = decodeString(parser); preparsed.type = findType(typeMapping, preparsed.id); if (preparsed.isReady()) { return preparsed.build(); @@ -133,7 +133,7 @@ protected WsResponse.IdValue decodeSubscription(TypeMapping typeMapping, JsonPar throw new IllegalStateException("Either id or result not found in JSON"); } - private JavaType findType(TypeMapping typeMapping, Integer id) { + private JavaType findType(TypeMapping typeMapping, T id) { JavaType type; type = typeMapping.get(id); if (type == null) { @@ -152,14 +152,24 @@ private Integer decodeNumber(JsonParser parser) throws IOException { return parser.getIntValue(); } - public interface TypeMapping { - JavaType get(int id); + private String decodeString(JsonParser parser) throws IOException { + if (parser.currentToken() != JsonToken.VALUE_STRING) { + parser.nextToken(); + } + if (!parser.currentToken().isScalarValue()) { + throw new IllegalStateException("Id is not a string"); + } + return parser.getValueAsString(); + } + + public interface TypeMapping { + JavaType get(T id); } - private static class Preparsed { + private static class Preparsed { private final ObjectMapper objectMapper; - Integer id = null; + T id = null; JavaType type = null; TreeNode node = null; @@ -174,12 +184,12 @@ public boolean isReady() { error != null || (type != null && node != null); } - public WsResponse.IdValue build() throws IOException { + public WsResponse.IdValue build() throws IOException { if (id == null) { throw new IllegalStateException("Id is not set"); } if (error != null) { - return new WsResponse.IdValue(id, error); + return new WsResponse.IdValue(id, error); } if (type == null) { throw new IllegalStateException("Type is not set"); @@ -188,7 +198,7 @@ public WsResponse.IdValue build() throws IOException { Object value = objectMapper .readerFor(type) .readValue(node.traverse(objectMapper)); - return new WsResponse.IdValue(id, value); + return new WsResponse.IdValue(id, value); } throw new IllegalStateException("Not ready"); } diff --git a/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/DefaultSubscription.java b/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/DefaultSubscription.java index fb0e6c4..4348f3a 100644 --- a/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/DefaultSubscription.java +++ b/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/DefaultSubscription.java @@ -8,7 +8,7 @@ public class DefaultSubscription implements Subscription, Consumer> { - private Integer id; + private String id; private final JavaType type; private final String unsubscribeMethod; private final PolkadotWsApi client; @@ -20,7 +20,7 @@ public DefaultSubscription(JavaType type, String unsubscribeMethod, PolkadotWsAp this.client = client; } - public Integer getId() { + public String getId() { return id; } @@ -28,7 +28,7 @@ public JavaType getType() { return type; } - public void setId(int id) { + public void setId(String id) { if (this.id != null) { throw new IllegalStateException("Subscription id is already set to " + this.id); } diff --git a/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/PolkadotWsApi.java b/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/PolkadotWsApi.java index b1a8d74..6204cbb 100644 --- a/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/PolkadotWsApi.java +++ b/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/PolkadotWsApi.java @@ -28,7 +28,7 @@ public class PolkadotWsApi extends AbstractPolkadotApi implements AutoCloseable, private final AtomicInteger id = new AtomicInteger(0); private final AtomicReference webSocket = new AtomicReference<>(null); private final ConcurrentHashMap> execution = new ConcurrentHashMap<>(); - private final ConcurrentHashMap> subscriptions = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> subscriptions = new ConcurrentHashMap<>(); private final URI target; private final DecodeResponse decodeResponse; private final HttpClient httpClient; @@ -42,9 +42,9 @@ private PolkadotWsApi(URI target, HttpClient httpClient, ObjectMapper objectMapp this.target = target; this.httpClient = httpClient; this.onClose = onClose; - var rpcMapping = new DecodeResponse.TypeMapping() { + var rpcMapping = new DecodeResponse.TypeMapping() { @Override - public JavaType get(int id) { + public JavaType get(Integer id) { var x = execution.get(id); if (x == null) { return null; @@ -52,9 +52,9 @@ public JavaType get(int id) { return x.getType(); } }; - var subMapping = new DecodeResponse.TypeMapping() { + var subMapping = new DecodeResponse.TypeMapping() { @Override - public JavaType get(int id) { + public JavaType get(String id) { var x = subscriptions.get(id); if (x == null) { return null; @@ -161,7 +161,7 @@ public CompletableFuture execute(RpcCall call) { @Override public CompletableFuture> subscribe(SubscribeCall call) { var subscription = new DefaultSubscription(call.getResultType(objectMapper.getTypeFactory()), call.getUnsubscribe(), this); - var start = this.execute(RpcCall.create(Integer.class, call.getMethod(), call.getParams())); + var start = this.execute(RpcCall.create(String.class, call.getMethod(), call.getParams())); return start.thenApply(id -> { subscriptions.put(id, subscription); subscription.setId(id); @@ -197,7 +197,7 @@ public void accept(SubscriptionResponse response) { s.accept(new Subscription.Event(response.method, response.value)); } - public boolean removeSubscription(int id) { + public boolean removeSubscription(String id) { return subscriptions.remove(id) != null; } @@ -217,17 +217,17 @@ public void close() throws Exception { } static class SubscriptionResponse { - private final int id; + private final String id; private final String method; private final T value; - public SubscriptionResponse(int id, String method, T value) { + public SubscriptionResponse(String id, String method, T value) { this.id = id; this.method = method; this.value = value; } - public int getId() { + public String getId() { return id; } @@ -244,7 +244,7 @@ public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof SubscriptionResponse)) return false; SubscriptionResponse that = (SubscriptionResponse) o; - return id == that.id && + return id.equals(that.id) && Objects.equals(method, that.method) && Objects.equals(value, that.value); } diff --git a/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/WsResponse.java b/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/WsResponse.java index 3d0442b..07df5b5 100644 --- a/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/WsResponse.java +++ b/polkaj-api-ws/src/main/java/io/emeraldpay/polkaj/apiws/WsResponse.java @@ -86,16 +86,16 @@ public enum Type { /** * Pair of ID and associated Value */ - public static class IdValue { - private final int id; + public static class IdValue { + private final T id; private final Object value; - public IdValue(int id, Object value) { + public IdValue(T id, Object value) { this.id = id; this.value = value; } - public int getId() { + public T getId() { return id; } diff --git a/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/DecodeResponseSpec.groovy b/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/DecodeResponseSpec.groovy index 17cba0b..4fed793 100644 --- a/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/DecodeResponseSpec.groovy +++ b/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/DecodeResponseSpec.groovy @@ -14,9 +14,9 @@ class DecodeResponseSpec extends Specification { def "Decode rpc response with number"() { setup: - def json = '{"jsonrpc":"2.0","result":5,"id":1}' + def json = '{"jsonrpc":"2.0","result":"EsqruyKPnZvPZ6fr","id":1}' def mapping = Mock(DecodeResponse.TypeMapping) { - 1 * get(1) >> objectMapper.typeFactory.constructType(Integer.class) + 1 * get(1) >> objectMapper.typeFactory.constructType(String.class) } def decoder = new DecodeResponse(objectMapper, mapping, Stub(DecodeResponse.TypeMapping)) when: @@ -24,16 +24,16 @@ class DecodeResponseSpec extends Specification { then: act.type == WsResponse.Type.RPC with(act.asRpc()) { - result instanceof Integer - result == 5 + result instanceof String + result == "EsqruyKPnZvPZ6fr" } } def "Decode rpc response with number, when id comes first"() { setup: - def json = '{"jsonrpc":"2.0","id":3,"result":5}' + def json = '{"jsonrpc":"2.0","id":3,"result":"EsqruyKPnZvPZ6fr"}' def mapping = Mock(DecodeResponse.TypeMapping) { - 1 * get(3) >> objectMapper.typeFactory.constructType(Integer.class) + 1 * get(3) >> objectMapper.typeFactory.constructType(String.class) } def decoder = new DecodeResponse(objectMapper, mapping, Stub(DecodeResponse.TypeMapping)) when: @@ -41,8 +41,8 @@ class DecodeResponseSpec extends Specification { then: act.type == WsResponse.Type.RPC with(act.asRpc()) { - result instanceof Integer - result == 5 + result instanceof String + result == "EsqruyKPnZvPZ6fr" } } @@ -138,11 +138,11 @@ class DecodeResponseSpec extends Specification { ' "parentHash":"0xbe9110f6da6a19ac645a27472e459dcca6eaf4ee4b0b12700ca5d566eea9a638",' + ' "stateRoot":"0x57059722d680b591a469937449df772b95625d4230b39a0a7d855e16d597f168"' + ' },' + - ' "subscription":3' + + ' "subscription":"EsqruyKPnZvPZ6fr"' + ' }' + '}' def mapping = Mock(DecodeResponse.TypeMapping) { - 1 * get(3) >> objectMapper.typeFactory.constructType(BlockJson.Header.class) + 1 * get("EsqruyKPnZvPZ6fr") >> objectMapper.typeFactory.constructType(BlockJson.Header.class) } def decoder = new DecodeResponse(objectMapper, Stub(DecodeResponse.TypeMapping), mapping) when: @@ -151,7 +151,7 @@ class DecodeResponseSpec extends Specification { act.type == WsResponse.Type.SUBSCRIPTION PolkadotWsApi.SubscriptionResponse event = act.asEvent() event.method == "chain_newHead" - event.id == 3 + event.id == "EsqruyKPnZvPZ6fr" event.value instanceof BlockJson.Header with((BlockJson.Header)event.value) { number == 0x1d878c @@ -167,7 +167,7 @@ class DecodeResponseSpec extends Specification { '"jsonrpc":"2.0",' + '"method":"chain_newHead",' + '"params":{' + - ' "subscription":3,' + + ' "subscription":"EsqruyKPnZvPZ6fr",' + ' "result":{' + ' "digest":{"logs":["0x06424142453402d90000004077c40f00000000","0x05424142450101a0085dbd50d943878845263fa4d2bd8259cde78692f1e22488227843057d5a3101909f2bdfb492e6da3f63413366c9e189c7bf4bd62ae10607fe0c1550dc4d88"]},' + ' "extrinsicsRoot":"0x9869230c3cc05051ce9afef4458d2515fb2141bfd3bdcd88292f41e17ea00ae7",' + @@ -178,7 +178,7 @@ class DecodeResponseSpec extends Specification { ' }' + '}' def mapping = Mock(DecodeResponse.TypeMapping) { - 1 * get(3) >> objectMapper.typeFactory.constructType(BlockJson.Header.class) + 1 * get("EsqruyKPnZvPZ6fr") >> objectMapper.typeFactory.constructType(BlockJson.Header.class) } def decoder = new DecodeResponse(objectMapper, Stub(DecodeResponse.TypeMapping), mapping) when: @@ -187,7 +187,7 @@ class DecodeResponseSpec extends Specification { act.type == WsResponse.Type.SUBSCRIPTION PolkadotWsApi.SubscriptionResponse event = act.asEvent() event.method == "chain_newHead" - event.id == 3 + event.id == "EsqruyKPnZvPZ6fr" event.value instanceof BlockJson.Header with((BlockJson.Header)event.value) { number == 0x1d878c diff --git a/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/DefaultSubscriptionSpec.groovy b/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/DefaultSubscriptionSpec.groovy index 47c5ea7..c000eb6 100644 --- a/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/DefaultSubscriptionSpec.groovy +++ b/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/DefaultSubscriptionSpec.groovy @@ -11,12 +11,12 @@ class DefaultSubscriptionSpec extends Specification { def "Id is immutable"() { when: def s = new DefaultSubscription(null, "test", null) - s.setId(101) + s.setId("EsqruyKPnZvPZ6fr") then: - s.getId() == 101 + s.getId() == "EsqruyKPnZvPZ6fr" when: - s.setId(102) + s.setId("EsqruyKPnZvPZ6fr") then: thrown(IllegalStateException) } @@ -46,11 +46,11 @@ class DefaultSubscriptionSpec extends Specification { def client = Mock(PolkadotWsApi) when: def s = new DefaultSubscription(null, "untest", client) - s.setId(10) + s.setId("EsqruyKPnZvPZ6fr") s.close() then: - 1 * client.execute(RpcCall.create(Boolean.class, "untest", [10])) >> CompletableFuture.completedFuture(true) - 1 * client.removeSubscription(10) + 1 * client.execute(RpcCall.create(Boolean.class, "untest", ["EsqruyKPnZvPZ6fr"])) >> CompletableFuture.completedFuture(true) + 1 * client.removeSubscription("EsqruyKPnZvPZ6fr") } def "Close does nothing if not initialized"() { diff --git a/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/PolkadotWsClientSpec.groovy b/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/PolkadotWsClientSpec.groovy index 60f7496..8f17610 100644 --- a/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/PolkadotWsClientSpec.groovy +++ b/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/PolkadotWsClientSpec.groovy @@ -49,7 +49,7 @@ class PolkadotWsClientSpec extends Specification { setup: List> received = [] when: - server.onNextReply('{"jsonrpc":"2.0","result":101,"id":0}') + server.onNextReply('{"jsonrpc":"2.0","result":"EsqruyKPnZvPZ6fr","id":0}') def f = client.subscribe(SubscribeCall.create(BlockJson.Header.class, "chain_subscribeNewHead", "chain_unsubscribeNewHead")) def sub = f.get(TIMEOUT, TimeUnit.SECONDS) sub.handler({ event -> @@ -58,7 +58,7 @@ class PolkadotWsClientSpec extends Specification { result: event.result ]) }) - server.reply('{"jsonrpc":"2.0","method":"chain_newHead","params":{"result":{"digest":{"logs":[]},"extrinsicsRoot":"0x9869230c3cc05051ce9afef4458d2515fb2141bfd3bdcd88292f41e17ea00ae7","number":"0x1d878c","parentHash":"0xbe9110f6da6a19ac645a27472e459dcca6eaf4ee4b0b12700ca5d566eea9a638","stateRoot":"0x57059722d680b591a469937449df772b95625d4230b39a0a7d855e16d597f168"},"subscription":101}}') + server.reply('{"jsonrpc":"2.0","method":"chain_newHead","params":{"result":{"digest":{"logs":[]},"extrinsicsRoot":"0x9869230c3cc05051ce9afef4458d2515fb2141bfd3bdcd88292f41e17ea00ae7","number":"0x1d878c","parentHash":"0xbe9110f6da6a19ac645a27472e459dcca6eaf4ee4b0b12700ca5d566eea9a638","stateRoot":"0x57059722d680b591a469937449df772b95625d4230b39a0a7d855e16d597f168"},"subscription":"EsqruyKPnZvPZ6fr"}}') Thread.sleep(SLEEP) sub.close() Thread.sleep(SLEEP) @@ -69,7 +69,7 @@ class PolkadotWsClientSpec extends Specification { server.received.size() == 2 server.received[0].value == '{"jsonrpc":"2.0","id":0,"method":"chain_subscribeNewHead","params":[]}' - server.received[1].value == '{"jsonrpc":"2.0","id":1,"method":"chain_unsubscribeNewHead","params":[101]}' + server.received[1].value == '{"jsonrpc":"2.0","id":1,"method":"chain_unsubscribeNewHead","params":["EsqruyKPnZvPZ6fr"]}' } def "Make a request"() { diff --git a/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/WsResponseSpec.groovy b/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/WsResponseSpec.groovy index 5e6dd41..6185243 100644 --- a/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/WsResponseSpec.groovy +++ b/polkaj-api-ws/src/test/groovy/io/emeraldpay/polkaj/apiws/WsResponseSpec.groovy @@ -17,11 +17,11 @@ class WsResponseSpec extends Specification { def "Creates subscription response"() { when: - def act = WsResponse.subscription(new PolkadotWsApi.SubscriptionResponse(1, "test", "test")) + def act = WsResponse.subscription(new PolkadotWsApi.SubscriptionResponse("EsqruyKPnZvPZ6fr", "test", "test")) then: act.getType() == WsResponse.Type.SUBSCRIPTION - act.getValue() == new PolkadotWsApi.SubscriptionResponse(1, "test", "test") - act.asEvent() == new PolkadotWsApi.SubscriptionResponse(1, "test", "test") + act.getValue() == new PolkadotWsApi.SubscriptionResponse("EsqruyKPnZvPZ6fr", "test", "test") + act.asEvent() == new PolkadotWsApi.SubscriptionResponse("EsqruyKPnZvPZ6fr", "test", "test") } def "Cannot cast rcp to event"() { @@ -33,7 +33,7 @@ class WsResponseSpec extends Specification { def "Cannot cast event to rpc"() { when: - WsResponse.subscription(new PolkadotWsApi.SubscriptionResponse(1, "test", "test")).asRpc() + WsResponse.subscription(new PolkadotWsApi.SubscriptionResponse("EsqruyKPnZvPZ6fr", "test", "test")).asRpc() then: thrown(ClassCastException) }