com.echobox.api.tiktok.client.DefaultTikTokClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ebx-tiktok-sdk Show documentation
Show all versions of ebx-tiktok-sdk Show documentation
ebx-tiktok-sdk is a pure Java TikTok API client. It implements the v1.3 API.
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.echobox.api.tiktok.client;
import com.echobox.api.tiktok.client.DefaultWebRequestor.HttpMethod;
import com.echobox.api.tiktok.client.WebRequestor.Response;
import com.echobox.api.tiktok.connection.Connection;
import com.echobox.api.tiktok.exception.TikTokAPIException;
import com.echobox.api.tiktok.exception.TikTokException;
import com.echobox.api.tiktok.exception.TikTokJsonMappingException;
import com.echobox.api.tiktok.exception.TikTokNetworkException;
import com.echobox.api.tiktok.model.HasCursor;
import com.echobox.api.tiktok.model.response.TokenResponse;
import com.echobox.api.tiktok.version.Version;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Function;
/**
* Default Java TikTok client for communicating with the API, specific endpoints will be
* configured in separate 'connection' classes.
*
* @author eddspencer
*/
public class DefaultTikTokClient implements TikTokClient {
private static final String TIKTOK_ENDPOINT_URL = "https://business-api.tiktok.com/open_api";
/**
* Endpoint path for access token creation
*/
protected static final String ENDPOINT_TOKEN = "/tt_user/oauth2/token/";
/**
* Endpoint path for refresh token creation
*/
protected static final String ENDPOINT_REFRESH_TOKEN = "/tt_user/oauth2/refresh_token/";
private static final Gson JSON_FACTORY = new Gson();
private final Version version;
private WebRequestor webRequestor;
/**
* Constructor with no access token for use when obtaining an access token
*
* @param version the version
* @throws GeneralSecurityException the general security exception
* @throws IOException the io exception
*/
public DefaultTikTokClient(Version version) throws GeneralSecurityException, IOException {
this(version, (String) null);
}
/**
* Constructor with access token for use when calling the endpoints to get and update data
*
* @param version the version
* @param accessToken the access token
* @throws GeneralSecurityException the general security exception
* @throws IOException the IO exception
*/
public DefaultTikTokClient(Version version, String accessToken)
throws GeneralSecurityException, IOException {
this(version, new DefaultWebRequestor(accessToken));
}
/**
* Constructor with web requestor for use when calling the endpoints to get and update data
*
* @param version the version
* @param webRequestor the web requestor
*/
public DefaultTikTokClient(Version version, WebRequestor webRequestor) {
this.version = version;
this.webRequestor = webRequestor;
}
@Override
public Version getVersion() {
return version;
}
@Override
public WebRequestor getWebRequestor() {
return webRequestor;
}
protected void setWebRequestor(WebRequestor webRequestor) {
this.webRequestor = webRequestor;
}
@Override
public T fetchObject(String endpoint, Class responseType, Parameter... parameters) {
return makeRequest(endpoint, HttpMethod.GET, responseType, null, parameters);
}
@Override
public > Connection fetchConnection(String endpoint,
Class responseType, Parameter... parameters) {
final Function> getNextPage = cursor -> {
final ArrayList parameterList = new ArrayList<>(Arrays.asList(parameters));
if (cursor != null) {
int index = parameterList.indexOf(Parameter.with("cursor", cursor.toString()));
if (index == -1) {
parameterList.add(Parameter.with("cursor", cursor.toString()));
} else {
parameterList.set(index, Parameter.with("cursor", cursor.toString()));
}
}
return makeRequest(endpoint, HttpMethod.GET, responseType, null,
parameterList.toArray(new Parameter[0]));
};
return new Connection<>(getNextPage);
}
@Override
public T publish(String endpoint, Class responseType, Object data,
Parameter... parameters) {
return makeRequest(endpoint, HttpMethod.POST, responseType, data, parameters);
}
@Override
public AuthTokensV2 obtainAuthTokensFromAuthCodeV2(String appId, String appSecret,
String authCode, String redirectURI) {
final TokenRequestBody requestBody =
new TokenRequestBody(appId, appSecret, authCode, redirectURI);
return makeRequest(ENDPOINT_TOKEN, HttpMethod.POST, TokenResponse.class, requestBody)
.getAuthTokens();
}
@Override
public AuthTokensV2 obtainAuthTokensFromRefreshTokenV2(String appId, String appSecret,
String refreshToken) {
final RefreshTokenRequestBody requestBody =
new RefreshTokenRequestBody(appId, appSecret, refreshToken);
return makeRequest(ENDPOINT_REFRESH_TOKEN, HttpMethod.POST, TokenResponse.class, requestBody)
.getAuthTokens();
}
protected T makeRequest(String endpoint, HttpMethod httpMethod, Class responseType,
Object data, Parameter... parameters) {
final Response response = executeRequest(endpoint, httpMethod, data, parameters);
validateResponse(response);
try {
return JSON_FACTORY.fromJson(response.getBody(), responseType);
} catch (JsonSyntaxException ex) {
throw new TikTokJsonMappingException("Error calling endpoint: " + endpoint, ex);
}
}
protected void validateResponse(Response response) {
final Status tikTokStatus = getTikTokStatus(response.getBody());
final TikTokException ex = TikTokAPIException
.fromStatusCode(response.getStatusCode(), tikTokStatus.getCode(),
tikTokStatus.getMessage());
if (ex != null) {
throw ex;
}
}
protected Status getTikTokStatus(String body) {
if (StringUtils.isEmpty(body)) {
return Status.EMPTY;
}
try {
return JSON_FACTORY.fromJson(body, Status.class);
} catch (JsonSyntaxException ex) {
// Treat body like message if it is not JSON
return new Status(null, body);
}
}
protected Response executeRequest(String endpoint, HttpMethod httpMethod, Object data,
Parameter... parameters) {
final String fullEndpoint = createFullEndpoint(endpoint);
final String parameterString = Parameter.toURLEncodedString(parameters);
try {
switch (httpMethod) {
case GET:
return webRequestor.executeGet(fullEndpoint + parameterString);
case POST:
final String jsonBody = data == null ? null : JSON_FACTORY.toJson(data);
return webRequestor.executePost(fullEndpoint, jsonBody, parameterString);
default:
throw new TikTokException("Unrecognised HTTP method: " + httpMethod);
}
} catch (IOException ex) {
throw new TikTokNetworkException("Error calling endpoint: " + endpoint, ex);
}
}
protected String createFullEndpoint(String endpoint) {
while (endpoint.startsWith("/")) {
endpoint = endpoint.substring(1);
}
return String.format("%s/%s/%s", TIKTOK_ENDPOINT_URL, version.getUrlElement(), endpoint);
}
/**
* Wrapper class for TikTok status code and message
* @author eddspencer
*/
@AllArgsConstructor
@NoArgsConstructor
@Getter
static class Status {
private static final Status EMPTY = new Status(null, null);
private Integer code;
private String message;
}
/**
* Request POST body for the oauth2 token endpoint
*
* @author paulpopa
*/
@AllArgsConstructor
@NoArgsConstructor
@Getter
protected static class TokenRequestBody {
@SerializedName("client_id")
private String clientId;
@SerializedName("client_secret")
private String clientSecret;
@SerializedName("grant_type")
private String grantType;
@SerializedName("auth_code")
private String authCode;
@SerializedName("redirect_uri")
private String redirectURI;
public TokenRequestBody(String clientId, String clientSecret, String authCode,
String redirectURI) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.grantType = "authorization_code";
this.authCode = authCode;
this.redirectURI = redirectURI;
}
}
/**
* Request POST body for the oauth2 refresh token endpoint
*
* @author paulpopa
*/
@AllArgsConstructor
@NoArgsConstructor
@Getter
protected static class RefreshTokenRequestBody {
@SerializedName("client_id")
private String clientId;
@SerializedName("client_secret")
private String clientSecret;
@SerializedName("grant_type")
private String grantType;
@SerializedName("refresh_token")
private String refreshToken;
public RefreshTokenRequestBody(String clientId, String clientSecret, String refreshToken) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.grantType = "refresh_token";
this.refreshToken = refreshToken;
}
}
}