From 5087bd83a6caa95cb991b57d6550e609571cf9c3 Mon Sep 17 00:00:00 2001 From: Witalij Berdinskich Date: Thu, 1 Jul 2021 01:58:00 +0300 Subject: [PATCH] Add JSON-java decoder and encoder (#1387) * Provides decoder and encoder for JSON-java * Rename feign-json to feign-org.json, add link to JSON web-site to README, use instanceof instead of references (JsonEncoder) * Return back feign-json, JSON-java * Fix parent version * Added JSON-java decoder to decoders list * Increase coverage: empty response body, parsing exception is caused by another exception * Use isAssignableFrom to allow methods return extended JSONObject and JSONArray classes Co-authored-by: Marvin Froeder --- json/README.md | 16 + json/pom.xml | 69 ++ .../src/main/java/feign/json/JsonDecoder.java | 86 +++ .../src/main/java/feign/json/JsonEncoder.java | 65 ++ .../test/java/feign/json/JsonCodecTest.java | 93 +++ .../test/java/feign/json/JsonDecoderTest.java | 218 ++++++ .../test/java/feign/json/JsonEncoderTest.java | 69 ++ .../feign/json/examples/GitHubExample.java | 48 ++ .../test/resources/fixtures/contributors.json | 632 ++++++++++++++++++ pom.xml | 8 + src/docs/overview-mindmap.iuml | 1 + 11 files changed, 1305 insertions(+) create mode 100644 json/README.md create mode 100644 json/pom.xml create mode 100644 json/src/main/java/feign/json/JsonDecoder.java create mode 100644 json/src/main/java/feign/json/JsonEncoder.java create mode 100644 json/src/test/java/feign/json/JsonCodecTest.java create mode 100644 json/src/test/java/feign/json/JsonDecoderTest.java create mode 100644 json/src/test/java/feign/json/JsonEncoderTest.java create mode 100644 json/src/test/java/feign/json/examples/GitHubExample.java create mode 100644 json/src/test/resources/fixtures/contributors.json diff --git a/json/README.md b/json/README.md new file mode 100644 index 00000000..108e6b8f --- /dev/null +++ b/json/README.md @@ -0,0 +1,16 @@ +JSON-java Codec +=================== + +This module adds support for encoding and decoding [JSON][] via [JSON-java][]. + +Add `JsonEncoder` and/or `JsonDecoder` to your `Feign.Builder` like so: + +```java +api = Feign.builder() + .decoder(new JsonDecoder()) + .encoder(new JsonEncoder()) + .target(GitHub.class, "https://api"); +``` + +[JSON]: https://www.json.org/json-en.html +[JSON-java]: https://github.com/stleary/JSON-java diff --git a/json/pom.xml b/json/pom.xml new file mode 100644 index 00000000..0cfdd797 --- /dev/null +++ b/json/pom.xml @@ -0,0 +1,69 @@ + + + + 4.0.0 + + + io.github.openfeign + parent + 11.3-SNAPSHOT + + + feign-json + Feign JSON-java + Feign JSON-java + + + ${project.basedir}/.. + 1.3 + 1.10.19 + + + + + ${project.groupId} + feign-core + + + org.json + json + + + org.hamcrest + hamcrest-core + ${hamcrest.version} + test + + + org.hamcrest + hamcrest-library + ${hamcrest.version} + test + + + org.mockito + mockito-all + ${mockito.version} + test + + + ${project.groupId} + feign-mock + test + + + diff --git a/json/src/main/java/feign/json/JsonDecoder.java b/json/src/main/java/feign/json/JsonDecoder.java new file mode 100644 index 00000000..578ae3ea --- /dev/null +++ b/json/src/main/java/feign/json/JsonDecoder.java @@ -0,0 +1,86 @@ +/** + * Copyright 2012-2021 The Feign Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package feign.json; + +import feign.Response; +import feign.codec.DecodeException; +import feign.codec.Decoder; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.lang.reflect.Type; +import static feign.Util.UTF_8; +import static java.lang.String.format; + +/** + * Decodes responses using JSON-java. + *

+ * Basic example with {@link feign.Feign.Builder}: + * + *

+ *   interface GitHub {
+ *
+ *     {@literal @}RequestLine("GET /repos/{owner}/{repo}/contributors")
+ *     JSONArray contributors({@literal @}Param("owner") String owner, {@literal @}Param("repo") String repo);
+ *
+ *   }
+ *
+ *   GitHub github = Feign.builder()
+ *                      .decoder(new JsonDecoder())
+ *                      .target(GitHub.class, "https://api.github.com");
+ *
+ *   JSONArray contributors = github.contributors("openfeign", "feign");
+ *
+ *   System.out.println(contributors.getJSONObject(0).getString("login"));
+ * 
+ */ +public class JsonDecoder implements Decoder { + + @Override + public Object decode(Response response, Type type) throws IOException, DecodeException { + if (response.body() == null) + return null; + try (Reader reader = response.body().asReader(response.charset())) { + Reader bodyReader = (reader.markSupported()) ? reader : new BufferedReader(reader); + bodyReader.mark(1); + if (bodyReader.read() == -1) { + return null; // Empty body + } + bodyReader.reset(); + return decodeJSON(response, type, bodyReader); + } catch (JSONException jsonException) { + if (jsonException.getCause() != null && jsonException.getCause() instanceof IOException) { + throw (IOException) jsonException.getCause(); + } + throw new DecodeException(response.status(), jsonException.getMessage(), response.request(), + jsonException); + } + } + + private Object decodeJSON(Response response, Type type, Reader reader) { + JSONTokener tokenizer = new JSONTokener(reader); + if (JSONObject.class.isAssignableFrom((Class) type)) + return new JSONObject(tokenizer); + else if (JSONArray.class.isAssignableFrom((Class) type)) + return new JSONArray(tokenizer); + else + throw new DecodeException(response.status(), + format("%s is not a type supported by this decoder.", type), response.request()); + } + +} diff --git a/json/src/main/java/feign/json/JsonEncoder.java b/json/src/main/java/feign/json/JsonEncoder.java new file mode 100644 index 00000000..b809f196 --- /dev/null +++ b/json/src/main/java/feign/json/JsonEncoder.java @@ -0,0 +1,65 @@ +/** + * Copyright 2012-2021 The Feign Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package feign.json; + +import feign.RequestTemplate; +import feign.codec.EncodeException; +import feign.codec.Encoder; +import org.json.JSONArray; +import org.json.JSONObject; +import java.lang.reflect.Type; +import static java.lang.String.format; + +/** + * Encodes requests using JSON-java. + *

+ * Basic example with {@link feign.Feign.Builder}: + * + *

+ *   interface GitHub {
+ *
+ *     {@literal @}RequestLine("POST /repos/{owner}/{repo}/contributors")
+ *     JSONObject create({@literal @}Param("owner") String owner,
+ *                       {@literal @}@Param("repo") String repo,
+ *                       JSONObject contributor);
+ *
+ *   }
+ *
+ *   GitHub github = Feign.builder()
+ *                      .decoder(new JsonDecoder())
+ *                      .encoder(new JsonEncoder())
+ *                      .target(GitHub.class, "https://api.github.com");
+ *
+ *   JSONObject contributor = new JSONObject();
+ *
+ *   contributor.put("login", "radio-rogal");
+ *   contributor.put("contributions", 0);
+ *   github.create("openfeign", "feign", contributor);
+ * 
+ */ +public class JsonEncoder implements Encoder { + + @Override + public void encode(Object object, Type bodyType, RequestTemplate template) + throws EncodeException { + if (object == null) + return; + if (object instanceof JSONArray || object instanceof JSONObject) { + template.body(object.toString()); + } else { + throw new EncodeException(format("%s is not a type supported by this encoder.", bodyType)); + } + } + +} diff --git a/json/src/test/java/feign/json/JsonCodecTest.java b/json/src/test/java/feign/json/JsonCodecTest.java new file mode 100644 index 00000000..c9998603 --- /dev/null +++ b/json/src/test/java/feign/json/JsonCodecTest.java @@ -0,0 +1,93 @@ +/** + * Copyright 2012-2021 The Feign Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package feign.json; + +import feign.Feign; +import feign.Param; +import feign.Request; +import feign.RequestLine; +import feign.mock.HttpMethod; +import feign.mock.MockClient; +import feign.mock.MockTarget; +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.Before; +import org.junit.Test; +import java.io.IOException; +import java.io.InputStream; +import static feign.Util.toByteArray; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + + +interface GitHub { + + @RequestLine("GET /repos/{owner}/{repo}/contributors") + JSONArray contributors(@Param("owner") String owner, @Param("repo") String repo); + + @RequestLine("POST /repos/{owner}/{repo}/contributors") + JSONObject create(@Param("owner") String owner, + @Param("repo") String repo, + JSONObject contributor); + +} + + +public class JsonCodecTest { + + private GitHub github; + private MockClient mockClient; + + @Before + public void setUp() { + mockClient = new MockClient(); + github = Feign.builder() + .decoder(new JsonDecoder()) + .encoder(new JsonEncoder()) + .client(mockClient) + .target(new MockTarget<>(GitHub.class)); + } + + @Test + public void decodes() throws IOException { + try (InputStream input = getClass().getResourceAsStream("/fixtures/contributors.json")) { + byte[] response = toByteArray(input); + mockClient.ok(HttpMethod.GET, "/repos/openfeign/feign/contributors", response); + JSONArray contributors = github.contributors("openfeign", "feign"); + assertThat(contributors.toList(), hasSize(30)); + contributors.forEach(contributor -> ((JSONObject) contributor).getString("login")); + } + } + + @Test + public void encodes() { + JSONObject contributor = new JSONObject(); + contributor.put("login", "radio-rogal"); + contributor.put("contributions", 0); + mockClient.ok(HttpMethod.POST, "/repos/openfeign/feign/contributors", + "{\"login\":\"radio-rogal\",\"contributions\":0}"); + JSONObject response = github.create("openfeign", "feign", contributor); + Request request = mockClient.verifyOne(HttpMethod.POST, "/repos/openfeign/feign/contributors"); + assertNotNull(request.body()); + String json = new String(request.body()); + assertThat(json, containsString("\"login\":\"radio-rogal\"")); + assertThat(json, containsString("\"contributions\":0")); + assertEquals("radio-rogal", response.getString("login")); + assertEquals(0, response.getInt("contributions")); + } + +} diff --git a/json/src/test/java/feign/json/JsonDecoderTest.java b/json/src/test/java/feign/json/JsonDecoderTest.java new file mode 100644 index 00000000..a90a0062 --- /dev/null +++ b/json/src/test/java/feign/json/JsonDecoderTest.java @@ -0,0 +1,218 @@ +/** + * Copyright 2012-2021 The Feign Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package feign.json; + +import feign.Request; +import feign.Response; +import feign.codec.DecodeException; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.BeforeClass; +import org.junit.Test; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Date; +import static feign.Util.UTF_8; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class JsonDecoderTest { + + private static JSONArray jsonArray; + private static JSONObject jsonObject; + private static Request request; + + @BeforeClass + public static void setUpClass() { + jsonObject = new JSONObject(); + jsonObject.put("a", "b"); + jsonObject.put("c", 1); + jsonArray = new JSONArray(); + jsonArray.put(jsonObject); + jsonArray.put(123); + request = Request.create(Request.HttpMethod.GET, "/qwerty", Collections.emptyMap(), + "xyz".getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8, null); + } + + @Test + public void decodesArray() throws IOException { + String json = "[{\"a\":\"b\",\"c\":1},123]"; + Response response = Response.builder() + .status(204) + .reason("OK") + .headers(Collections.emptyMap()) + .body(json, UTF_8) + .request(request) + .build(); + assertTrue(jsonArray.similar(new JsonDecoder().decode(response, JSONArray.class))); + } + + @Test + public void decodesObject() throws IOException { + String json = "{\"a\":\"b\",\"c\":1}"; + Response response = Response.builder() + .status(204) + .reason("OK") + .headers(Collections.emptyMap()) + .body(json, UTF_8) + .request(request) + .build(); + assertTrue(jsonObject.similar(new JsonDecoder().decode(response, JSONObject.class))); + } + + @Test + public void nullBodyDecodesToNull() throws IOException { + Response response = Response.builder() + .status(204) + .reason("OK") + .headers(Collections.emptyMap()) + .request(request) + .build(); + assertNull(new JsonDecoder().decode(response, JSONObject.class)); + } + + @Test + public void emptyBodyDecodesToNull() throws IOException { + Response response = Response.builder() + .status(204) + .reason("OK") + .headers(Collections.emptyMap()) + .body("", UTF_8) + .request(request) + .build(); + assertNull(new JsonDecoder().decode(response, JSONObject.class)); + } + + @Test + public void unknownTypeThrowsDecodeException() throws IOException { + String json = "[{\"a\":\"b\",\"c\":1},123]"; + Response response = Response.builder() + .status(204) + .reason("OK") + .headers(Collections.emptyMap()) + .body(json, UTF_8) + .request(request) + .build(); + Exception exception = assertThrows(DecodeException.class, + () -> new JsonDecoder().decode(response, Date.class)); + assertEquals("class java.util.Date is not a type supported by this decoder.", + exception.getMessage()); + } + + @Test + public void badJsonThrowsWrappedJSONException() throws IOException { + String json = "{\"a\":\"b\",\"c\":1}"; + Response response = Response.builder() + .status(204) + .reason("OK") + .headers(Collections.emptyMap()) + .body(json, UTF_8) + .request(request) + .build(); + Exception exception = assertThrows(DecodeException.class, + () -> new JsonDecoder().decode(response, JSONArray.class)); + assertEquals("A JSONArray text must start with '[' at 1 [character 2 line 1]", + exception.getMessage()); + assertTrue(exception.getCause() instanceof JSONException); + } + + @Test + public void causedByCommonException() throws IOException { + Response.Body body = mock(Response.Body.class); + when(body.asReader(any())).thenThrow(new JSONException("test exception", + new Exception("test cause exception"))); + Response response = Response.builder() + .status(204) + .reason("OK") + .headers(Collections.emptyMap()) + .body(body) + .request(request) + .build(); + Exception exception = assertThrows(DecodeException.class, + () -> new JsonDecoder().decode(response, JSONArray.class)); + assertEquals("test exception", exception.getMessage()); + } + + @Test + public void causedByIOException() throws IOException { + Response.Body body = mock(Response.Body.class); + when(body.asReader(any())).thenThrow(new JSONException("test exception", + new IOException("test cause exception"))); + Response response = Response.builder() + .status(204) + .reason("OK") + .headers(Collections.emptyMap()) + .body(body) + .request(request) + .build(); + Exception exception = assertThrows(IOException.class, + () -> new JsonDecoder().decode(response, JSONArray.class)); + assertEquals("test cause exception", exception.getMessage()); + } + + @Test + public void checkedException() throws IOException { + Response.Body body = mock(Response.Body.class); + when(body.asReader(any())).thenThrow(new IOException("test exception")); + Response response = Response.builder() + .status(204) + .reason("OK") + .headers(Collections.emptyMap()) + .body(body) + .request(request) + .build(); + Exception exception = assertThrows(IOException.class, + () -> new JsonDecoder().decode(response, JSONArray.class)); + assertEquals("test exception", exception.getMessage()); + } + + @Test + public void decodesExtendedArray() throws IOException { + String json = "[{\"a\":\"b\",\"c\":1},123]"; + Response response = Response.builder() + .status(204) + .reason("OK") + .headers(Collections.emptyMap()) + .body(json, UTF_8) + .request(request) + .build(); + assertTrue(jsonArray.similar(new JsonDecoder().decode(response, ExtendedJSONArray.class))); + } + + @Test + public void decodeExtendedObject() throws IOException { + String json = "{\"a\":\"b\",\"c\":1}"; + Response response = Response.builder() + .status(204) + .reason("OK") + .headers(Collections.emptyMap()) + .body(json, UTF_8) + .request(request) + .build(); + assertTrue(jsonObject.similar(new JsonDecoder().decode(response, ExtendedJSONObject.class))); + } + + static class ExtendedJSONArray extends JSONArray { + + } + + static class ExtendedJSONObject extends JSONObject { + + } + +} diff --git a/json/src/test/java/feign/json/JsonEncoderTest.java b/json/src/test/java/feign/json/JsonEncoderTest.java new file mode 100644 index 00000000..0d08fbd5 --- /dev/null +++ b/json/src/test/java/feign/json/JsonEncoderTest.java @@ -0,0 +1,69 @@ +/** + * Copyright 2012-2021 The Feign Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package feign.json; + +import feign.RequestTemplate; +import feign.codec.EncodeException; +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.Before; +import org.junit.Test; +import java.util.Date; +import static feign.Util.UTF_8; +import static org.junit.Assert.*; + +public class JsonEncoderTest { + + private JSONArray jsonArray; + private JSONObject jsonObject; + private RequestTemplate requestTemplate; + + @Before + public void setUp() { + jsonObject = new JSONObject(); + jsonObject.put("a", "b"); + jsonObject.put("c", 1); + jsonArray = new JSONArray(); + jsonArray.put(jsonObject); + jsonArray.put(123); + requestTemplate = new RequestTemplate(); + } + + @Test + public void encodesArray() { + new JsonEncoder().encode(jsonArray, JSONArray.class, requestTemplate); + assertEquals("[{\"a\":\"b\",\"c\":1},123]", new String(requestTemplate.body(), UTF_8)); + } + + @Test + public void encodesObject() { + new JsonEncoder().encode(jsonObject, JSONObject.class, requestTemplate); + assertEquals("{\"a\":\"b\",\"c\":1}", new String(requestTemplate.body(), UTF_8)); + } + + @Test + public void encodesNull() { + new JsonEncoder().encode(null, JSONObject.class, new RequestTemplate()); + assertNull(requestTemplate.body()); + } + + @Test + public void unknownTypeThrowsEncodeException() { + Exception exception = assertThrows(EncodeException.class, + () -> new JsonEncoder().encode("qwerty", Date.class, new RequestTemplate())); + assertEquals("class java.util.Date is not a type supported by this encoder.", + exception.getMessage()); + } + +} diff --git a/json/src/test/java/feign/json/examples/GitHubExample.java b/json/src/test/java/feign/json/examples/GitHubExample.java new file mode 100644 index 00000000..c884119e --- /dev/null +++ b/json/src/test/java/feign/json/examples/GitHubExample.java @@ -0,0 +1,48 @@ +/** + * Copyright 2012-2021 The Feign Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package feign.json.examples; + +import feign.Feign; +import feign.Param; +import feign.RequestLine; +import feign.json.JsonDecoder; +import org.json.JSONArray; +import org.json.JSONObject; + +interface GitHub { + + @RequestLine("GET /repos/{owner}/{repo}/contributors") + JSONArray contributors(@Param("owner") String owner, @Param("repo") String repo); + +} + + +/** + * adapted from {@code com.example.retrofit.GitHubClient} + */ +public class GitHubExample { + + public static void main(String... args) { + GitHub github = Feign.builder() + .decoder(new JsonDecoder()) + .target(GitHub.class, "https://api.github.com"); + + System.out.println("Let's fetch and print a list of the contributors to this library."); + JSONArray contributors = github.contributors("netflix", "feign"); + contributors.forEach(contributor -> { + System.out.println(((JSONObject) contributor).getString("login")); + }); + } + +} diff --git a/json/src/test/resources/fixtures/contributors.json b/json/src/test/resources/fixtures/contributors.json new file mode 100644 index 00000000..66a23953 --- /dev/null +++ b/json/src/test/resources/fixtures/contributors.json @@ -0,0 +1,632 @@ +[ + { + "login": "kdavisk6", + "id": 6013627, + "node_id": "MDQ6VXNlcjYwMTM2Mjc=", + "avatar_url": "https://avatars.githubusercontent.com/u/6013627?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/kdavisk6", + "html_url": "https://github.com/kdavisk6", + "followers_url": "https://api.github.com/users/kdavisk6/followers", + "following_url": "https://api.github.com/users/kdavisk6/following{/other_user}", + "gists_url": "https://api.github.com/users/kdavisk6/gists{/gist_id}", + "starred_url": "https://api.github.com/users/kdavisk6/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/kdavisk6/subscriptions", + "organizations_url": "https://api.github.com/users/kdavisk6/orgs", + "repos_url": "https://api.github.com/users/kdavisk6/repos", + "events_url": "https://api.github.com/users/kdavisk6/events{/privacy}", + "received_events_url": "https://api.github.com/users/kdavisk6/received_events", + "type": "User", + "site_admin": false, + "contributions": 115 + }, + { + "login": "velo", + "id": 136590, + "node_id": "MDQ6VXNlcjEzNjU5MA==", + "avatar_url": "https://avatars.githubusercontent.com/u/136590?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/velo", + "html_url": "https://github.com/velo", + "followers_url": "https://api.github.com/users/velo/followers", + "following_url": "https://api.github.com/users/velo/following{/other_user}", + "gists_url": "https://api.github.com/users/velo/gists{/gist_id}", + "starred_url": "https://api.github.com/users/velo/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/velo/subscriptions", + "organizations_url": "https://api.github.com/users/velo/orgs", + "repos_url": "https://api.github.com/users/velo/repos", + "events_url": "https://api.github.com/users/velo/events{/privacy}", + "received_events_url": "https://api.github.com/users/velo/received_events", + "type": "User", + "site_admin": false, + "contributions": 98 + }, + { + "login": "quidryan", + "id": 360255, + "node_id": "MDQ6VXNlcjM2MDI1NQ==", + "avatar_url": "https://avatars.githubusercontent.com/u/360255?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/quidryan", + "html_url": "https://github.com/quidryan", + "followers_url": "https://api.github.com/users/quidryan/followers", + "following_url": "https://api.github.com/users/quidryan/following{/other_user}", + "gists_url": "https://api.github.com/users/quidryan/gists{/gist_id}", + "starred_url": "https://api.github.com/users/quidryan/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/quidryan/subscriptions", + "organizations_url": "https://api.github.com/users/quidryan/orgs", + "repos_url": "https://api.github.com/users/quidryan/repos", + "events_url": "https://api.github.com/users/quidryan/events{/privacy}", + "received_events_url": "https://api.github.com/users/quidryan/received_events", + "type": "User", + "site_admin": false, + "contributions": 43 + }, + { + "login": "snyk-bot", + "id": 19733683, + "node_id": "MDQ6VXNlcjE5NzMzNjgz", + "avatar_url": "https://avatars.githubusercontent.com/u/19733683?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/snyk-bot", + "html_url": "https://github.com/snyk-bot", + "followers_url": "https://api.github.com/users/snyk-bot/followers", + "following_url": "https://api.github.com/users/snyk-bot/following{/other_user}", + "gists_url": "https://api.github.com/users/snyk-bot/gists{/gist_id}", + "starred_url": "https://api.github.com/users/snyk-bot/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/snyk-bot/subscriptions", + "organizations_url": "https://api.github.com/users/snyk-bot/orgs", + "repos_url": "https://api.github.com/users/snyk-bot/repos", + "events_url": "https://api.github.com/users/snyk-bot/events{/privacy}", + "received_events_url": "https://api.github.com/users/snyk-bot/received_events", + "type": "User", + "site_admin": false, + "contributions": 16 + }, + { + "login": "rspieldenner", + "id": 782102, + "node_id": "MDQ6VXNlcjc4MjEwMg==", + "avatar_url": "https://avatars.githubusercontent.com/u/782102?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/rspieldenner", + "html_url": "https://github.com/rspieldenner", + "followers_url": "https://api.github.com/users/rspieldenner/followers", + "following_url": "https://api.github.com/users/rspieldenner/following{/other_user}", + "gists_url": "https://api.github.com/users/rspieldenner/gists{/gist_id}", + "starred_url": "https://api.github.com/users/rspieldenner/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/rspieldenner/subscriptions", + "organizations_url": "https://api.github.com/users/rspieldenner/orgs", + "repos_url": "https://api.github.com/users/rspieldenner/repos", + "events_url": "https://api.github.com/users/rspieldenner/events{/privacy}", + "received_events_url": "https://api.github.com/users/rspieldenner/received_events", + "type": "User", + "site_admin": false, + "contributions": 14 + }, + { + "login": "davidmc24", + "id": 447825, + "node_id": "MDQ6VXNlcjQ0NzgyNQ==", + "avatar_url": "https://avatars.githubusercontent.com/u/447825?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/davidmc24", + "html_url": "https://github.com/davidmc24", + "followers_url": "https://api.github.com/users/davidmc24/followers", + "following_url": "https://api.github.com/users/davidmc24/following{/other_user}", + "gists_url": "https://api.github.com/users/davidmc24/gists{/gist_id}", + "starred_url": "https://api.github.com/users/davidmc24/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/davidmc24/subscriptions", + "organizations_url": "https://api.github.com/users/davidmc24/orgs", + "repos_url": "https://api.github.com/users/davidmc24/repos", + "events_url": "https://api.github.com/users/davidmc24/events{/privacy}", + "received_events_url": "https://api.github.com/users/davidmc24/received_events", + "type": "User", + "site_admin": false, + "contributions": 12 + }, + { + "login": "ahus1", + "id": 3957921, + "node_id": "MDQ6VXNlcjM5NTc5MjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/3957921?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/ahus1", + "html_url": "https://github.com/ahus1", + "followers_url": "https://api.github.com/users/ahus1/followers", + "following_url": "https://api.github.com/users/ahus1/following{/other_user}", + "gists_url": "https://api.github.com/users/ahus1/gists{/gist_id}", + "starred_url": "https://api.github.com/users/ahus1/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/ahus1/subscriptions", + "organizations_url": "https://api.github.com/users/ahus1/orgs", + "repos_url": "https://api.github.com/users/ahus1/repos", + "events_url": "https://api.github.com/users/ahus1/events{/privacy}", + "received_events_url": "https://api.github.com/users/ahus1/received_events", + "type": "User", + "site_admin": false, + "contributions": 6 + }, + { + "login": "carterkozak", + "id": 3854321, + "node_id": "MDQ6VXNlcjM4NTQzMjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/3854321?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/carterkozak", + "html_url": "https://github.com/carterkozak", + "followers_url": "https://api.github.com/users/carterkozak/followers", + "following_url": "https://api.github.com/users/carterkozak/following{/other_user}", + "gists_url": "https://api.github.com/users/carterkozak/gists{/gist_id}", + "starred_url": "https://api.github.com/users/carterkozak/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/carterkozak/subscriptions", + "organizations_url": "https://api.github.com/users/carterkozak/orgs", + "repos_url": "https://api.github.com/users/carterkozak/repos", + "events_url": "https://api.github.com/users/carterkozak/events{/privacy}", + "received_events_url": "https://api.github.com/users/carterkozak/received_events", + "type": "User", + "site_admin": false, + "contributions": 5 + }, + { + "login": "spencergibb", + "id": 594085, + "node_id": "MDQ6VXNlcjU5NDA4NQ==", + "avatar_url": "https://avatars.githubusercontent.com/u/594085?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/spencergibb", + "html_url": "https://github.com/spencergibb", + "followers_url": "https://api.github.com/users/spencergibb/followers", + "following_url": "https://api.github.com/users/spencergibb/following{/other_user}", + "gists_url": "https://api.github.com/users/spencergibb/gists{/gist_id}", + "starred_url": "https://api.github.com/users/spencergibb/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/spencergibb/subscriptions", + "organizations_url": "https://api.github.com/users/spencergibb/orgs", + "repos_url": "https://api.github.com/users/spencergibb/repos", + "events_url": "https://api.github.com/users/spencergibb/events{/privacy}", + "received_events_url": "https://api.github.com/users/spencergibb/received_events", + "type": "User", + "site_admin": false, + "contributions": 5 + }, + { + "login": "allenxwang", + "id": 1728105, + "node_id": "MDQ6VXNlcjE3MjgxMDU=", + "avatar_url": "https://avatars.githubusercontent.com/u/1728105?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/allenxwang", + "html_url": "https://github.com/allenxwang", + "followers_url": "https://api.github.com/users/allenxwang/followers", + "following_url": "https://api.github.com/users/allenxwang/following{/other_user}", + "gists_url": "https://api.github.com/users/allenxwang/gists{/gist_id}", + "starred_url": "https://api.github.com/users/allenxwang/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/allenxwang/subscriptions", + "organizations_url": "https://api.github.com/users/allenxwang/orgs", + "repos_url": "https://api.github.com/users/allenxwang/repos", + "events_url": "https://api.github.com/users/allenxwang/events{/privacy}", + "received_events_url": "https://api.github.com/users/allenxwang/received_events", + "type": "User", + "site_admin": false, + "contributions": 5 + }, + { + "login": "gimbimloki", + "id": 4572139, + "node_id": "MDQ6VXNlcjQ1NzIxMzk=", + "avatar_url": "https://avatars.githubusercontent.com/u/4572139?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/gimbimloki", + "html_url": "https://github.com/gimbimloki", + "followers_url": "https://api.github.com/users/gimbimloki/followers", + "following_url": "https://api.github.com/users/gimbimloki/following{/other_user}", + "gists_url": "https://api.github.com/users/gimbimloki/gists{/gist_id}", + "starred_url": "https://api.github.com/users/gimbimloki/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/gimbimloki/subscriptions", + "organizations_url": "https://api.github.com/users/gimbimloki/orgs", + "repos_url": "https://api.github.com/users/gimbimloki/repos", + "events_url": "https://api.github.com/users/gimbimloki/events{/privacy}", + "received_events_url": "https://api.github.com/users/gimbimloki/received_events", + "type": "User", + "site_admin": false, + "contributions": 4 + }, + { + "login": "jkschneider", + "id": 1697736, + "node_id": "MDQ6VXNlcjE2OTc3MzY=", + "avatar_url": "https://avatars.githubusercontent.com/u/1697736?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/jkschneider", + "html_url": "https://github.com/jkschneider", + "followers_url": "https://api.github.com/users/jkschneider/followers", + "following_url": "https://api.github.com/users/jkschneider/following{/other_user}", + "gists_url": "https://api.github.com/users/jkschneider/gists{/gist_id}", + "starred_url": "https://api.github.com/users/jkschneider/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/jkschneider/subscriptions", + "organizations_url": "https://api.github.com/users/jkschneider/orgs", + "repos_url": "https://api.github.com/users/jkschneider/repos", + "events_url": "https://api.github.com/users/jkschneider/events{/privacy}", + "received_events_url": "https://api.github.com/users/jkschneider/received_events", + "type": "User", + "site_admin": false, + "contributions": 4 + }, + { + "login": "nmiyake", + "id": 4267425, + "node_id": "MDQ6VXNlcjQyNjc0MjU=", + "avatar_url": "https://avatars.githubusercontent.com/u/4267425?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/nmiyake", + "html_url": "https://github.com/nmiyake", + "followers_url": "https://api.github.com/users/nmiyake/followers", + "following_url": "https://api.github.com/users/nmiyake/following{/other_user}", + "gists_url": "https://api.github.com/users/nmiyake/gists{/gist_id}", + "starred_url": "https://api.github.com/users/nmiyake/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/nmiyake/subscriptions", + "organizations_url": "https://api.github.com/users/nmiyake/orgs", + "repos_url": "https://api.github.com/users/nmiyake/repos", + "events_url": "https://api.github.com/users/nmiyake/events{/privacy}", + "received_events_url": "https://api.github.com/users/nmiyake/received_events", + "type": "User", + "site_admin": false, + "contributions": 4 + }, + { + "login": "rfalke", + "id": 1261007, + "node_id": "MDQ6VXNlcjEyNjEwMDc=", + "avatar_url": "https://avatars.githubusercontent.com/u/1261007?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/rfalke", + "html_url": "https://github.com/rfalke", + "followers_url": "https://api.github.com/users/rfalke/followers", + "following_url": "https://api.github.com/users/rfalke/following{/other_user}", + "gists_url": "https://api.github.com/users/rfalke/gists{/gist_id}", + "starred_url": "https://api.github.com/users/rfalke/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/rfalke/subscriptions", + "organizations_url": "https://api.github.com/users/rfalke/orgs", + "repos_url": "https://api.github.com/users/rfalke/repos", + "events_url": "https://api.github.com/users/rfalke/events{/privacy}", + "received_events_url": "https://api.github.com/users/rfalke/received_events", + "type": "User", + "site_admin": false, + "contributions": 4 + }, + { + "login": "SimY4", + "id": 1394639, + "node_id": "MDQ6VXNlcjEzOTQ2Mzk=", + "avatar_url": "https://avatars.githubusercontent.com/u/1394639?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/SimY4", + "html_url": "https://github.com/SimY4", + "followers_url": "https://api.github.com/users/SimY4/followers", + "following_url": "https://api.github.com/users/SimY4/following{/other_user}", + "gists_url": "https://api.github.com/users/SimY4/gists{/gist_id}", + "starred_url": "https://api.github.com/users/SimY4/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/SimY4/subscriptions", + "organizations_url": "https://api.github.com/users/SimY4/orgs", + "repos_url": "https://api.github.com/users/SimY4/repos", + "events_url": "https://api.github.com/users/SimY4/events{/privacy}", + "received_events_url": "https://api.github.com/users/SimY4/received_events", + "type": "User", + "site_admin": false, + "contributions": 3 + }, + { + "login": "bstick12", + "id": 1146861, + "node_id": "MDQ6VXNlcjExNDY4NjE=", + "avatar_url": "https://avatars.githubusercontent.com/u/1146861?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/bstick12", + "html_url": "https://github.com/bstick12", + "followers_url": "https://api.github.com/users/bstick12/followers", + "following_url": "https://api.github.com/users/bstick12/following{/other_user}", + "gists_url": "https://api.github.com/users/bstick12/gists{/gist_id}", + "starred_url": "https://api.github.com/users/bstick12/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/bstick12/subscriptions", + "organizations_url": "https://api.github.com/users/bstick12/orgs", + "repos_url": "https://api.github.com/users/bstick12/repos", + "events_url": "https://api.github.com/users/bstick12/events{/privacy}", + "received_events_url": "https://api.github.com/users/bstick12/received_events", + "type": "User", + "site_admin": false, + "contributions": 3 + }, + { + "login": "mstrYoda", + "id": 12763626, + "node_id": "MDQ6VXNlcjEyNzYzNjI2", + "avatar_url": "https://avatars.githubusercontent.com/u/12763626?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mstrYoda", + "html_url": "https://github.com/mstrYoda", + "followers_url": "https://api.github.com/users/mstrYoda/followers", + "following_url": "https://api.github.com/users/mstrYoda/following{/other_user}", + "gists_url": "https://api.github.com/users/mstrYoda/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mstrYoda/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mstrYoda/subscriptions", + "organizations_url": "https://api.github.com/users/mstrYoda/orgs", + "repos_url": "https://api.github.com/users/mstrYoda/repos", + "events_url": "https://api.github.com/users/mstrYoda/events{/privacy}", + "received_events_url": "https://api.github.com/users/mstrYoda/received_events", + "type": "User", + "site_admin": false, + "contributions": 3 + }, + { + "login": "jacob-meacham", + "id": 1624811, + "node_id": "MDQ6VXNlcjE2MjQ4MTE=", + "avatar_url": "https://avatars.githubusercontent.com/u/1624811?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/jacob-meacham", + "html_url": "https://github.com/jacob-meacham", + "followers_url": "https://api.github.com/users/jacob-meacham/followers", + "following_url": "https://api.github.com/users/jacob-meacham/following{/other_user}", + "gists_url": "https://api.github.com/users/jacob-meacham/gists{/gist_id}", + "starred_url": "https://api.github.com/users/jacob-meacham/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/jacob-meacham/subscriptions", + "organizations_url": "https://api.github.com/users/jacob-meacham/orgs", + "repos_url": "https://api.github.com/users/jacob-meacham/repos", + "events_url": "https://api.github.com/users/jacob-meacham/events{/privacy}", + "received_events_url": "https://api.github.com/users/jacob-meacham/received_events", + "type": "User", + "site_admin": false, + "contributions": 3 + }, + { + "login": "pnepywoda", + "id": 13909400, + "node_id": "MDQ6VXNlcjEzOTA5NDAw", + "avatar_url": "https://avatars.githubusercontent.com/u/13909400?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/pnepywoda", + "html_url": "https://github.com/pnepywoda", + "followers_url": "https://api.github.com/users/pnepywoda/followers", + "following_url": "https://api.github.com/users/pnepywoda/following{/other_user}", + "gists_url": "https://api.github.com/users/pnepywoda/gists{/gist_id}", + "starred_url": "https://api.github.com/users/pnepywoda/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/pnepywoda/subscriptions", + "organizations_url": "https://api.github.com/users/pnepywoda/orgs", + "repos_url": "https://api.github.com/users/pnepywoda/repos", + "events_url": "https://api.github.com/users/pnepywoda/events{/privacy}", + "received_events_url": "https://api.github.com/users/pnepywoda/received_events", + "type": "User", + "site_admin": false, + "contributions": 3 + }, + { + "login": "santhosh-tekuri", + "id": 1112271, + "node_id": "MDQ6VXNlcjExMTIyNzE=", + "avatar_url": "https://avatars.githubusercontent.com/u/1112271?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/santhosh-tekuri", + "html_url": "https://github.com/santhosh-tekuri", + "followers_url": "https://api.github.com/users/santhosh-tekuri/followers", + "following_url": "https://api.github.com/users/santhosh-tekuri/following{/other_user}", + "gists_url": "https://api.github.com/users/santhosh-tekuri/gists{/gist_id}", + "starred_url": "https://api.github.com/users/santhosh-tekuri/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/santhosh-tekuri/subscriptions", + "organizations_url": "https://api.github.com/users/santhosh-tekuri/orgs", + "repos_url": "https://api.github.com/users/santhosh-tekuri/repos", + "events_url": "https://api.github.com/users/santhosh-tekuri/events{/privacy}", + "received_events_url": "https://api.github.com/users/santhosh-tekuri/received_events", + "type": "User", + "site_admin": false, + "contributions": 3 + }, + { + "login": "jerzykrlk", + "id": 4005877, + "node_id": "MDQ6VXNlcjQwMDU4Nzc=", + "avatar_url": "https://avatars.githubusercontent.com/u/4005877?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/jerzykrlk", + "html_url": "https://github.com/jerzykrlk", + "followers_url": "https://api.github.com/users/jerzykrlk/followers", + "following_url": "https://api.github.com/users/jerzykrlk/following{/other_user}", + "gists_url": "https://api.github.com/users/jerzykrlk/gists{/gist_id}", + "starred_url": "https://api.github.com/users/jerzykrlk/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/jerzykrlk/subscriptions", + "organizations_url": "https://api.github.com/users/jerzykrlk/orgs", + "repos_url": "https://api.github.com/users/jerzykrlk/repos", + "events_url": "https://api.github.com/users/jerzykrlk/events{/privacy}", + "received_events_url": "https://api.github.com/users/jerzykrlk/received_events", + "type": "User", + "site_admin": false, + "contributions": 3 + }, + { + "login": "benmanbs", + "id": 1074810, + "node_id": "MDQ6VXNlcjEwNzQ4MTA=", + "avatar_url": "https://avatars.githubusercontent.com/u/1074810?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/benmanbs", + "html_url": "https://github.com/benmanbs", + "followers_url": "https://api.github.com/users/benmanbs/followers", + "following_url": "https://api.github.com/users/benmanbs/following{/other_user}", + "gists_url": "https://api.github.com/users/benmanbs/gists{/gist_id}", + "starred_url": "https://api.github.com/users/benmanbs/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/benmanbs/subscriptions", + "organizations_url": "https://api.github.com/users/benmanbs/orgs", + "repos_url": "https://api.github.com/users/benmanbs/repos", + "events_url": "https://api.github.com/users/benmanbs/events{/privacy}", + "received_events_url": "https://api.github.com/users/benmanbs/received_events", + "type": "User", + "site_admin": false, + "contributions": 2 + }, + { + "login": "schlosna", + "id": 54594, + "node_id": "MDQ6VXNlcjU0NTk0", + "avatar_url": "https://avatars.githubusercontent.com/u/54594?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/schlosna", + "html_url": "https://github.com/schlosna", + "followers_url": "https://api.github.com/users/schlosna/followers", + "following_url": "https://api.github.com/users/schlosna/following{/other_user}", + "gists_url": "https://api.github.com/users/schlosna/gists{/gist_id}", + "starred_url": "https://api.github.com/users/schlosna/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/schlosna/subscriptions", + "organizations_url": "https://api.github.com/users/schlosna/orgs", + "repos_url": "https://api.github.com/users/schlosna/repos", + "events_url": "https://api.github.com/users/schlosna/events{/privacy}", + "received_events_url": "https://api.github.com/users/schlosna/received_events", + "type": "User", + "site_admin": false, + "contributions": 2 + }, + { + "login": "dharmeshjogadia", + "id": 6071120, + "node_id": "MDQ6VXNlcjYwNzExMjA=", + "avatar_url": "https://avatars.githubusercontent.com/u/6071120?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/dharmeshjogadia", + "html_url": "https://github.com/dharmeshjogadia", + "followers_url": "https://api.github.com/users/dharmeshjogadia/followers", + "following_url": "https://api.github.com/users/dharmeshjogadia/following{/other_user}", + "gists_url": "https://api.github.com/users/dharmeshjogadia/gists{/gist_id}", + "starred_url": "https://api.github.com/users/dharmeshjogadia/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/dharmeshjogadia/subscriptions", + "organizations_url": "https://api.github.com/users/dharmeshjogadia/orgs", + "repos_url": "https://api.github.com/users/dharmeshjogadia/repos", + "events_url": "https://api.github.com/users/dharmeshjogadia/events{/privacy}", + "received_events_url": "https://api.github.com/users/dharmeshjogadia/received_events", + "type": "User", + "site_admin": false, + "contributions": 2 + }, + { + "login": "edio", + "id": 787439, + "node_id": "MDQ6VXNlcjc4NzQzOQ==", + "avatar_url": "https://avatars.githubusercontent.com/u/787439?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/edio", + "html_url": "https://github.com/edio", + "followers_url": "https://api.github.com/users/edio/followers", + "following_url": "https://api.github.com/users/edio/following{/other_user}", + "gists_url": "https://api.github.com/users/edio/gists{/gist_id}", + "starred_url": "https://api.github.com/users/edio/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/edio/subscriptions", + "organizations_url": "https://api.github.com/users/edio/orgs", + "repos_url": "https://api.github.com/users/edio/repos", + "events_url": "https://api.github.com/users/edio/events{/privacy}", + "received_events_url": "https://api.github.com/users/edio/received_events", + "type": "User", + "site_admin": false, + "contributions": 2 + }, + { + "login": "androidfred", + "id": 870822, + "node_id": "MDQ6VXNlcjg3MDgyMg==", + "avatar_url": "https://avatars.githubusercontent.com/u/870822?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/androidfred", + "html_url": "https://github.com/androidfred", + "followers_url": "https://api.github.com/users/androidfred/followers", + "following_url": "https://api.github.com/users/androidfred/following{/other_user}", + "gists_url": "https://api.github.com/users/androidfred/gists{/gist_id}", + "starred_url": "https://api.github.com/users/androidfred/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/androidfred/subscriptions", + "organizations_url": "https://api.github.com/users/androidfred/orgs", + "repos_url": "https://api.github.com/users/androidfred/repos", + "events_url": "https://api.github.com/users/androidfred/events{/privacy}", + "received_events_url": "https://api.github.com/users/androidfred/received_events", + "type": "User", + "site_admin": false, + "contributions": 2 + }, + { + "login": "stromnet", + "id": 668449, + "node_id": "MDQ6VXNlcjY2ODQ0OQ==", + "avatar_url": "https://avatars.githubusercontent.com/u/668449?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/stromnet", + "html_url": "https://github.com/stromnet", + "followers_url": "https://api.github.com/users/stromnet/followers", + "following_url": "https://api.github.com/users/stromnet/following{/other_user}", + "gists_url": "https://api.github.com/users/stromnet/gists{/gist_id}", + "starred_url": "https://api.github.com/users/stromnet/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/stromnet/subscriptions", + "organizations_url": "https://api.github.com/users/stromnet/orgs", + "repos_url": "https://api.github.com/users/stromnet/repos", + "events_url": "https://api.github.com/users/stromnet/events{/privacy}", + "received_events_url": "https://api.github.com/users/stromnet/received_events", + "type": "User", + "site_admin": false, + "contributions": 2 + }, + { + "login": "JonathanO", + "id": 1206546, + "node_id": "MDQ6VXNlcjEyMDY1NDY=", + "avatar_url": "https://avatars.githubusercontent.com/u/1206546?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/JonathanO", + "html_url": "https://github.com/JonathanO", + "followers_url": "https://api.github.com/users/JonathanO/followers", + "following_url": "https://api.github.com/users/JonathanO/following{/other_user}", + "gists_url": "https://api.github.com/users/JonathanO/gists{/gist_id}", + "starred_url": "https://api.github.com/users/JonathanO/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/JonathanO/subscriptions", + "organizations_url": "https://api.github.com/users/JonathanO/orgs", + "repos_url": "https://api.github.com/users/JonathanO/repos", + "events_url": "https://api.github.com/users/JonathanO/events{/privacy}", + "received_events_url": "https://api.github.com/users/JonathanO/received_events", + "type": "User", + "site_admin": false, + "contributions": 2 + }, + { + "login": "finnetrolle", + "id": 6028373, + "node_id": "MDQ6VXNlcjYwMjgzNzM=", + "avatar_url": "https://avatars.githubusercontent.com/u/6028373?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/finnetrolle", + "html_url": "https://github.com/finnetrolle", + "followers_url": "https://api.github.com/users/finnetrolle/followers", + "following_url": "https://api.github.com/users/finnetrolle/following{/other_user}", + "gists_url": "https://api.github.com/users/finnetrolle/gists{/gist_id}", + "starred_url": "https://api.github.com/users/finnetrolle/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/finnetrolle/subscriptions", + "organizations_url": "https://api.github.com/users/finnetrolle/orgs", + "repos_url": "https://api.github.com/users/finnetrolle/repos", + "events_url": "https://api.github.com/users/finnetrolle/events{/privacy}", + "received_events_url": "https://api.github.com/users/finnetrolle/received_events", + "type": "User", + "site_admin": false, + "contributions": 2 + }, + { + "login": "padilo", + "id": 783959, + "node_id": "MDQ6VXNlcjc4Mzk1OQ==", + "avatar_url": "https://avatars.githubusercontent.com/u/783959?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/padilo", + "html_url": "https://github.com/padilo", + "followers_url": "https://api.github.com/users/padilo/followers", + "following_url": "https://api.github.com/users/padilo/following{/other_user}", + "gists_url": "https://api.github.com/users/padilo/gists{/gist_id}", + "starred_url": "https://api.github.com/users/padilo/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/padilo/subscriptions", + "organizations_url": "https://api.github.com/users/padilo/orgs", + "repos_url": "https://api.github.com/users/padilo/repos", + "events_url": "https://api.github.com/users/padilo/events{/privacy}", + "received_events_url": "https://api.github.com/users/padilo/received_events", + "type": "User", + "site_admin": false, + "contributions": 2 + } +] diff --git a/pom.xml b/pom.xml index ee37f476..8924949b 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ jaxb jaxrs jaxrs2 + json okhttp googlehttpclient ribbon @@ -78,6 +79,7 @@ 2.8.7 1.7.30 1.60 + 20210307 4.13.1 2.12.3 @@ -320,6 +322,12 @@ ${bouncy.version} + + org.json + json + ${json.version} + + com.fasterxml.jackson.core jackson-databind diff --git a/src/docs/overview-mindmap.iuml b/src/docs/overview-mindmap.iuml index 972f9200..ab98025c 100644 --- a/src/docs/overview-mindmap.iuml +++ b/src/docs/overview-mindmap.iuml @@ -26,6 +26,7 @@ *** Jackson JAXB *** Jackson Jr *** Sax +*** JSON-java ** metrics *** Dropwizard Metrics 5 *** Micrometer