From 6034617b61a7c453f62bc2cf6ef0158428147a33 Mon Sep 17 00:00:00 2001 From: zashraf1985 Date: Wed, 27 Jul 2022 17:23:14 -0700 Subject: [PATCH 1/6] Development complete, Unit tests remaining. --- .../ab/internal/JsonParserProvider.java | 59 ++++++++++++++++ .../ab/odp/parser/ResponseJsonParser.java | 7 ++ .../odp/parser/ResponseJsonParserFactory.java | 34 +++++++++ .../ab/odp/parser/impl/GsonParser.java | 26 +++++++ .../ab/odp/parser/impl/JacksonParser.java | 37 ++++++++++ .../ab/odp/parser/impl/JsonParser.java | 24 +++++++ .../ab/odp/parser/impl/JsonSimpleParser.java | 37 ++++++++++ .../ab/odp/parser/impl/GsonParserTest.java | 18 +++++ .../ab/odp/parser/impl/JacksonParserTest.java | 18 +++++ .../ab/odp/parser/impl/JsonParserTest.java | 18 +++++ .../odp/parser/impl/JsonSimpleParserTest.java | 18 +++++ .../com/optimizely/ab/odp/ODPApiManager.java | 70 +++++++++++++++++++ 12 files changed, 366 insertions(+) create mode 100644 core-api/src/main/java/com/optimizely/ab/internal/JsonParserProvider.java create mode 100644 core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParser.java create mode 100644 core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactory.java create mode 100644 core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java create mode 100644 core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JacksonParser.java create mode 100644 core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java create mode 100644 core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParser.java create mode 100644 core-api/src/test/java/com/optimizely/ab/odp/parser/impl/GsonParserTest.java create mode 100644 core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JacksonParserTest.java create mode 100644 core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonParserTest.java create mode 100644 core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParserTest.java create mode 100644 core-httpclient-impl/src/main/java/com/optimizely/ab/odp/ODPApiManager.java diff --git a/core-api/src/main/java/com/optimizely/ab/internal/JsonParserProvider.java b/core-api/src/main/java/com/optimizely/ab/internal/JsonParserProvider.java new file mode 100644 index 000000000..fb8f28325 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/internal/JsonParserProvider.java @@ -0,0 +1,59 @@ +package com.optimizely.ab.internal; + +import com.optimizely.ab.config.parser.MissingJsonParserException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public enum JsonParserProvider { + GSON("com.google.gson.Gson"), + JACKSON("com.fasterxml.jackson.databind.ObjectMapper" ), + JSON("org.json.JSONObject"), + JSON_SIMPLE("org.json.simple.JSONObject"); + + private static final Logger logger = LoggerFactory.getLogger(JsonParserProvider.class); + + private final String className; + + JsonParserProvider(String className) { + this.className = className; + } + + private boolean isAvailable() { + try { + Class.forName(className); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + + public static JsonParserProvider getDefaultParser() { + String defaultParserName = PropertyUtils.get("default_parser"); + + if (defaultParserName != null) { + try { + JsonParserProvider parser = JsonParserProvider.valueOf(defaultParserName); + if (parser.isAvailable()) { + logger.debug("using json parser: {}, based on override config", parser.className); + return parser; + } + + logger.warn("configured parser {} is not available in the classpath", defaultParserName); + } catch (IllegalArgumentException e) { + logger.warn("configured parser {} is not a valid value", defaultParserName); + } + } + + for (JsonParserProvider parser: JsonParserProvider.values()) { + if (!parser.isAvailable()) { + continue; + } + + logger.info("using json parser: {}", parser.className); + return parser; + } + + throw new MissingJsonParserException("unable to locate a JSON parser. " + + "Please see for more information"); + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParser.java new file mode 100644 index 000000000..5d87edec1 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParser.java @@ -0,0 +1,7 @@ +package com.optimizely.ab.odp.parser; + +import java.util.List; + +public interface ResponseJsonParser { + public List parseQualifiedSegments(String responseJson); +} diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactory.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactory.java new file mode 100644 index 000000000..337c37418 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactory.java @@ -0,0 +1,34 @@ +package com.optimizely.ab.odp.parser; + +import com.optimizely.ab.internal.JsonParserProvider; +import com.optimizely.ab.odp.parser.impl.GsonParser; +import com.optimizely.ab.odp.parser.impl.JacksonParser; +import com.optimizely.ab.odp.parser.impl.JsonParser; +import com.optimizely.ab.odp.parser.impl.JsonSimpleParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ResponseJsonParserFactory { + private static final Logger logger = LoggerFactory.getLogger(ResponseJsonParserFactory.class); + + public static ResponseJsonParser getParser() { + JsonParserProvider parserProvider = JsonParserProvider.getDefaultParser(); + ResponseJsonParser jsonParser = null; + switch (parserProvider) { + case GSON: + jsonParser = new GsonParser(); + break; + case JACKSON: + jsonParser = new JacksonParser(); + break; + case JSON: + jsonParser = new JsonParser(); + break; + case JSON_SIMPLE: + jsonParser = new JsonSimpleParser(); + break; + } + logger.info("Using " + parserProvider.toString() + " parser"); + return jsonParser; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java new file mode 100644 index 000000000..194e5ec4a --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java @@ -0,0 +1,26 @@ +package com.optimizely.ab.odp.parser.impl; + +import com.google.gson.*; +import com.google.gson.JsonParser; +import com.optimizely.ab.odp.parser.ResponseJsonParser; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class GsonParser implements ResponseJsonParser { + @Override + public List parseQualifiedSegments(String responseJson) { + List parsedSegments = new ArrayList<>(); + JsonObject root = JsonParser.parseString(responseJson).getAsJsonObject(); + JsonArray edges = root.getAsJsonObject("data").getAsJsonObject("customer").getAsJsonObject("audiences").getAsJsonArray("edges"); + for (int i = 0; i < edges.size() ; i++) { + JsonObject node = edges.get(i).getAsJsonObject().getAsJsonObject("node"); + if (node.has("state") && node.get("state").getAsString().equals("qualified")) { + parsedSegments.add(node.get("name").getAsString()); + } + } + return parsedSegments; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JacksonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JacksonParser.java new file mode 100644 index 000000000..3e9a6b610 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JacksonParser.java @@ -0,0 +1,37 @@ +package com.optimizely.ab.odp.parser.impl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.optimizely.ab.odp.parser.ResponseJsonParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; + +import java.util.List; + +public class JacksonParser implements ResponseJsonParser { + private static final Logger logger = LoggerFactory.getLogger(JacksonParser.class); + + @Override + public List parseQualifiedSegments(String responseJson) { + ObjectMapper objectMapper = new ObjectMapper(); + List parsedSegments = new ArrayList<>(); + JsonNode node; + try { + node = objectMapper.readTree(responseJson); + } catch (JsonProcessingException e) { + logger.error("Error parsing qualified segments from response", e); + return null; + } + JsonNode edges = node.path("data").path("customer").path("audiences").path("edges"); + for (JsonNode edgeNode: edges) { + String state = edgeNode.path("node").path("state").asText(); + if (state.equals("qualified")) { + parsedSegments.add(edgeNode.path("node").path("name").asText()); + } + } + return parsedSegments; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java new file mode 100644 index 000000000..9d373dd7e --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java @@ -0,0 +1,24 @@ +package com.optimizely.ab.odp.parser.impl; + +import com.optimizely.ab.odp.parser.ResponseJsonParser; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class JsonParser implements ResponseJsonParser { + @Override + public List parseQualifiedSegments(String responseJson) { + List parsedSegments = new ArrayList<>(); + JSONObject root = new JSONObject(responseJson); + JSONArray edges = root.getJSONObject("data").getJSONObject("customer").getJSONObject("audiences").getJSONArray("edges"); + for (int i = 0; i < edges.length(); i++) { + JSONObject node = edges.getJSONObject(i).getJSONObject("node"); + if (node.has("state") && node.getString("state").equals("qualified")) { + parsedSegments.add(node.getString("name")); + } + } + return parsedSegments; + } +} diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParser.java new file mode 100644 index 000000000..320897c13 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParser.java @@ -0,0 +1,37 @@ +package com.optimizely.ab.odp.parser.impl; + +import com.optimizely.ab.odp.parser.ResponseJsonParser; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +public class JsonSimpleParser implements ResponseJsonParser { + private static final Logger logger = LoggerFactory.getLogger(JsonSimpleParser.class); + + @Override + public List parseQualifiedSegments(String responseJson) { + List parsedSegments = new ArrayList<>(); + JSONParser parser = new JSONParser(); + JSONObject root = null; + try { + root = (JSONObject) parser.parse(responseJson); + JSONArray edges = (JSONArray)((JSONObject)((JSONObject)(((JSONObject) root.get("data"))).get("customer")).get("audiences")).get("edges"); + for (int i = 0; i < edges.size(); i++) { + JSONObject node = (JSONObject) ((JSONObject) edges.get(i)).get("node"); + if (node.containsKey("state") && (node.get("state")).equals("qualified")) { + parsedSegments.add((String) node.get("name")); + } + } + return parsedSegments; + } catch (ParseException e) { + logger.error("Error parsing qualified segments from response", e); + return null; + } + } +} diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/GsonParserTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/GsonParserTest.java new file mode 100644 index 000000000..f0a0ef45b --- /dev/null +++ b/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/GsonParserTest.java @@ -0,0 +1,18 @@ +package com.optimizely.ab.odp.parser.impl; + +import com.optimizely.ab.odp.parser.ResponseJsonParser; +import junit.framework.TestCase; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +public class GsonParserTest extends TestCase { + @Test + public void test1() { + String responseToParse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; + ResponseJsonParser jsonParser = new GsonParser(); + List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + assertEquals(Arrays.asList("has_email", "has_email_opted_in"), parsedSegments); + } +} diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JacksonParserTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JacksonParserTest.java new file mode 100644 index 000000000..40847c763 --- /dev/null +++ b/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JacksonParserTest.java @@ -0,0 +1,18 @@ +package com.optimizely.ab.odp.parser.impl; + +import com.optimizely.ab.odp.parser.ResponseJsonParser; +import junit.framework.TestCase; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +public class JacksonParserTest extends TestCase { + @Test + public void test1() { + String responseToParse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; + ResponseJsonParser jsonParser = new JacksonParser(); + List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + assertEquals(Arrays.asList("has_email", "has_email_opted_in"), parsedSegments); + } +} diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonParserTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonParserTest.java new file mode 100644 index 000000000..59f4761d9 --- /dev/null +++ b/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonParserTest.java @@ -0,0 +1,18 @@ +package com.optimizely.ab.odp.parser.impl; + +import com.optimizely.ab.odp.parser.ResponseJsonParser; +import junit.framework.TestCase; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +public class JsonParserTest extends TestCase { + @Test + public void test1() { + String responseToParse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; + ResponseJsonParser jsonParser = new JsonParser(); + List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + assertEquals(Arrays.asList("has_email", "has_email_opted_in"), parsedSegments); + } +} diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParserTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParserTest.java new file mode 100644 index 000000000..553fedac8 --- /dev/null +++ b/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParserTest.java @@ -0,0 +1,18 @@ +package com.optimizely.ab.odp.parser.impl; + +import com.optimizely.ab.odp.parser.ResponseJsonParser; +import junit.framework.TestCase; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +public class JsonSimpleParserTest extends TestCase { + @Test + public void test1() { + String responseToParse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; + ResponseJsonParser jsonParser = new JsonSimpleParser(); + List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + assertEquals(Arrays.asList("has_email", "has_email_opted_in"), parsedSegments); + } +} diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/ODPApiManager.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/ODPApiManager.java new file mode 100644 index 000000000..05f8b543d --- /dev/null +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/ODPApiManager.java @@ -0,0 +1,70 @@ +package com.optimizely.ab.odp; + +import com.optimizely.ab.event.AsyncEventHandler; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.List; + +public class ODPApiManager { + + private static final Logger logger = LoggerFactory.getLogger(ODPApiManager.class); + + private String getSegmentsStringForRequest(List segmentsList) { + StringBuilder segmentsString = new StringBuilder(); + for (int i = 0; i < segmentsList.size(); i++) { + if (i > 0) { + segmentsString.append(", "); + } + segmentsString.append("\\\"").append(segmentsList.get(i)).append("\\\""); + } + return segmentsString.toString(); + } + + public String fetchQualifiedSegments(String apiKey, String apiEndpoint, String userKey, String userValue, List segmentsToCheck) { + HttpPost request = new HttpPost(apiEndpoint); + String segmentsString = getSegmentsStringForRequest(segmentsToCheck); + String requestPayload = String.format("{\"query\": \"query {customer(%s: \\\"%s\\\") {audiences(subset: [%s]) {edges {node {name state}}}}}\"}", userKey, userValue, segmentsString); + + try { + request.setEntity(new StringEntity(requestPayload)); + } catch (UnsupportedEncodingException e) { + logger.warn("Error encoding request payload", e); + } + request.setHeader("x-api-key", apiKey); + request.setHeader("content-type", "application/json"); + HttpClient client = HttpClientBuilder.create().build(); + + + HttpResponse response = null; + try { + response = client.execute(request); + } catch (IOException e) { + logger.error("Error retrieving response from ODP service", e); + return null; + } + + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + StatusLine statusLine = response.getStatusLine(); + logger.error(String.format("Unexpected response from ODP server, Response code: %d, %s", statusLine.getStatusCode(), statusLine.getReasonPhrase())); + return null; + } + + try { + return EntityUtils.toString(response.getEntity()); + } catch (IOException e) { + logger.error("Error converting ODP segments response to string", e); + } + return null; + } +} From 899fa8821d45a9ecf359b0d52acb0f76e3c15bc7 Mon Sep 17 00:00:00 2001 From: zashraf1985 Date: Wed, 27 Jul 2022 23:31:59 -0700 Subject: [PATCH 2/6] Added parameterized tests for parsers --- .../ab/odp/parser/impl/GsonParser.java | 25 +++++--- .../ab/odp/parser/impl/JsonParser.java | 24 ++++--- .../ab/odp/parser/ResponseJsonParserTest.java | 62 +++++++++++++++++++ .../ab/odp/parser/impl/GsonParserTest.java | 18 ------ .../ab/odp/parser/impl/JacksonParserTest.java | 18 ------ .../ab/odp/parser/impl/JsonParserTest.java | 18 ------ .../odp/parser/impl/JsonSimpleParserTest.java | 18 ------ 7 files changed, 95 insertions(+), 88 deletions(-) create mode 100644 core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserTest.java delete mode 100644 core-api/src/test/java/com/optimizely/ab/odp/parser/impl/GsonParserTest.java delete mode 100644 core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JacksonParserTest.java delete mode 100644 core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonParserTest.java delete mode 100644 core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParserTest.java diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java index 194e5ec4a..916ee4c1f 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java @@ -3,24 +3,31 @@ import com.google.gson.*; import com.google.gson.JsonParser; import com.optimizely.ab.odp.parser.ResponseJsonParser; -import org.json.JSONArray; -import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; public class GsonParser implements ResponseJsonParser { + private static final Logger logger = LoggerFactory.getLogger(GsonParser.class); + @Override public List parseQualifiedSegments(String responseJson) { List parsedSegments = new ArrayList<>(); - JsonObject root = JsonParser.parseString(responseJson).getAsJsonObject(); - JsonArray edges = root.getAsJsonObject("data").getAsJsonObject("customer").getAsJsonObject("audiences").getAsJsonArray("edges"); - for (int i = 0; i < edges.size() ; i++) { - JsonObject node = edges.get(i).getAsJsonObject().getAsJsonObject("node"); - if (node.has("state") && node.get("state").getAsString().equals("qualified")) { - parsedSegments.add(node.get("name").getAsString()); + try { + JsonObject root = JsonParser.parseString(responseJson).getAsJsonObject(); + JsonArray edges = root.getAsJsonObject("data").getAsJsonObject("customer").getAsJsonObject("audiences").getAsJsonArray("edges"); + for (int i = 0; i < edges.size(); i++) { + JsonObject node = edges.get(i).getAsJsonObject().getAsJsonObject("node"); + if (node.has("state") && node.get("state").getAsString().equals("qualified")) { + parsedSegments.add(node.get("name").getAsString()); + } } + return parsedSegments; + } catch (JsonSyntaxException e) { + logger.error("Error parsing qualified segments from response", e); + return null; } - return parsedSegments; } } diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java index 9d373dd7e..b4f1a239f 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java @@ -2,23 +2,33 @@ import com.optimizely.ab.odp.parser.ResponseJsonParser; import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; public class JsonParser implements ResponseJsonParser { + private static final Logger logger = LoggerFactory.getLogger(JsonParser.class); + @Override public List parseQualifiedSegments(String responseJson) { List parsedSegments = new ArrayList<>(); - JSONObject root = new JSONObject(responseJson); - JSONArray edges = root.getJSONObject("data").getJSONObject("customer").getJSONObject("audiences").getJSONArray("edges"); - for (int i = 0; i < edges.length(); i++) { - JSONObject node = edges.getJSONObject(i).getJSONObject("node"); - if (node.has("state") && node.getString("state").equals("qualified")) { - parsedSegments.add(node.getString("name")); + try { + JSONObject root = new JSONObject(responseJson); + JSONArray edges = root.getJSONObject("data").getJSONObject("customer").getJSONObject("audiences").getJSONArray("edges"); + for (int i = 0; i < edges.length(); i++) { + JSONObject node = edges.getJSONObject(i).getJSONObject("node"); + if (node.has("state") && node.getString("state").equals("qualified")) { + parsedSegments.add(node.getString("name")); + } } + return parsedSegments; + } catch (JSONException e) { + logger.error("Error parsing qualified segments from response", e); + return null; } - return parsedSegments; } } diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserTest.java new file mode 100644 index 000000000..0e97c47ce --- /dev/null +++ b/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserTest.java @@ -0,0 +1,62 @@ +package com.optimizely.ab.odp.parser; + +import com.optimizely.ab.odp.parser.ResponseJsonParser; +import static junit.framework.TestCase.assertEquals; + +import com.optimizely.ab.odp.parser.impl.*; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.List; + +@RunWith(Parameterized.class) +public class ResponseJsonParserTest { + private ResponseJsonParser jsonParser; + + public ResponseJsonParserTest(ResponseJsonParser jsonParser) { + super(); + this.jsonParser = jsonParser; + } + + @Parameterized.Parameters + public static List input() { + return Arrays.asList(new GsonParser(), new JsonParser(), new JsonSimpleParser(), new JacksonParser()); + } + + @Test + public void returnSegmentsListWhenResponseIsCorrect() { + String responseToParse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; + List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + assertEquals(Arrays.asList("has_email", "has_email_opted_in"), parsedSegments); + } + + @Test + public void excludeSegmentsWhenStateNotQualified() { + String responseToParse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"not_qualified\"}}]}}}}"; + List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + assertEquals(Arrays.asList("has_email"), parsedSegments); + } + + @Test + public void returnEmptyListWhenResponseHasEmptyArray() { + String responseToParse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[]}}}}"; + List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + assertEquals(Arrays.asList(), parsedSegments); + } + + @Test + public void returnNullWhenJsonFormatIsValidButUnexpectedData() { + String responseToParse = "{\"data\"\"consumer\":{\"randomKey\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; + List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + assertEquals(null, parsedSegments); + } + + @Test + public void returnNullWhenJsonIsMalformed() { + String responseToParse = "{\"data\"\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; + List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + assertEquals(null, parsedSegments); + } +} diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/GsonParserTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/GsonParserTest.java deleted file mode 100644 index f0a0ef45b..000000000 --- a/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/GsonParserTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.optimizely.ab.odp.parser.impl; - -import com.optimizely.ab.odp.parser.ResponseJsonParser; -import junit.framework.TestCase; -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -public class GsonParserTest extends TestCase { - @Test - public void test1() { - String responseToParse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; - ResponseJsonParser jsonParser = new GsonParser(); - List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); - assertEquals(Arrays.asList("has_email", "has_email_opted_in"), parsedSegments); - } -} diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JacksonParserTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JacksonParserTest.java deleted file mode 100644 index 40847c763..000000000 --- a/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JacksonParserTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.optimizely.ab.odp.parser.impl; - -import com.optimizely.ab.odp.parser.ResponseJsonParser; -import junit.framework.TestCase; -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -public class JacksonParserTest extends TestCase { - @Test - public void test1() { - String responseToParse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; - ResponseJsonParser jsonParser = new JacksonParser(); - List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); - assertEquals(Arrays.asList("has_email", "has_email_opted_in"), parsedSegments); - } -} diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonParserTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonParserTest.java deleted file mode 100644 index 59f4761d9..000000000 --- a/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonParserTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.optimizely.ab.odp.parser.impl; - -import com.optimizely.ab.odp.parser.ResponseJsonParser; -import junit.framework.TestCase; -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -public class JsonParserTest extends TestCase { - @Test - public void test1() { - String responseToParse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; - ResponseJsonParser jsonParser = new JsonParser(); - List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); - assertEquals(Arrays.asList("has_email", "has_email_opted_in"), parsedSegments); - } -} diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParserTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParserTest.java deleted file mode 100644 index 553fedac8..000000000 --- a/core-api/src/test/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParserTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.optimizely.ab.odp.parser.impl; - -import com.optimizely.ab.odp.parser.ResponseJsonParser; -import junit.framework.TestCase; -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -public class JsonSimpleParserTest extends TestCase { - @Test - public void test1() { - String responseToParse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; - ResponseJsonParser jsonParser = new JsonSimpleParser(); - List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); - assertEquals(Arrays.asList("has_email", "has_email_opted_in"), parsedSegments); - } -} From b14562e21814ae052600c26bdfcac534058ef559 Mon Sep 17 00:00:00 2001 From: zashraf1985 Date: Fri, 29 Jul 2022 09:44:20 -0700 Subject: [PATCH 3/6] 1. Added unit tests for default parsers 2. Added error response parsing and it's unit tests --- .../ab/internal/JsonParserProvider.java | 8 +- .../com/optimizely/ab/odp/ODPApiManager.java | 7 ++ .../odp/parser/ResponseJsonParserFactory.java | 8 +- .../ab/odp/parser/impl/GsonParser.java | 14 ++++ .../ab/odp/parser/impl/JacksonParser.java | 34 +++++--- .../ab/odp/parser/impl/JsonParser.java | 14 ++++ .../ab/odp/parser/impl/JsonSimpleParser.java | 16 +++- .../ab/internal/JsonParserProviderTest.java | 31 +++++++ .../parser/ResponseJsonParserFactoryTest.java | 36 +++++++++ .../ab/odp/parser/ResponseJsonParserTest.java | 21 ++++- ...Manager.java => DefaultODPApiManager.java} | 81 +++++++++++++++++-- 11 files changed, 243 insertions(+), 27 deletions(-) create mode 100644 core-api/src/main/java/com/optimizely/ab/odp/ODPApiManager.java create mode 100644 core-api/src/test/java/com/optimizely/ab/internal/JsonParserProviderTest.java create mode 100644 core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactoryTest.java rename core-httpclient-impl/src/main/java/com/optimizely/ab/odp/{ODPApiManager.java => DefaultODPApiManager.java} (51%) diff --git a/core-api/src/main/java/com/optimizely/ab/internal/JsonParserProvider.java b/core-api/src/main/java/com/optimizely/ab/internal/JsonParserProvider.java index fb8f28325..4dbb0abab 100644 --- a/core-api/src/main/java/com/optimizely/ab/internal/JsonParserProvider.java +++ b/core-api/src/main/java/com/optimizely/ab/internal/JsonParserProvider.java @@ -5,10 +5,10 @@ import org.slf4j.LoggerFactory; public enum JsonParserProvider { - GSON("com.google.gson.Gson"), - JACKSON("com.fasterxml.jackson.databind.ObjectMapper" ), - JSON("org.json.JSONObject"), - JSON_SIMPLE("org.json.simple.JSONObject"); + GSON_CONFIG_PARSER("com.google.gson.Gson"), + JACKSON_CONFIG_PARSER("com.fasterxml.jackson.databind.ObjectMapper" ), + JSON_CONFIG_PARSER("org.json.JSONObject"), + JSON_SIMPLE_CONFIG_PARSER("org.json.simple.JSONObject"); private static final Logger logger = LoggerFactory.getLogger(JsonParserProvider.class); diff --git a/core-api/src/main/java/com/optimizely/ab/odp/ODPApiManager.java b/core-api/src/main/java/com/optimizely/ab/odp/ODPApiManager.java new file mode 100644 index 000000000..a37025522 --- /dev/null +++ b/core-api/src/main/java/com/optimizely/ab/odp/ODPApiManager.java @@ -0,0 +1,7 @@ +package com.optimizely.ab.odp; + +import java.util.List; + +public interface ODPApiManager { + String fetchQualifiedSegments(String apiKey, String apiEndpoint, String userKey, String userValue, List segmentsToCheck); +} diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactory.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactory.java index 337c37418..00e0ac66d 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactory.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactory.java @@ -15,16 +15,16 @@ public static ResponseJsonParser getParser() { JsonParserProvider parserProvider = JsonParserProvider.getDefaultParser(); ResponseJsonParser jsonParser = null; switch (parserProvider) { - case GSON: + case GSON_CONFIG_PARSER: jsonParser = new GsonParser(); break; - case JACKSON: + case JACKSON_CONFIG_PARSER: jsonParser = new JacksonParser(); break; - case JSON: + case JSON_CONFIG_PARSER: jsonParser = new JsonParser(); break; - case JSON_SIMPLE: + case JSON_SIMPLE_CONFIG_PARSER: jsonParser = new JsonSimpleParser(); break; } diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java index 916ee4c1f..0fc6203c8 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java @@ -17,6 +17,20 @@ public List parseQualifiedSegments(String responseJson) { List parsedSegments = new ArrayList<>(); try { JsonObject root = JsonParser.parseString(responseJson).getAsJsonObject(); + + if (root.has("errors")) { + JsonArray errors = root.getAsJsonArray("errors"); + StringBuilder logMessage = new StringBuilder(); + for (int i = 0; i < errors.size(); i++) { + if (i > 0) { + logMessage.append(", "); + } + logMessage.append(errors.get(i).getAsJsonObject().get("message").getAsString()); + } + logger.error(logMessage.toString()); + return null; + } + JsonArray edges = root.getAsJsonObject("data").getAsJsonObject("customer").getAsJsonObject("audiences").getAsJsonArray("edges"); for (int i = 0; i < edges.size(); i++) { JsonObject node = edges.get(i).getAsJsonObject().getAsJsonObject("node"); diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JacksonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JacksonParser.java index 3e9a6b610..f98c404c4 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JacksonParser.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JacksonParser.java @@ -18,20 +18,34 @@ public class JacksonParser implements ResponseJsonParser { public List parseQualifiedSegments(String responseJson) { ObjectMapper objectMapper = new ObjectMapper(); List parsedSegments = new ArrayList<>(); - JsonNode node; + JsonNode root; try { - node = objectMapper.readTree(responseJson); + root = objectMapper.readTree(responseJson); + + if (root.has("errors")) { + JsonNode errors = root.path("errors"); + StringBuilder logMessage = new StringBuilder(); + for (int i = 0; i < errors.size(); i++) { + if (i > 0) { + logMessage.append(", "); + } + logMessage.append(errors.get(i).path("message")); + } + logger.error(logMessage.toString()); + return null; + } + + JsonNode edges = root.path("data").path("customer").path("audiences").path("edges"); + for (JsonNode edgeNode : edges) { + String state = edgeNode.path("node").path("state").asText(); + if (state.equals("qualified")) { + parsedSegments.add(edgeNode.path("node").path("name").asText()); + } + } + return parsedSegments; } catch (JsonProcessingException e) { logger.error("Error parsing qualified segments from response", e); return null; } - JsonNode edges = node.path("data").path("customer").path("audiences").path("edges"); - for (JsonNode edgeNode: edges) { - String state = edgeNode.path("node").path("state").asText(); - if (state.equals("qualified")) { - parsedSegments.add(edgeNode.path("node").path("name").asText()); - } - } - return parsedSegments; } } diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java index b4f1a239f..e3c66a139 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java @@ -18,6 +18,20 @@ public List parseQualifiedSegments(String responseJson) { List parsedSegments = new ArrayList<>(); try { JSONObject root = new JSONObject(responseJson); + + if (root.has("errors")) { + JSONArray errors = root.getJSONArray("errors"); + StringBuilder logMessage = new StringBuilder(); + for (int i = 0; i < errors.length(); i++) { + if (i > 0) { + logMessage.append(", "); + } + logMessage.append(errors.getJSONObject(i).getString("message")); + } + logger.error(logMessage.toString()); + return null; + } + JSONArray edges = root.getJSONObject("data").getJSONObject("customer").getJSONObject("audiences").getJSONArray("edges"); for (int i = 0; i < edges.length(); i++) { JSONObject node = edges.getJSONObject(i).getJSONObject("node"); diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParser.java index 320897c13..de2eb1760 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParser.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParser.java @@ -21,6 +21,20 @@ public List parseQualifiedSegments(String responseJson) { JSONObject root = null; try { root = (JSONObject) parser.parse(responseJson); + + if (root.containsKey("errors")) { + JSONArray errors = (JSONArray) root.get("errors"); + StringBuilder logMessage = new StringBuilder(); + for (int i = 0; i < errors.size(); i++) { + if (i > 0) { + logMessage.append(", "); + } + logMessage.append((String)((JSONObject) errors.get(i)).get("message")); + } + logger.error(logMessage.toString()); + return null; + } + JSONArray edges = (JSONArray)((JSONObject)((JSONObject)(((JSONObject) root.get("data"))).get("customer")).get("audiences")).get("edges"); for (int i = 0; i < edges.size(); i++) { JSONObject node = (JSONObject) ((JSONObject) edges.get(i)).get("node"); @@ -29,7 +43,7 @@ public List parseQualifiedSegments(String responseJson) { } } return parsedSegments; - } catch (ParseException e) { + } catch (ParseException | NullPointerException e) { logger.error("Error parsing qualified segments from response", e); return null; } diff --git a/core-api/src/test/java/com/optimizely/ab/internal/JsonParserProviderTest.java b/core-api/src/test/java/com/optimizely/ab/internal/JsonParserProviderTest.java new file mode 100644 index 000000000..1e3672d12 --- /dev/null +++ b/core-api/src/test/java/com/optimizely/ab/internal/JsonParserProviderTest.java @@ -0,0 +1,31 @@ +package com.optimizely.ab.internal; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +public class JsonParserProviderTest { + @Before + @After + public void clearParserSystemProperty() { + PropertyUtils.clear("default_parser"); + } + + @Test + public void getGsonParserProviderWhenNoDefaultIsSet() { + assertEquals(JsonParserProvider.GSON_CONFIG_PARSER, JsonParserProvider.getDefaultParser()); + } + + @Test + public void getCorrectParserProviderWhenValidDefaultIsProvided() { + PropertyUtils.set("default_parser", "JSON_CONFIG_PARSER"); + assertEquals(JsonParserProvider.JSON_CONFIG_PARSER, JsonParserProvider.getDefaultParser()); + } + + @Test + public void getGsonParserWhenProvidedDefaultParserDoesNotExist() { + PropertyUtils.set("default_parser", "GARBAGE_VALUE"); + assertEquals(JsonParserProvider.GSON_CONFIG_PARSER, JsonParserProvider.getDefaultParser()); + } +} \ No newline at end of file diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactoryTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactoryTest.java new file mode 100644 index 000000000..3c3c8c8db --- /dev/null +++ b/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactoryTest.java @@ -0,0 +1,36 @@ +package com.optimizely.ab.odp.parser; + +import com.optimizely.ab.internal.JsonParserProvider; +import com.optimizely.ab.internal.PropertyUtils; +import com.optimizely.ab.odp.parser.impl.GsonParser; +import com.optimizely.ab.odp.parser.impl.JsonParser; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class ResponseJsonParserFactoryTest { + @Before + @After + public void clearParserSystemProperty() { + PropertyUtils.clear("default_parser"); + } + + @Test + public void getGsonParserWhenNoDefaultIsSet() { + assertEquals(GsonParser.class, ResponseJsonParserFactory.getParser().getClass()); + } + + @Test + public void getCorrectParserWhenValidDefaultIsProvided() { + PropertyUtils.set("default_parser", "JSON_CONFIG_PARSER"); + assertEquals(JsonParser.class, ResponseJsonParserFactory.getParser().getClass()); + } + + @Test + public void getGsonParserWhenGivenDefaultParserDoesNotExist() { + PropertyUtils.set("default_parser", "GARBAGE_VALUE"); + assertEquals(GsonParser.class, ResponseJsonParserFactory.getParser().getClass()); + } +} \ No newline at end of file diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserTest.java index 0e97c47ce..aa99fa058 100644 --- a/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserTest.java @@ -1,9 +1,11 @@ package com.optimizely.ab.odp.parser; -import com.optimizely.ab.odp.parser.ResponseJsonParser; +import ch.qos.logback.classic.Level; +import com.optimizely.ab.internal.LogbackVerifier; import static junit.framework.TestCase.assertEquals; import com.optimizely.ab.odp.parser.impl.*; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -13,7 +15,10 @@ @RunWith(Parameterized.class) public class ResponseJsonParserTest { - private ResponseJsonParser jsonParser; + private final ResponseJsonParser jsonParser; + + @Rule + public LogbackVerifier logbackVerifier = new LogbackVerifier(); public ResponseJsonParserTest(ResponseJsonParser jsonParser) { super(); @@ -28,7 +33,7 @@ public static List input() { @Test public void returnSegmentsListWhenResponseIsCorrect() { String responseToParse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; - List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); assertEquals(Arrays.asList("has_email", "has_email_opted_in"), parsedSegments); } @@ -50,6 +55,7 @@ public void returnEmptyListWhenResponseHasEmptyArray() { public void returnNullWhenJsonFormatIsValidButUnexpectedData() { String responseToParse = "{\"data\"\"consumer\":{\"randomKey\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + logbackVerifier.expectMessage(Level.ERROR, "Error parsing qualified segments from response"); assertEquals(null, parsedSegments); } @@ -57,6 +63,15 @@ public void returnNullWhenJsonFormatIsValidButUnexpectedData() { public void returnNullWhenJsonIsMalformed() { String responseToParse = "{\"data\"\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + logbackVerifier.expectMessage(Level.ERROR, "Error parsing qualified segments from response"); + assertEquals(null, parsedSegments); + } + + @Test + public void returnNullAndLogCorrectErrorWhenErrorResponseIsReturned() { + String responseToParse = "{\"errors\":[{\"message\":\"Exception while fetching data (/customer) : java.lang.RuntimeException: could not resolve _fs_user_id = wrong_id\",\"locations\":[{\"line\":2,\"column\":3}],\"path\":[\"customer\"],\"extensions\":{\"classification\":\"InvalidIdentifierException\"}}],\"data\":{\"customer\":null}}"; + List parsedSegments = jsonParser.parseQualifiedSegments(responseToParse); + logbackVerifier.expectMessage(Level.ERROR, "Exception while fetching data (/customer) : java.lang.RuntimeException: could not resolve _fs_user_id = wrong_id"); assertEquals(null, parsedSegments); } } diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/ODPApiManager.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java similarity index 51% rename from core-httpclient-impl/src/main/java/com/optimizely/ab/odp/ODPApiManager.java rename to core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java index 05f8b543d..8a9553840 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/ODPApiManager.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java @@ -1,6 +1,5 @@ package com.optimizely.ab.odp; -import com.optimizely.ab.event.AsyncEventHandler; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.StatusLine; @@ -16,9 +15,8 @@ import java.io.UnsupportedEncodingException; import java.util.List; -public class ODPApiManager { - - private static final Logger logger = LoggerFactory.getLogger(ODPApiManager.class); +public class DefaultODPApiManager implements ODPApiManager { + private static final Logger logger = LoggerFactory.getLogger(DefaultODPApiManager.class); private String getSegmentsStringForRequest(List segmentsList) { StringBuilder segmentsString = new StringBuilder(); @@ -31,6 +29,80 @@ private String getSegmentsStringForRequest(List segmentsList) { return segmentsString.toString(); } + // ODP GraphQL API + // - https://api.zaius.com/v3/graphql + // - test ODP public API key = "W4WzcEs-ABgXorzY7h1LCQ" + /* + + [GraphQL Request] + + // fetch info with fs_user_id for ["has_email", "has_email_opted_in", "push_on_sale"] segments + curl -i -H 'Content-Type: application/json' -H 'x-api-key: W4WzcEs-ABgXorzY7h1LCQ' -X POST -d '{"query":"query {customer(fs_user_id: \"tester-101\") {audiences(subset:[\"has_email\",\"has_email_opted_in\",\"push_on_sale\"]) {edges {node {name state}}}}}"}' https://api.zaius.com/v3/graphql + // fetch info with vuid for ["has_email", "has_email_opted_in", "push_on_sale"] segments + curl -i -H 'Content-Type: application/json' -H 'x-api-key: W4WzcEs-ABgXorzY7h1LCQ' -X POST -d '{"query":"query {customer(vuid: \"d66a9d81923d4d2f99d8f64338976322\") {audiences(subset:[\"has_email\",\"has_email_opted_in\",\"push_on_sale\"]) {edges {node {name state}}}}}"}' https://api.zaius.com/v3/graphql + query MyQuery { + customer(vuid: "d66a9d81923d4d2f99d8f64338976322") { + audiences(subset:["has_email","has_email_opted_in","push_on_sale"]) { + edges { + node { + name + state + } + } + } + } + } + [GraphQL Response] + + { + "data": { + "customer": { + "audiences": { + "edges": [ + { + "node": { + "name": "has_email", + "state": "qualified", + } + }, + { + "node": { + "name": "has_email_opted_in", + "state": "qualified", + } + }, + ... + ] + } + } + } + } + + [GraphQL Error Response] + { + "errors": [ + { + "message": "Exception while fetching data (/customer) : java.lang.RuntimeException: could not resolve _fs_user_id = asdsdaddddd", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "customer" + ], + "extensions": { + "classification": "InvalidIdentifierException" + } + } + ], + "data": { + "customer": null + } + } + */ + @Override public String fetchQualifiedSegments(String apiKey, String apiEndpoint, String userKey, String userValue, List segmentsToCheck) { HttpPost request = new HttpPost(apiEndpoint); String segmentsString = getSegmentsStringForRequest(segmentsToCheck); @@ -45,7 +117,6 @@ public String fetchQualifiedSegments(String apiKey, String apiEndpoint, String u request.setHeader("content-type", "application/json"); HttpClient client = HttpClientBuilder.create().build(); - HttpResponse response = null; try { response = client.execute(request); From a7c66fc8c340dfb2292eed73103e0868345a1ff9 Mon Sep 17 00:00:00 2001 From: zashraf1985 Date: Fri, 29 Jul 2022 13:56:15 -0700 Subject: [PATCH 4/6] Added tests for graphQL API call. --- .../ab/odp/DefaultODPApiManager.java | 39 +++- .../ab/internal/LogbackVerifier.java | 168 ++++++++++++++++++ .../ab/odp/DefaultODPApiManagerTest.java | 101 +++++++++++ 3 files changed, 300 insertions(+), 8 deletions(-) create mode 100644 core-httpclient-impl/src/test/java/com/optimizely/ab/internal/LogbackVerifier.java create mode 100644 core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java index 8a9553840..66e3f6d45 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java @@ -1,12 +1,12 @@ package com.optimizely.ab.odp; -import org.apache.http.HttpResponse; +import com.optimizely.ab.OptimizelyHttpClient; +import com.optimizely.ab.annotations.VisibleForTesting; import org.apache.http.HttpStatus; import org.apache.http.StatusLine; -import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,7 +18,19 @@ public class DefaultODPApiManager implements ODPApiManager { private static final Logger logger = LoggerFactory.getLogger(DefaultODPApiManager.class); - private String getSegmentsStringForRequest(List segmentsList) { + private final OptimizelyHttpClient httpClient; + + public DefaultODPApiManager() { + this(OptimizelyHttpClient.builder().build()); + } + + @VisibleForTesting + DefaultODPApiManager(OptimizelyHttpClient httpClient) { + this.httpClient = httpClient; + } + + @VisibleForTesting + String getSegmentsStringForRequest(List segmentsList) { StringBuilder segmentsString = new StringBuilder(); for (int i = 0; i < segmentsList.size(); i++) { if (i > 0) { @@ -107,7 +119,6 @@ public String fetchQualifiedSegments(String apiKey, String apiEndpoint, String u HttpPost request = new HttpPost(apiEndpoint); String segmentsString = getSegmentsStringForRequest(segmentsToCheck); String requestPayload = String.format("{\"query\": \"query {customer(%s: \\\"%s\\\") {audiences(subset: [%s]) {edges {node {name state}}}}}\"}", userKey, userValue, segmentsString); - try { request.setEntity(new StringEntity(requestPayload)); } catch (UnsupportedEncodingException e) { @@ -115,11 +126,10 @@ public String fetchQualifiedSegments(String apiKey, String apiEndpoint, String u } request.setHeader("x-api-key", apiKey); request.setHeader("content-type", "application/json"); - HttpClient client = HttpClientBuilder.create().build(); - HttpResponse response = null; + CloseableHttpResponse response = null; try { - response = client.execute(request); + response = httpClient.execute(request); } catch (IOException e) { logger.error("Error retrieving response from ODP service", e); return null; @@ -128,6 +138,7 @@ public String fetchQualifiedSegments(String apiKey, String apiEndpoint, String u if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { StatusLine statusLine = response.getStatusLine(); logger.error(String.format("Unexpected response from ODP server, Response code: %d, %s", statusLine.getStatusCode(), statusLine.getReasonPhrase())); + closeHttpResponse(response); return null; } @@ -135,7 +146,19 @@ public String fetchQualifiedSegments(String apiKey, String apiEndpoint, String u return EntityUtils.toString(response.getEntity()); } catch (IOException e) { logger.error("Error converting ODP segments response to string", e); + } finally { + closeHttpResponse(response); } return null; } + + private static void closeHttpResponse(CloseableHttpResponse response) { + if (response != null) { + try { + response.close(); + } catch (IOException e) { + logger.warn(e.getLocalizedMessage()); + } + } + } } diff --git a/core-httpclient-impl/src/test/java/com/optimizely/ab/internal/LogbackVerifier.java b/core-httpclient-impl/src/test/java/com/optimizely/ab/internal/LogbackVerifier.java new file mode 100644 index 000000000..25154e97d --- /dev/null +++ b/core-httpclient-impl/src/test/java/com/optimizely/ab/internal/LogbackVerifier.java @@ -0,0 +1,168 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.internal; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.core.AppenderBase; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.slf4j.LoggerFactory; + +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + +import static org.junit.Assert.fail; + +/** + * TODO As a usability improvement we should require expected messages be added after the message are expected to be + * logged. This will allow us to map the failure immediately back to the test line number as opposed to the async + * validation now that happens at the end of each individual test. + * + * From http://techblog.kenshoo.com/2013/08/junit-rule-for-verifying-logback-logging.html + */ +public class LogbackVerifier implements TestRule { + + private List expectedEvents = new LinkedList(); + + private CaptureAppender appender; + + @Override + public Statement apply(final Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + before(); + try { + base.evaluate(); + verify(); + } finally { + after(); + } + } + }; + } + + public void expectMessage(Level level) { + expectMessage(level, ""); + } + + public void expectMessage(Level level, String msg) { + expectMessage(level, msg, (Class) null); + } + + public void expectMessage(Level level, String msg, Class throwableClass) { + expectMessage(level, msg, null, 1); + } + + public void expectMessage(Level level, String msg, int times) { + expectMessage(level, msg, null, times); + } + + public void expectMessage(Level level, + String msg, + Class throwableClass, + int times) { + for (int i = 0; i < times; i++) { + expectedEvents.add(new ExpectedLogEvent(level, msg, throwableClass)); + } + } + + private void before() { + appender = new CaptureAppender(); + appender.setName("MOCK"); + appender.start(); + ((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).addAppender(appender); + } + + private void verify() throws Throwable { + ListIterator actualIterator = appender.getEvents().listIterator(); + + for (final ExpectedLogEvent expectedEvent : expectedEvents) { + boolean found = false; + while (actualIterator.hasNext()) { + ILoggingEvent actual = actualIterator.next(); + + if (expectedEvent.matches(actual)) { + found = true; + break; + } + } + + if (!found) { + fail(expectedEvent.toString()); + } + } + } + + private void after() { + ((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).detachAppender(appender); + } + + private static class CaptureAppender extends AppenderBase { + + List actualLoggingEvent = new LinkedList<>(); + + @Override + protected void append(ILoggingEvent eventObject) { + actualLoggingEvent.add(eventObject); + } + + public List getEvents() { + return actualLoggingEvent; + } + } + + private final static class ExpectedLogEvent { + private final String message; + private final Level level; + private final Class throwableClass; + + private ExpectedLogEvent(Level level, + String message, + Class throwableClass) { + this.message = message; + this.level = level; + this.throwableClass = throwableClass; + } + + private boolean matches(ILoggingEvent actual) { + boolean match = actual.getFormattedMessage().contains(message); + match &= actual.getLevel().equals(level); + match &= matchThrowables(actual); + return match; + } + + private boolean matchThrowables(ILoggingEvent actual) { + IThrowableProxy eventProxy = actual.getThrowableProxy(); + return throwableClass == null || eventProxy != null && throwableClass.getName().equals(eventProxy.getClassName()); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ExpectedLogEvent{"); + sb.append("level=").append(level); + sb.append(", message='").append(message).append('\''); + sb.append(", throwableClass=").append(throwableClass); + sb.append('}'); + return sb.toString(); + } + } +} diff --git a/core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java b/core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java new file mode 100644 index 000000000..32e597671 --- /dev/null +++ b/core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java @@ -0,0 +1,101 @@ +package com.optimizely.ab.odp; + +import ch.qos.logback.classic.Level; +import com.optimizely.ab.OptimizelyHttpClient; +import com.optimizely.ab.internal.LogbackVerifier; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.util.EntityUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.util.ArrayList; +import java.util.Arrays; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; + +public class DefaultODPApiManagerTest { + private static final String validResponse = "{\"data\":{\"customer\":{\"audiences\":{\"edges\":[{\"node\":{\"name\":\"has_email\",\"state\":\"qualified\"}},{\"node\":{\"name\":\"has_email_opted_in\",\"state\":\"qualified\"}}]}}}}"; + + @Rule + public LogbackVerifier logbackVerifier = new LogbackVerifier(); + + OptimizelyHttpClient mockHttpClient; + + @Before + public void setUp() throws Exception { + setupHttpClient(200); + } + + private void setupHttpClient(int statusCode) throws Exception { + mockHttpClient = mock(OptimizelyHttpClient.class); + CloseableHttpResponse httpResponse = mock(CloseableHttpResponse.class); + StatusLine statusLine = mock(StatusLine.class); + + when(statusLine.getStatusCode()).thenReturn(statusCode); + when(httpResponse.getStatusLine()).thenReturn(statusLine); + when(httpResponse.getEntity()).thenReturn(new StringEntity(validResponse)); + + when(mockHttpClient.execute(any(HttpPost.class))) + .thenReturn(httpResponse); + } + + @Test + public void generateCorrectSegmentsStringWhenListHasOneItem() { + DefaultODPApiManager apiManager = new DefaultODPApiManager(); + String expected = "\\\"only_segment\\\""; + String actual = apiManager.getSegmentsStringForRequest(Arrays.asList("only_segment")); + assertEquals(expected, actual); + } + + @Test + public void generateCorrectSegmentsStringWhenListHasMultipleItems() { + DefaultODPApiManager apiManager = new DefaultODPApiManager(); + String expected = "\\\"segment_1\\\", \\\"segment_2\\\", \\\"segment_3\\\""; + String actual = apiManager.getSegmentsStringForRequest(Arrays.asList("segment_1", "segment_2", "segment_3")); + assertEquals(expected, actual); + } + + @Test + public void generateEmptyStringWhenGivenListIsEmpty() { + DefaultODPApiManager apiManager = new DefaultODPApiManager(); + String actual = apiManager.getSegmentsStringForRequest(new ArrayList<>()); + assertEquals("", actual); + } + + @Test + public void generateCorrectRequestBody() throws Exception { + ODPApiManager apiManager = new DefaultODPApiManager(mockHttpClient); + apiManager.fetchQualifiedSegments("key", "endPoint", "fs_user_id", "test_user", Arrays.asList("segment_1", "segment_2")); + verify(mockHttpClient, times(1)).execute(any(HttpPost.class)); + + String expectedResponse = "{\"query\": \"query {customer(fs_user_id: \\\"test_user\\\") {audiences(subset: [\\\"segment_1\\\", \\\"segment_2\\\"]) {edges {node {name state}}}}}\"}"; + ArgumentCaptor request = ArgumentCaptor.forClass(HttpPost.class); + verify(mockHttpClient).execute(request.capture()); + assertEquals(expectedResponse, EntityUtils.toString(request.getValue().getEntity())); + } + + @Test + public void returnResponseStringWhenStatusIs200() throws Exception { + ODPApiManager apiManager = new DefaultODPApiManager(mockHttpClient); + String responseString = apiManager.fetchQualifiedSegments("key", "endPoint", "fs_user_id", "test_user", Arrays.asList("segment_1", "segment_2")); + verify(mockHttpClient, times(1)).execute(any(HttpPost.class)); + assertEquals(validResponse, responseString); + } + + @Test + public void returnNullWhenStatusIsNot200AndLogError() throws Exception { + setupHttpClient(500); + ODPApiManager apiManager = new DefaultODPApiManager(mockHttpClient); + String responseString = apiManager.fetchQualifiedSegments("key", "endPoint", "fs_user_id", "test_user", Arrays.asList("segment_1", "segment_2")); + verify(mockHttpClient, times(1)).execute(any(HttpPost.class)); + logbackVerifier.expectMessage(Level.ERROR, "Unexpected response from ODP server, Response code: 500, null"); + assertEquals(null, responseString); + } +} From 889957c072107661b301c9162675f15a768bba1a Mon Sep 17 00:00:00 2001 From: zashraf1985 Date: Fri, 29 Jul 2022 14:06:28 -0700 Subject: [PATCH 5/6] Added copyrights headers and missing line breaks --- .../ab/internal/JsonParserProvider.java | 15 +++++++++++++++ .../com/optimizely/ab/odp/ODPApiManager.java | 15 +++++++++++++++ .../ab/odp/parser/ResponseJsonParser.java | 15 +++++++++++++++ .../odp/parser/ResponseJsonParserFactory.java | 15 +++++++++++++++ .../ab/odp/parser/impl/GsonParser.java | 15 +++++++++++++++ .../ab/odp/parser/impl/JacksonParser.java | 15 +++++++++++++++ .../ab/odp/parser/impl/JsonParser.java | 15 +++++++++++++++ .../ab/odp/parser/impl/JsonSimpleParser.java | 15 +++++++++++++++ .../ab/internal/JsonParserProviderTest.java | 17 ++++++++++++++++- .../parser/ResponseJsonParserFactoryTest.java | 17 ++++++++++++++++- .../ab/odp/parser/ResponseJsonParserTest.java | 15 +++++++++++++++ .../optimizely/ab/odp/DefaultODPApiManager.java | 15 +++++++++++++++ .../ab/odp/DefaultODPApiManagerTest.java | 15 +++++++++++++++ 13 files changed, 197 insertions(+), 2 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/internal/JsonParserProvider.java b/core-api/src/main/java/com/optimizely/ab/internal/JsonParserProvider.java index 4dbb0abab..6f6de6516 100644 --- a/core-api/src/main/java/com/optimizely/ab/internal/JsonParserProvider.java +++ b/core-api/src/main/java/com/optimizely/ab/internal/JsonParserProvider.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.internal; import com.optimizely.ab.config.parser.MissingJsonParserException; diff --git a/core-api/src/main/java/com/optimizely/ab/odp/ODPApiManager.java b/core-api/src/main/java/com/optimizely/ab/odp/ODPApiManager.java index a37025522..ed40e37fd 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/ODPApiManager.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/ODPApiManager.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.odp; import java.util.List; diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParser.java index 5d87edec1..d494a78d0 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParser.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParser.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.odp.parser; import java.util.List; diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactory.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactory.java index 00e0ac66d..7762cef0e 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactory.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactory.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.odp.parser; import com.optimizely.ab.internal.JsonParserProvider; diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java index 0fc6203c8..b27d65078 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/GsonParser.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.odp.parser.impl; import com.google.gson.*; diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JacksonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JacksonParser.java index f98c404c4..f1a38eca7 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JacksonParser.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JacksonParser.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.odp.parser.impl; import com.fasterxml.jackson.core.JsonProcessingException; diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java index e3c66a139..fcae748a4 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonParser.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.odp.parser.impl; import com.optimizely.ab.odp.parser.ResponseJsonParser; diff --git a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParser.java b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParser.java index de2eb1760..1bee81b0a 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParser.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/parser/impl/JsonSimpleParser.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.odp.parser.impl; import com.optimizely.ab.odp.parser.ResponseJsonParser; diff --git a/core-api/src/test/java/com/optimizely/ab/internal/JsonParserProviderTest.java b/core-api/src/test/java/com/optimizely/ab/internal/JsonParserProviderTest.java index 1e3672d12..a65e9b6f5 100644 --- a/core-api/src/test/java/com/optimizely/ab/internal/JsonParserProviderTest.java +++ b/core-api/src/test/java/com/optimizely/ab/internal/JsonParserProviderTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.internal; import org.junit.After; @@ -28,4 +43,4 @@ public void getGsonParserWhenProvidedDefaultParserDoesNotExist() { PropertyUtils.set("default_parser", "GARBAGE_VALUE"); assertEquals(JsonParserProvider.GSON_CONFIG_PARSER, JsonParserProvider.getDefaultParser()); } -} \ No newline at end of file +} diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactoryTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactoryTest.java index 3c3c8c8db..f5d8e8c89 100644 --- a/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactoryTest.java +++ b/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserFactoryTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.odp.parser; import com.optimizely.ab.internal.JsonParserProvider; @@ -33,4 +48,4 @@ public void getGsonParserWhenGivenDefaultParserDoesNotExist() { PropertyUtils.set("default_parser", "GARBAGE_VALUE"); assertEquals(GsonParser.class, ResponseJsonParserFactory.getParser().getClass()); } -} \ No newline at end of file +} diff --git a/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserTest.java b/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserTest.java index aa99fa058..1acd5cf15 100644 --- a/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserTest.java +++ b/core-api/src/test/java/com/optimizely/ab/odp/parser/ResponseJsonParserTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.odp.parser; import ch.qos.logback.classic.Level; diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java index 66e3f6d45..bd47779ab 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.odp; import com.optimizely.ab.OptimizelyHttpClient; diff --git a/core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java b/core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java index 32e597671..c81af2dce 100644 --- a/core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java +++ b/core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java @@ -1,3 +1,18 @@ +/** + * Copyright 2022, Optimizely Inc. and contributors + * + * 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 com.optimizely.ab.odp; import ch.qos.logback.classic.Level; From f793afc7fbda156977849582f21b6677e77cfdfe Mon Sep 17 00:00:00 2001 From: zashraf1985 Date: Tue, 2 Aug 2022 13:52:23 -0700 Subject: [PATCH 6/6] incorporated review feedback --- .../main/java/com/optimizely/ab/odp/DefaultODPApiManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java index bd47779ab..f0703dcb0 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java @@ -150,7 +150,7 @@ public String fetchQualifiedSegments(String apiKey, String apiEndpoint, String u return null; } - if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + if (response.getStatusLine().getStatusCode() >= 400) { StatusLine statusLine = response.getStatusLine(); logger.error(String.format("Unexpected response from ODP server, Response code: %d, %s", statusLine.getStatusCode(), statusLine.getReasonPhrase())); closeHttpResponse(response);