com.optimizely.ab.odp.DefaultODPApiManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core-httpclient-impl Show documentation
Show all versions of core-httpclient-impl Show documentation
The Java SDK for Optimizely Feature Experimentation, Optimizely Full Stack (legacy), and Optimizely Rollouts
/**
* Copyright 2022-2023, 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;
import com.optimizely.ab.annotations.VisibleForTesting;
import com.optimizely.ab.odp.parser.ResponseJsonParser;
import com.optimizely.ab.odp.parser.ResponseJsonParserFactory;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class DefaultODPApiManager implements ODPApiManager {
private static final Logger logger = LoggerFactory.getLogger(DefaultODPApiManager.class);
private final OptimizelyHttpClient httpClientSegments;
private final OptimizelyHttpClient httpClientEvents;
public DefaultODPApiManager() {
this(OptimizelyHttpClient.builder().build());
}
public DefaultODPApiManager(int segmentFetchTimeoutMillis, int eventDispatchTimeoutMillis) {
httpClientSegments = OptimizelyHttpClient.builder().setTimeoutMillis(segmentFetchTimeoutMillis).build();
if (segmentFetchTimeoutMillis == eventDispatchTimeoutMillis) {
// If the timeouts are same, single httpClient can be used for both.
httpClientEvents = httpClientSegments;
} else {
httpClientEvents = OptimizelyHttpClient.builder().setTimeoutMillis(eventDispatchTimeoutMillis).build();
}
}
@VisibleForTesting
DefaultODPApiManager(OptimizelyHttpClient httpClient) {
this.httpClientSegments = httpClient;
this.httpClientEvents = httpClient;
}
@VisibleForTesting
String getSegmentsStringForRequest(Set segmentsList) {
StringBuilder segmentsString = new StringBuilder();
Iterator segmentsListIterator = segmentsList.iterator();
for (int i = 0; i < segmentsList.size(); i++) {
if (i > 0) {
segmentsString.append(", ");
}
segmentsString.append("\"").append(segmentsListIterator.next()).append("\"");
}
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": {
"code": "INVALID_IDENTIFIER_EXCEPTION",
"classification": "DataFetchingException"
}
}
],
"data": {
"customer": null
}
}
*/
@Override
public List fetchQualifiedSegments(String apiKey, String apiEndpoint, String userKey, String userValue, Set segmentsToCheck) {
HttpPost request = new HttpPost(apiEndpoint);
String segmentsString = getSegmentsStringForRequest(segmentsToCheck);
String query = String.format("query($userId: String, $audiences: [String]) {customer(%s: $userId) {audiences(subset: $audiences) {edges {node {name state}}}}}", userKey);
String variables = String.format("{\"userId\": \"%s\", \"audiences\": [%s]}", userValue, segmentsString);
String requestPayload = String.format("{\"query\": \"%s\", \"variables\": %s}", query, variables);
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");
CloseableHttpResponse response = null;
try {
response = httpClientSegments.execute(request);
} catch (IOException e) {
logger.error("Error retrieving response from ODP service", e);
return null;
}
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);
return null;
}
ResponseJsonParser parser = ResponseJsonParserFactory.getParser();
try {
return parser.parseQualifiedSegments(EntityUtils.toString(response.getEntity()));
} catch (IOException e) {
logger.error("Error converting ODP segments response to string", e);
} catch (Exception e) {
logger.error("Audience segments fetch failed (Error Parsing Response)");
logger.debug(e.getMessage());
} finally {
closeHttpResponse(response);
}
return null;
}
/*
eventPayload Format
[
{
"action": "identified",
"identifiers": {"vuid": , "fs_user_id": , ....},
"data": {“source”: