From 454ffd181e205769f32554c56282daf706a60a00 Mon Sep 17 00:00:00 2001 From: Ivan Garcia Sainz-Aja Date: Fri, 14 Nov 2025 07:09:38 +0100 Subject: [PATCH] fix: $RefParser.java replaceWith$Ref keep references when posible related to: #18 --- pom.xml | 6 ++ .../zenwave360/jsonrefparser/$RefParser.java | 40 ++++++++++- .../jsonrefparser/AuthenticationValue.java | 39 +++++++++-- .../jsonrefparser/resolver/HttpResolver.java | 3 +- .../io/zenwave360/jsonrefparser/GH18.java | 5 +- .../resolver/HttpResolverHeaderTest.java | 69 +++++++++++++++++++ 6 files changed, 153 insertions(+), 9 deletions(-) create mode 100644 src/test/java/io/zenwave360/jsonrefparser/resolver/HttpResolverHeaderTest.java diff --git a/pom.xml b/pom.xml index 226e4c6..b2788c7 100644 --- a/pom.xml +++ b/pom.xml @@ -105,6 +105,12 @@ 1.2.13 test + + com.github.tomakehurst + wiremock-jre8 + 2.35.0 + test + diff --git a/src/main/java/io/zenwave360/jsonrefparser/$RefParser.java b/src/main/java/io/zenwave360/jsonrefparser/$RefParser.java index eb3146b..4105285 100644 --- a/src/main/java/io/zenwave360/jsonrefparser/$RefParser.java +++ b/src/main/java/io/zenwave360/jsonrefparser/$RefParser.java @@ -113,6 +113,20 @@ public class $RefParser { return this; } + public $RefParser withAuthenticationValues(AuthenticationValue... authenticationValue) { + if(authenticationValue != null) { + Arrays.stream(authenticationValue).forEach(this::withAuthentication); + } + return this; + } + + public $RefParser withAuthenticationValues(List authenticationValue) { + if(authenticationValue != null) { + authenticationValue.forEach(this::withAuthentication); + } + return this; + } + public $RefParser withResolver(RefFormat refFormat, Resolver resolver) { this.resolvers.put(refFormat, resolver); return this; @@ -304,8 +318,8 @@ private void dereference(ExtendedJsonContext jsonContext, Object value, String[] throw e; } } else if(value instanceof Map) { - // visit - ((Map) value).entrySet().forEach(e -> { + // visit - use ArrayList to avoid ConcurrentModificationException + new ArrayList<>(((Map) value).entrySet()).forEach(e -> { dereference(jsonContext, e.getValue(), ArrayUtils.add(paths, e.getKey()), currentFileURL); }); } else if(value instanceof List) { @@ -318,6 +332,28 @@ private void dereference(ExtendedJsonContext jsonContext, Object value, String[] } private void replaceWith$Ref(ExtendedJsonContext jsonContext, String jsonPath, Object resolved) { + Map original = jsonContext.read(jsonPath); + if(original.containsKey("$ref") && original.size() == 1) { + mergeResolvedIntoOriginal(jsonContext, jsonPath, resolved); + } else { + mergeResolvedAndReplaceOriginal(jsonContext, jsonPath, resolved); + } + } + + private void mergeResolvedIntoOriginal(ExtendedJsonContext jsonContext, String jsonPath, Object resolved) { + Map original = jsonContext.read(jsonPath); + if (resolved instanceof Map) { + for (Map.Entry entry : (original).entrySet()) { + if(!entry.getKey().equals("$ref")) { + ((Map) resolved).put(entry.getKey(), entry.getValue()); + } + } + } + jsonContext.set(jsonPath, resolved); + } + + private void mergeResolvedAndReplaceOriginal(ExtendedJsonContext jsonContext, String jsonPath, Object resolved) { + // losing original reference, they will become different objects Map original = jsonContext.read(jsonPath); Map replacement = new LinkedHashMap<>(); replacement.putAll(original); diff --git a/src/main/java/io/zenwave360/jsonrefparser/AuthenticationValue.java b/src/main/java/io/zenwave360/jsonrefparser/AuthenticationValue.java index 33ea3ee..ff890f8 100644 --- a/src/main/java/io/zenwave360/jsonrefparser/AuthenticationValue.java +++ b/src/main/java/io/zenwave360/jsonrefparser/AuthenticationValue.java @@ -21,6 +21,7 @@ public static enum AuthenticationType { private String value; private AuthenticationType type = AuthenticationType.HEADER; + private List urlPatterns = Arrays.asList("*"); private Predicate urlMatcher = ANY_MATCH; public AuthenticationValue() { @@ -60,6 +61,20 @@ public AuthenticationValue withUrlMatcher(Predicate urlMatcher) { return this; } + public AuthenticationValue withUrlPattern(String urlPattern) { + this.urlPatterns = List.of(urlPattern); + return this; + } + + public boolean matches(URL url) { + if(urlMatcher != null) { + return urlMatcher.test(url); + } else if(urlPatterns != null) { + return urlPatterns.stream().anyMatch(url.toString()::matches); + } + return ANY_MATCH.test(url); + } + public String getKey() { return this.key; } @@ -72,10 +87,6 @@ public AuthenticationType getType() { return this.type; } - public Predicate getUrlMatcher() { - return this.urlMatcher; - } - public void setKey(String key) { this.key = key; } @@ -91,4 +102,22 @@ public void setType(AuthenticationType type) { public void setUrlMatcher(Predicate urlMatcher) { this.urlMatcher = urlMatcher; } -} \ No newline at end of file + + public void setUrlPattern(String urlPattern) { + this.urlPatterns = List.of(urlPattern); + } + + public void setUrlPatterns(List urlPatterns) { + this.urlPatterns = urlPatterns; + } + + public String toString() { + return "AuthenticationValue{" + + "key='" + key + '\'' + + ", value='" + value + '\'' + + ", type=" + type + + ", urlPatterns=" + urlPatterns + + ", urlMatcher=" + urlMatcher + + '}'; + } +} diff --git a/src/main/java/io/zenwave360/jsonrefparser/resolver/HttpResolver.java b/src/main/java/io/zenwave360/jsonrefparser/resolver/HttpResolver.java index d344e82..6b77a8a 100644 --- a/src/main/java/io/zenwave360/jsonrefparser/resolver/HttpResolver.java +++ b/src/main/java/io/zenwave360/jsonrefparser/resolver/HttpResolver.java @@ -2,6 +2,7 @@ import io.zenwave360.jsonrefparser.$Ref; import io.zenwave360.jsonrefparser.AuthenticationValue; +import org.apache.commons.lang3.builder.ToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,7 +71,7 @@ protected String downloadUrlToString(String url, List auths final List header = new ArrayList<>(); if (auths != null && auths.size() > 0) { for (AuthenticationValue auth : auths) { - if (auth.getUrlMatcher() == null || auth.getUrlMatcher().test(inUrl)) { + if (auth.matches(inUrl)) { if (AuthenticationValue.AuthenticationType.HEADER == auth.getType()) { header.add(auth); } diff --git a/src/test/java/io/zenwave360/jsonrefparser/GH18.java b/src/test/java/io/zenwave360/jsonrefparser/GH18.java index 9b86a77..25ddc1f 100644 --- a/src/test/java/io/zenwave360/jsonrefparser/GH18.java +++ b/src/test/java/io/zenwave360/jsonrefparser/GH18.java @@ -13,8 +13,11 @@ public void test() throws Exception { refParser.withOptions(new $RefParserOptions().withOnCircular($RefParserOptions.OnCircular.SKIP)); $Refs refs = refParser.parse().dereference().getRefs(); String schema = refs.schema().toString(); + System.out.println("Actual schema output:"); + System.out.println(schema); + System.out.println("Contains 'lorem={title=lorem': " + schema.contains("lorem={title=lorem")); + System.out.println("Contains 'ipsum={title=ipsum': " + schema.contains("ipsum={title=ipsum")); Assert.assertTrue(schema.contains("lorem={title=lorem")); Assert.assertTrue(schema.contains("ipsum={title=ipsum")); - System.out.println(refs.schema()); } } diff --git a/src/test/java/io/zenwave360/jsonrefparser/resolver/HttpResolverHeaderTest.java b/src/test/java/io/zenwave360/jsonrefparser/resolver/HttpResolverHeaderTest.java new file mode 100644 index 0000000..905a884 --- /dev/null +++ b/src/test/java/io/zenwave360/jsonrefparser/resolver/HttpResolverHeaderTest.java @@ -0,0 +1,69 @@ +package io.zenwave360.jsonrefparser.resolver; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.zenwave360.jsonrefparser.$Ref; +import io.zenwave360.jsonrefparser.AuthenticationValue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.net.URI; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +public class HttpResolverHeaderTest { + + private WireMockServer wireMockServer; + private HttpResolver httpResolver; + + @Before + public void setup() { + wireMockServer = new WireMockServer(options().port(8089)); + wireMockServer.start(); + WireMock.configureFor("localhost", 8089); + + httpResolver = new HttpResolver(); + } + + @After + public void teardown() { + wireMockServer.stop(); + } + + @Test + public void testHeadersAreSent() throws Exception { + // Setup mock response + stubFor(get(urlEqualTo("/test.json")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("{\"test\": \"data\"}"))); + + httpResolver.withAuthentication(new AuthenticationValue() + .withHeader("Authorization", "Bearer test-token") + .withUrlMatcher(url -> url.getHost().equals("localhost"))); + + httpResolver.withAuthentication(new AuthenticationValue() + .withHeader("API-Key", "test-key") + .withUrlPattern("local*")); + + httpResolver.withAuthentication(new AuthenticationValue() + .withHeader("OTHER", "other") + .withUrlPattern("other")); + + // Make request + $Ref ref = $Ref.of("http://localhost:8089/test.json", null); + String result = httpResolver.resolve(ref); + + // Verify headers were sent + verify(getRequestedFor(urlEqualTo("/test.json")) + .withHeader("Authorization", equalTo("Bearer test-token")) + .withHeader("API-Key", equalTo("test-key")) + .withHeader("Accept", equalTo("application/json, application/yaml, */*")) + .withHeader("User-Agent", equalTo("Apache-HttpClient/$JSONSchemaRefParserJVM"))); + + System.out.println("Response: " + result); + } +}