com.facebook.ads.sdk.serverside.EventRequest Maven / Gradle / Ivy
/**
* Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
*
* You are hereby granted a non-exclusive, worldwide, royalty-free license to use, copy, modify,
* and distribute this software in source code or binary form for use in connection with the web
* services and APIs provided by Facebook.
*
*
As with any software that integrates with the Facebook platform, your use of this software is
* subject to the Facebook Developer Principles and Policies [http://developers.facebook.com/policy/].
* This copyright notice shall be included in all copies or substantial portions of the software.
*
*
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.facebook.ads.sdk.serverside;
import com.facebook.ads.sdk.APIConfig;
import com.facebook.ads.sdk.APIContext;
import com.facebook.ads.sdk.APIException;
import com.facebook.ads.sdk.APINode;
import com.facebook.ads.sdk.AdsPixel;
import com.facebook.ads.sdk.AdsPixel.APIRequestCreateEvent;
import com.facebook.ads.utils.CustomDataAdapter;
import com.facebook.ads.utils.AppDataAdapter;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Server side pixel event request
*/
public class EventRequest {
private static Gson gson = null;
@SerializedName("data")
private List data = new ArrayList();
@SerializedName("test_event_code")
private String testEventCode = null;
@SerializedName("partner_agent")
private String partnerAgent = null;
@SerializedName("namespace_id")
private String namespaceId = null;
@SerializedName("upload_id")
private String uploadId = null;
@SerializedName("upload_tag")
private String uploadTag = null;
@SerializedName("upload_source")
private String uploadSource = null;
private String pixelId;
private APIContext context;
private HttpServiceInterface httpServiceClient;
private CustomEndpointRequest endpointRequest;
/**
* Constructor.
*
* @param pixelId Ad pixel id
* @param context Api context
*/
public EventRequest(String pixelId, APIContext context) {
this.pixelId = pixelId;
this.context = context;
}
/**
* Constructor.
*
* @param pixelId Ad pixel id
* @param context Api context
* @param data list of event
* @param testEventCode code used to verify that your server events are received correctly by
* @param partnerAgent A value that represents the platform that is sending the event
* @param namespaceId Scope used to resolve extern_id or Third-party ID. Can be another data set or data partner ID.
* @param uploadId Unique id used to denote the current set being uploaded.
* @param uploadTag Tag string added to track your Offline event uploads.
* @param uploadSource The origin/source of data for the dataset to be uploaded.
* @param httpServiceClient The HttpServiceInterface client to use for executing the request.
* Facebook
*/
public EventRequest(String pixelId, APIContext context, List data,
String testEventCode, String partnerAgent, String namespaceId, String uploadId,
String uploadTag, String uploadSource, HttpServiceInterface httpServiceClient) {
this.data = data;
this.testEventCode = testEventCode;
this.partnerAgent = partnerAgent;
this.pixelId = pixelId;
this.context = context;
this.namespaceId = namespaceId;
this.uploadId = uploadId;
this.uploadTag = uploadTag;
this.uploadSource = uploadSource;
this.httpServiceClient = httpServiceClient;
this.endpointRequest = null;
}
static /*package*/ synchronized Gson getGson() {
if (gson != null) {
return gson;
} else {
gson =
new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC, Modifier.PROTECTED)
.disableHtmlEscaping()
.registerTypeAdapter(CustomData.class, new CustomDataAdapter())
.registerTypeAdapter(AppData.class, new AppDataAdapter())
.create();
}
return gson;
}
/**
* Set a list of Server Event objects
*
* @param data list of Server Event
* @return EventRequest
*/
public EventRequest data(List data) {
this.data = data;
return this;
}
public void setCustomEndpoint(CustomEndpointRequest endpointRequest) {
this.endpointRequest = endpointRequest;
}
/**
* Add a Server Event object
*
* @param dataItem Server Event
* @return EventRequest
*/
public EventRequest addDataItem(Event dataItem) {
this.data.add(dataItem);
return this;
}
/**
* A list of Server Event objects
*
* @return data
*/
public List getData() {
return data;
}
/**
* Set a list of Server Event objects
*
* @param data list of Server Event
*/
public void setData(List data) {
this.data = data;
}
/**
* The namespace id is a scope used to resolve extern_id or Third-party ID.
* Can be another data set or data partner ID.
*
* @return namespaceId
*/
public String getNamespaceId() {
return namespaceId;
}
/**
* Set namespace id
*
* @param namespaceId Scope used to resolve extern_id or Third-party ID. Can be another data set or data partner ID.
*/
public void setNamespaceId(String namespaceId) {
this.namespaceId = namespaceId;
}
/**
* Set namespace id
*
* @param namespaceId Scope used to resolve extern_id or Third-party ID. Can be another data set or data partner ID.
* @return EventRequest
*/
public EventRequest namespaceId(String namespaceId) {
this.namespaceId = namespaceId;
return this;
}
/**
* The upload id is a unique id used to denote the current set being uploaded.
*
* @return uploadId
*/
public String getUploadId() {
return uploadId;
}
/**
* Set upload id
*
* @param uploadId Unique id used to denote the current set being uploaded.
*/
public void setUploadId(String uploadId) {
this.uploadId = uploadId;
}
/**
* Set upload id
*
* @param uploadId Unique id used to denote the current set being uploaded.
* @return EventRequest
*/
public EventRequest uploadId(String uploadId) {
this.uploadId = uploadId;
return this;
}
/**
* The upload tag is a tag string added to track your Offline event uploads.
*
* @return uploadTag
*/
public String getUploadTag() {
return uploadTag;
}
/**
* Set upload tag
*
* @param uploadTag Tag string added to track your Offline event uploads.
*/
public void setUploadTag(String uploadTag) {
this.uploadTag = uploadTag;
}
/**
* Set upload tag
*
* @param uploadTag Tag string added to track your Offline event uploads.
* @return EventRequest
*/
public EventRequest uploadTag(String uploadTag) {
this.uploadTag = uploadTag;
return this;
}
/**
* The upload source is the origin/source of data for the dataset to be uploaded.
*
* @return uploadSource
*/
public String getUploadSource() {
return uploadSource;
}
/**
* Set upload source
*
* @param uploadSource The origin/source of data for the dataset to be uploaded.
*/
public void setUploadSource(String uploadSource) {
this.uploadSource = uploadSource;
}
/**
* Set upload source
*
* @param uploadSource The origin/source of data for the dataset to be uploaded.
* @return EventRequest
*/
public EventRequest uploadSource(String uploadSource) {
this.uploadSource = uploadSource;
return this;
}
/**
* Set code used to verify that your server events are received correctly by Facebook. Use this
* code to test your server events in the Test Events feature in Events Manager. See Test Events
* Tool (https://developers.facebook.com/docs/marketing-api/facebook-pixel/server-side-api/using-the-api#testEvents)
* for an example.
*
* @param testEventCode ode used to verify that your server events
* @return EventRequest
*/
public EventRequest testEventCode(String testEventCode) {
this.testEventCode = testEventCode;
return this;
}
/**
* Code used to verify that your server events are received correctly by Facebook. Use this code
* to test your server events in the Test Events feature in Events Manager. See Test Events Tool
* (https://developers.facebook.com/docs/marketing-api/facebook-pixel/server-side-api/using-the-api#testEvents)
* for an example.
*
* @return testEventCode
*/
public String getTestEventCode() {
return testEventCode;
}
/**
* Set code used to verify that your server events are received correctly by Facebook. Use this
* code to test your server events in the Test Events feature in Events Manager. See Test Events
* Tool (https://developers.facebook.com/docs/marketing-api/facebook-pixel/server-side-api/using-the-api#testEvents)
* for an example.
*
* @param testEventCode ode used to verify that your server events
*/
public void setTestEventCode(String testEventCode) {
this.testEventCode = testEventCode;
}
/**
* Sets a value that represents the platform that is sending the event.
*
* @param partnerAgent a value that represents the platform that is sending the event
* @return EventRequest
*/
public EventRequest partnerAgent(String partnerAgent) {
this.partnerAgent = partnerAgent;
return this;
}
/**
* A value that represents the platform that is sending the event.
*
* @return partnerAgent
*/
public String getPartnerAgent() {
return partnerAgent;
}
/**
* Set a value that represents the platform that is sending the event.
*
* @param partnerAgent a value that represents the platform that is sending the event
*/
public void setPartnerAgent(String partnerAgent) {
this.partnerAgent = partnerAgent;
}
/**
* Get the httpServiceClient.
*
* @return httpServiceClient
*/
public HttpServiceInterface getHttpServiceClient() {
return httpServiceClient;
}
/**
* Set a custom http client that will be used to send the event request.
*
* @param httpServiceClient the object implementing HttpServiceInterface
*/
public void setHttpServiceClient(HttpServiceInterface httpServiceClient) {
this.httpServiceClient = httpServiceClient;
}
/**
* Synchronously send Event to Facebook GraphAPI.
*
* @return event response
* @throws APIException Api Exception
*/
public EventResponse execute() throws APIException {
EventResponse response;
try {
if (endpointRequest != null && endpointRequest.isSendToEndpointOnly()) {
// do not send to CAPI Endpoint. If no exception was thrown, we can assume all events were sent successfully
final Map customEndpointResponses = sendEventsToCustomEndpoint();
context.log(String.format("Successfully sent %d event(s) to %s only", data.size(), endpointRequest.getEndpoint()));
context.log("Skipping CAPI Endpoint");
response = new EventResponse(data.size(), new ArrayList(), "");
response.setCustomEndpointResponses(customEndpointResponses);
return response;
} else if (endpointRequest != null) {
response = sendToCAPIEndpoint();
final Map customEndpointResponses = sendEventsToCustomEndpoint();
response.setCustomEndpointResponses(customEndpointResponses);
} else {
response = sendToCAPIEndpoint();
}
context.log(String.format("Successfully sent %d event(s)", response.getEventsReceived()));
return response;
} catch (APIException e) {
context.log(e.getMessage());
throw e;
}
}
/**
* Synchronously send events to Custom Endpoint.
*
* @return event response
* @throws APIException Api Exception
*/
private Map sendEventsToCustomEndpoint() throws APIException.FailedRequestException {
final Map responses = new HashMap();
final CustomEndpointResponse customEndpointResponse = endpointRequest.sendEvent(context, pixelId, data);
responses.put(endpointRequest.getEndpoint(), customEndpointResponse);
return responses;
}
/**
* Synchronously send events to Facebook Conversions API.
*
* @return event response
* @throws APIException Api Exception
*/
private EventResponse sendToCAPIEndpoint() throws APIException {
EventResponse response;
if (httpServiceClient == null) {
final APIRequestCreateEvent event = getPixelCreateEvent();
final APINode pixel = event.execute();
response = gson.fromJson(pixel.getRawResponse(), EventResponse.class);
} else {
response = executeCustomHttpService(httpServiceClient);
}
return response;
}
private ListenableFuture sendEventAsyncToCustomEndpointOnly() {
final ListenableFuture customEndpointFuture = endpointRequest.sendEventAsync(context, pixelId, data);
return Futures.transformAsync(
customEndpointFuture,
new AsyncFunction() {
@Override
public ListenableFuture apply(final CustomEndpointResponse response) {
// do not send to CAPI Endpoint. If no exception was thrown, we can assume all events were sent successfully
context.log(String.format("Successfully sent %d event(s) to %s only", data.size(), endpointRequest.getEndpoint()));
context.log("Skipping CAPI Endpoint");
final EventResponse customEndpointResponse = new EventResponse(data.size(), new ArrayList(), "");
final Map endpointResponses = new HashMap();
endpointResponses.put(endpointRequest.getEndpoint(), response);
customEndpointResponse.setCustomEndpointResponses(endpointResponses);
return Futures.immediateFuture(customEndpointResponse);
}
},
MoreExecutors.directExecutor()
);
}
private ListenableFuture sendEventAsyncToCAPIAndCustomEndpoint() throws APIException {
final AdsPixel.APIRequestCreateEvent event = getPixelCreateEvent();
final ListenableFuture pixelFuture = event.executeAsync();
final ListenableFuture customEndpointFuture = endpointRequest.sendEventAsync(context, pixelId, data);
// put CAPI endpoint and custom endpoint into a list of futures
final ListenableFuture> futureOfList = Futures.allAsList(pixelFuture, customEndpointFuture);
return Futures.transformAsync(
futureOfList,
new AsyncFunction, EventResponse>() {
@Override
public ListenableFuture apply(final List