8000 feat: Add ODP GraphQL Api Interface (#481) · optimizely/java-sdk@618377d · GitHub
[go: up one dir, main page]

Skip to content

Commit 618377d

Browse files
authored
feat: Add ODP GraphQL Api Interface (#481)
## Summary 1. Added an ODPApiManager interface and its Default implementation that implements the graphQL call to fetch qualified segments. 2. The mechanism to get default parser is tightly coupled with Project Config module. I created a separate Factory class to get the default parser. This can be used by any future implementations that need a default parser. 3. Added parsing for all four supported parsers to parse valid and error responses. ## Test plan 1. Added new unit tests to test all the new code. 2. Existing unit tests pass 3. Existing Full stack compatibility suite tests pass ## Issues [OASIS-8384](https://optimizely.atlassian.net/browse/OASIS-8384)
1 parent 8bd6ea5 commit 618377d

File tree

14 files changed

+1076
-0
lines changed

14 files changed

+1076
-0
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Copyright 2022, Optimizely Inc. and contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.optimizely.ab.internal;
17+
18+
import com.optimizely.ab.config.parser.MissingJsonParserException;
19+
import org.slf4j.Logger;
20+
import org.slf4j.LoggerFactory;
21+
22+
public enum JsonParserProvider {
23+ 9E7A
GSON_CONFIG_PARSER("com.google.gson.Gson"),
24+
JACKSON_CONFIG_PARSER("com.fasterxml.jackson.databind.ObjectMapper" ),
25+
JSON_CONFIG_PARSER("org.json.JSONObject"),
26+
JSON_SIMPLE_CONFIG_PARSER("org.json.simple.JSONObject");
27+
28+
private static final Logger logger = LoggerFactory.getLogger(JsonParserProvider.class);
29+
30+
private final String className;
31+
32+
JsonParserProvider(String className) {
33+
this.className = className;
34+
}
35+
36+
private boolean isAvailable() {
37+
try {
38+
Class.forName(className);
39+
return true;
40+
} catch (ClassNotFoundException e) {
41+
return false;
42+
}
43+
}
44+
45+
public static JsonParserProvider getDefaultParser() {
46+
String defaultParserName = PropertyUtils.get("default_parser");
47+
48+
if (defaultParserName != null) {
49+
try {
50+
JsonParserProvider parser = JsonParserProvider.valueOf(defaultParserName);
51+
if (parser.isAvailable()) {
52+
logger.debug("using json parser: {}, based on override config", parser.className);
53+
return parser;
54+
}
55+
56+
logger.warn("configured parser {} is not available in the classpath", defaultParserName);
57+
} catch (IllegalArgumentException e) {
58+
logger.warn("configured parser {} is not a valid value", defaultParserName);
59+
}
60+
}
61+
62+
for (JsonParserProvider parser: JsonParserProvider.values()) {
63+
if (!parser.isAvailable()) {
64+
continue;
65+
}
66+
67+
logger.info("using json parser: {}", parser.className);
68+
return parser;
69+
}
70+
71+
throw new MissingJsonParserException("unable to locate a JSON parser. "
72+
+ "Please see <link> for more information");
73+
}
74+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright 2022, Optimizely Inc. and contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.optimizely.ab.odp;
17+
18+
import java.util.List;
19+
20+
public interface ODPApiManager {
21+
String fetchQualifiedSegments(String apiKey, String apiEndpoint, String userKey, String userValue, List<String> segmentsToCheck);
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright 2022, Optimizely Inc. and contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.optimizely.ab.odp.parser;
17+
18+
import java.util.List;
19+
20+
public interface ResponseJsonParser {
21+
public List<String> parseQualifiedSegments(String responseJson);
22+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Copyright 2022, Optimizely Inc. and contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.optimizely.ab.odp.parser;
17+
18+
import com.optimizely.ab.internal.JsonParserProvider;
19+
import com.optimizely.ab.odp.parser.impl.GsonParser;
20+
import com.optimizely.ab.odp.parser.impl.JacksonParser;
21+
import com.optimizely.ab.odp.parser.impl.JsonParser;
22+
import com.optimizely.ab.odp.parser.impl.JsonSimpleParser; 2851
23+
import org.slf4j.Logger;
24+
import org.slf4j.LoggerFactory;
25+
26+
public class ResponseJsonParserFactory {
27+
private static final Logger logger = LoggerFactory.getLogger(ResponseJsonParserFactory.class);
28+
29+
public static ResponseJsonParser getParser() {
30+
JsonParserProvider parserProvider = JsonParserProvider.getDefaultParser();
31+
ResponseJsonParser jsonParser = null;
32+
switch (parserProvider) {
33+
case GSON_CONFIG_PARSER:
34+
jsonParser = new GsonParser();
35+
break;
36+
case JACKSON_CONFIG_PARSER:
37+
jsonParser = new JacksonParser();
38+
break;
39+
case JSON_CONFIG_PARSER:
40+
jsonParser = new JsonParser();
41+
break;
42+
case JSON_SIMPLE_CONFIG_PARSER:
43+
jsonParser = new JsonSimpleParser();
44+
break;
45+
}
46+
logger.info("Using " + parserProvider.toString() + " parser");
47+
return jsonParser;
48+
}
49+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Copyright 2022, Optimizely Inc. and contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.optimizely.ab.odp.parser.impl;
17+
18+
import com.google.gson.*;
19+
import com.google.gson.JsonParser;
20+
import com.optimizely.ab.odp.parser.ResponseJsonParser;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
24+
import java.util.ArrayList;
25+
import java.util.List;
26+
27+
public class GsonParser implements ResponseJsonParser {
28+
private static final Logger logger = LoggerFactory.getLogger(GsonParser.class);
29+
30+
@Override
31+
public List<String> parseQualifiedSegments(String responseJson) {
32+
List<String> parsedSegments = new ArrayList<>();
33+
try {
34+
JsonObject root = JsonParser.parseString(responseJson).getAsJsonObject();
35+
36+
if (root.has("errors")) {
37+
JsonArray errors = root.getAsJsonArray("errors");
38+
StringBuilder logMessage = new StringBuilder();
39+
for (int i = 0; i < errors.size(); i++) {
40+
if (i > 0) {
41+
logMessage.append(", ");
42+
}
43+
logMessage.append(errors.get(i).getAsJsonObject().get("message").getAsString());
44+
}
45+
logger.error(logMessage.toString());
46+
return null;
47+
}
48+
49+
JsonArray edges = root.getAsJsonObject("data").getAsJsonObject("customer").getAsJsonObject("audiences").getAsJsonArray("edges");
50+
for (int i = 0; i < edges.size(); i++) {
51+
JsonObject node = edges.get(i).getAsJsonObject().getAsJsonObject("node");
52+
if (node.has("state") && node.get("state").getAsString().equals("qualified")) {
53+
parsedSegments.add(node.get("name").getAsString());
54+
}
55+
}
56+
return parsedSegments;
57+
} catch (JsonSyntaxException e) {
58+
logger.error("Error parsing qualified segments from response", e);
59+
return null;
60+
}
61+
}
62+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Copyright 2022, Optimizely Inc. and contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.optimizely.ab.odp.parser.impl;
17+
18+
import com.fasterxml.jackson.core.JsonProcessingException;
19+
import com.fasterxml.jackson.databind.JsonNode;
20+
import com.fasterxml.jackson.databind.ObjectMapper;
21+
import com.optimizely.ab.odp.parser.ResponseJsonParser;
22+
import org.slf4j.Logger;
23+
import org.slf4j.LoggerFactory;
24+
25+
import java.util.ArrayList;
26+
27+
import java.util.List;
28+
29+
public class JacksonParser implements ResponseJsonParser {
30+
private static final Logger logger = LoggerFactory.getLogger(JacksonParser.class);
31+
32+
@Override
33+
public List<String> parseQualifiedSegments(String responseJson) {
34+
ObjectMapper objectMapper = new ObjectMapper();
35+
List<String> parsedSegments = new ArrayList<>();
36+
JsonNode root;
37+
try {
38+
root = objectMapper.readTree(responseJson);
39+
40+
if (root.has("errors")) {
41+
JsonNode errors = root.path("errors");
42+
StringBuilder logMessage = new StringBuilder();
43+
for (int i = 0; i < errors.size(); i++) {
44+
if (i > 0) {
45+
logMessage.append(", ");
46+
}
47+
logMessage.append(errors.get(i).path("message"));
48+
}
49+
logger.error(logMessage.toString());
50+
return null;
51+
}
52+
53+
JsonNode edges = root.path("data").path("customer").path("audiences").path("edges");
54+
for (JsonNode edgeNode : edges) {
55+
String state = edgeNode.path("node").path("state").asText();
56+
if (state.equals("qualified")) {
57+
parsedSegments.add(edgeNode.path("node").path("name").asText());
58+
}
59+
}
60+
return parsedSegments;
61+
} catch (JsonProcessingException e) {
62+
logger.error("Error parsing qualified segments from response", e);
63+
return null;
64+
}
65+
}
66+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Copyright 2022, Optimizely Inc. and contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.optimizely.ab.odp.parser.impl;
17+
18+
import com.optimizely.ab.odp.parser.ResponseJsonParser;
19+
import org.json.JSONArray;
20+
import org.json.JSONException;
21+
import org.json.JSONObject;
22+
import org.slf4j.Logger;
23+
import org.slf4j.LoggerFactory;
24+
25+
import java.util.ArrayList;
26+
import java.util.List;
27+
28+
public class JsonParser implements ResponseJsonParser {
29+
private static final Logger logger = LoggerFactory.getLogger(JsonParser.class);
30+
31+
@Override
32+
public List<String> parseQualifiedSegments(String responseJson) {
33+
List<String> parsedSegments = new ArrayList<>();
34+
try {
35+
JSONObject root = new JSONObject(responseJson);
36+
37+
if (root.has("errors")) {
38+
JSONArray errors = root.getJSONArray("errors");
39+
StringBuilder logMessage = new StringBuilder();
40+
for (int i = 0; i < errors.length(); i++) {
41+
if (i > 0) {
42+
logMessage.append(", ");
43+
}
44+
logMessage.append(errors.getJSONObject(i).getString("message"));
45+
}
46+
logger.error(logMessage.toString());
47+
return null;
48+
}
49+
50+
JSONArray edges = root.getJSONObject("data").getJSONObject("customer").getJSONObject("audiences").getJSONArray("edges");
51+
for (int i = 0; i < edges.length(); i++) {
52+
JSONObject node = edges.getJSONObject(i).getJSONObject("node");
53+
if (node.has("state") && node.getString("state").equals("qualified")) {
54+
parsedSegments.add(node.getString("name"));
55+
}
56+
}
57+
return parsedSegments;
58+
} catch (JSONException e) {
59+
logger.error("Error parsing qualified segments from response", e);
60+
return null;
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)
0