-
Notifications
You must be signed in to change notification settings - Fork 948
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Manage audience upload by file (#573)
* Support create/update audience group by file API. * manage audience by file.
- Loading branch information
Showing
14 changed files
with
884 additions
and
1 deletion.
There are no files selected for viewing
49 changes: 49 additions & 0 deletions
49
line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* Copyright 2020 LINE Corporation | ||
* | ||
* LINE Corporation 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.linecorp.bot.client; | ||
|
||
import java.io.File; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
import com.linecorp.bot.model.manageaudience.response.CreateAudienceForUploadingResponse; | ||
import com.linecorp.bot.model.response.BotApiResponse; | ||
|
||
public interface ManageAudienceBlobClient { | ||
|
||
/** | ||
* Create audience for uploading user IDs (by file). | ||
*/ | ||
CompletableFuture<CreateAudienceForUploadingResponse> createAudienceForUploadingUserIds( | ||
String description, | ||
boolean isIfaAudience, | ||
String uploadDescription, | ||
File file | ||
); | ||
|
||
/** | ||
* Add user IDs or Identifiers for Advertisers (IFAs) to an audience for uploading user IDs (by file). | ||
*/ | ||
CompletableFuture<BotApiResponse> addUserIdsToAudience( | ||
long audienceGroupId, | ||
String uploadDescription, | ||
File file | ||
); | ||
|
||
static ManageAudienceBlobClientBuilder builder() { | ||
return new ManageAudienceBlobClientBuilder(); | ||
} | ||
} |
231 changes: 231 additions & 0 deletions
231
...bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobClientBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
/* | ||
* Copyright 2020 LINE Corporation | ||
* | ||
* LINE Corporation 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.linecorp.bot.client; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
import java.net.URI; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
||
import com.linecorp.bot.model.objectmapper.ModelObjectMapper; | ||
|
||
import lombok.NonNull; | ||
import lombok.Setter; | ||
import lombok.ToString; | ||
import lombok.experimental.Accessors; | ||
import lombok.experimental.PackagePrivate; | ||
import okhttp3.Interceptor; | ||
import okhttp3.OkHttpClient; | ||
import okhttp3.logging.HttpLoggingInterceptor; | ||
import okhttp3.logging.HttpLoggingInterceptor.Level; | ||
import retrofit2.Retrofit; | ||
import retrofit2.converter.jackson.JacksonConverterFactory; | ||
|
||
@ToString | ||
@Accessors(fluent = true) | ||
public class ManageAudienceBlobClientBuilder { | ||
private static final ObjectMapper objectMapper = ModelObjectMapper.createNewObjectMapper(); | ||
|
||
/** | ||
* Use {@link ManageAudienceBlobClient#builder()} to create instance. | ||
*/ | ||
@PackagePrivate | ||
ManageAudienceBlobClientBuilder() { | ||
} | ||
|
||
/** | ||
* API Endpoint. | ||
* | ||
* <p>Default value = "https://api-data.line.me/". | ||
*/ | ||
private URI apiEndPoint = LineClientConstants.DEFAULT_BLOB_END_POINT; | ||
|
||
/** | ||
* API Endpoint. | ||
* | ||
* <p>Default value = "https://api.line.me/". | ||
*/ // We can remove this after delete `setApiEndPoint(String apiEndPoint)`. | ||
public ManageAudienceBlobClientBuilder apiEndPoint(URI apiEndPoint) { | ||
this.apiEndPoint = requireNonNull(apiEndPoint, "apiEndPoint"); | ||
return this; | ||
} | ||
|
||
/** | ||
* Connection timeout. | ||
* | ||
* <p>Default value = {@value LineClientConstants#DEFAULT_CONNECT_TIMEOUT_MILLIS}ms. | ||
*/ | ||
@Setter | ||
private long connectTimeout = LineClientConstants.DEFAULT_CONNECT_TIMEOUT_MILLIS; | ||
|
||
/** | ||
* Connection timeout. | ||
* | ||
* <p>Default value = {@value LineClientConstants#DEFAULT_READ_TIMEOUT_MILLIS}ms. | ||
*/ | ||
@Setter | ||
private long readTimeout = LineClientConstants.DEFAULT_READ_TIMEOUT_MILLIS; | ||
|
||
/** | ||
* Write timeout. | ||
* | ||
* <p>Default value = {@value LineClientConstants#DEFAULT_WRITE_TIMEOUT_MILLIS}ms. | ||
*/ | ||
@Setter | ||
private long writeTimeout = LineClientConstants.DEFAULT_WRITE_TIMEOUT_MILLIS; | ||
|
||
/** | ||
* Channel token supplier of this client. | ||
* | ||
* <p>MUST BE NULL except you configured your own | ||
*/ | ||
@Setter | ||
private ChannelTokenSupplier channelTokenSupplier; | ||
|
||
/** | ||
* Custom {@link Retrofit.Builder} used internally. | ||
* | ||
* <p>If you want to use your own setting, specify {@link Retrofit.Builder} instance. | ||
* Default builder is used in case of {@code null} (default). | ||
* | ||
* <p>To use this method, please add dependency to 'com.squareup.retrofit2:retrofit'. | ||
* | ||
* @see #createDefaultRetrofitBuilder() | ||
*/ | ||
@Setter | ||
private Retrofit.Builder retrofitBuilder; | ||
|
||
/** | ||
* Add authentication header. | ||
* | ||
* <p>Default = {@value}. If you manage authentication header yourself, set to {@code false}. | ||
*/ | ||
@Setter | ||
private boolean addAuthenticationHeader = true; | ||
|
||
private OkHttpClient.Builder okHttpClientBuilder; | ||
|
||
/** | ||
* Custom interceptors. | ||
* | ||
* <p>You can add your own interceptors. | ||
* | ||
* <p>Note: Authentication interceptor is automatically added by default. | ||
* | ||
* @see #addAuthenticationHeader(boolean) | ||
*/ | ||
@Setter | ||
private List<Interceptor> additionalInterceptors = new ArrayList<>(); | ||
|
||
/** | ||
* Set fixed channel token. This overwrites {@link #channelTokenSupplier(ChannelTokenSupplier)}. | ||
* | ||
* @see #channelTokenSupplier(ChannelTokenSupplier) | ||
*/ | ||
public ManageAudienceBlobClientBuilder channelToken(String channelToken) { | ||
channelTokenSupplier(FixedChannelTokenSupplier.of(channelToken)); | ||
return this; | ||
} | ||
|
||
/** | ||
* Set customized OkHttpClient.Builder. | ||
* | ||
* <p>In case of you need your own customized {@link OkHttpClient}, | ||
* this builder allows specify {@link OkHttpClient.Builder} instance. | ||
* | ||
* <p>To use this method, please add dependency to 'com.squareup.retrofit2:retrofit'. | ||
* | ||
* @param addAuthenticationHeader If true, all default okhttp interceptors ignored. | ||
* You should insert authentication headers yourself. | ||
*/ | ||
public ManageAudienceBlobClientBuilder okHttpClientBuilder( | ||
final @NonNull OkHttpClient.Builder okHttpClientBuilder, | ||
final boolean addAuthenticationHeader) { | ||
this.okHttpClientBuilder = okHttpClientBuilder; | ||
this.addAuthenticationHeader = addAuthenticationHeader; | ||
|
||
return this; | ||
} | ||
|
||
/** | ||
* Creates a new {@link ManageAudienceBlobService}. | ||
*/ | ||
<T> T buildRetrofitIface(URI apiEndPoint, Class<T> retrofitIFace) { | ||
if (okHttpClientBuilder == null) { | ||
okHttpClientBuilder = new OkHttpClient.Builder(); | ||
} | ||
|
||
// Add interceptors. | ||
if (addAuthenticationHeader) { | ||
okHttpClientBuilder.addInterceptor(buildAuthenticationInterceptor(channelTokenSupplier)); | ||
} | ||
if (additionalInterceptors != null) { | ||
additionalInterceptors.forEach(okHttpClientBuilder::addInterceptor); | ||
} | ||
okHttpClientBuilder.addInterceptor(buildLoggingInterceptor()); | ||
|
||
// Set timeout. | ||
okHttpClientBuilder | ||
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) | ||
.readTimeout(readTimeout, TimeUnit.MILLISECONDS) | ||
.writeTimeout(writeTimeout, TimeUnit.MILLISECONDS); | ||
|
||
final OkHttpClient okHttpClient = okHttpClientBuilder.build(); | ||
|
||
if (retrofitBuilder == null) { | ||
retrofitBuilder = createDefaultRetrofitBuilder(); | ||
} | ||
retrofitBuilder.client(okHttpClient); | ||
retrofitBuilder.baseUrl(apiEndPoint.toString()); | ||
|
||
final Retrofit retrofit = retrofitBuilder.build(); | ||
|
||
return retrofit.create(retrofitIFace); | ||
} | ||
|
||
static HeaderInterceptor buildAuthenticationInterceptor(ChannelTokenSupplier channelTokenSupplier) { | ||
requireNonNull(channelTokenSupplier, "channelTokenSupplier"); | ||
return HeaderInterceptor.forChannelTokenSupplier(channelTokenSupplier); | ||
} | ||
|
||
static Interceptor buildLoggingInterceptor() { | ||
final Logger slf4jLogger = LoggerFactory.getLogger("com.linecorp.bot.client.wire"); | ||
|
||
return new HttpLoggingInterceptor(slf4jLogger::info) | ||
.setLevel(Level.BODY); | ||
} | ||
|
||
static Retrofit.Builder createDefaultRetrofitBuilder() { | ||
return new Retrofit.Builder() | ||
.addConverterFactory(JacksonConverterFactory.create(objectMapper)); | ||
} | ||
|
||
/** | ||
* Creates a new {@link ManageAudienceBlobService}. | ||
*/ | ||
public ManageAudienceBlobClient build() { | ||
return new ManageAudienceBlobClientImpl( | ||
buildRetrofitIface(apiEndPoint, ManageAudienceBlobService.class)); | ||
} | ||
} |
106 changes: 106 additions & 0 deletions
106
line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobClientImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* | ||
* Copyright 2020 LINE Corporation | ||
* | ||
* LINE Corporation 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.linecorp.bot.client; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
||
import com.linecorp.bot.model.manageaudience.ManageAudienceException; | ||
import com.linecorp.bot.model.manageaudience.response.CreateAudienceForUploadingResponse; | ||
import com.linecorp.bot.model.objectmapper.ModelObjectMapper; | ||
import com.linecorp.bot.model.response.BotApiResponse; | ||
|
||
import okhttp3.MediaType; | ||
import okhttp3.MultipartBody; | ||
import okhttp3.MultipartBody.Builder; | ||
import okhttp3.RequestBody; | ||
import retrofit2.Call; | ||
import retrofit2.Callback; | ||
import retrofit2.Response; | ||
|
||
public class ManageAudienceBlobClientImpl implements ManageAudienceBlobClient { | ||
private static final ObjectMapper objectMapper = ModelObjectMapper.createNewObjectMapper(); | ||
private final ManageAudienceBlobService retrofitImpl; | ||
|
||
public ManageAudienceBlobClientImpl(ManageAudienceBlobService retrofitImpl) { | ||
this.retrofitImpl = retrofitImpl; | ||
} | ||
|
||
@Override | ||
public CompletableFuture<CreateAudienceForUploadingResponse> createAudienceForUploadingUserIds( | ||
String description, boolean isIfaAudience, String uploadDescription, File file) { | ||
MultipartBody parts = new MultipartBody.Builder() | ||
.addFormDataPart("description", description) | ||
.addFormDataPart("isIfaAudience", String.valueOf(isIfaAudience)) | ||
.addFormDataPart("uploadDescription", uploadDescription) | ||
.addFormDataPart("file", file.getName(), | ||
RequestBody.create(MediaType.get("text/plain"), file)) | ||
.build(); | ||
|
||
return toFuture(retrofitImpl.createAudienceForUploadingUserIds(parts)); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<BotApiResponse> addUserIdsToAudience(long audienceGroupId, | ||
String uploadDescription, | ||
File file) { | ||
MultipartBody parts = new Builder() | ||
.addFormDataPart("audienceGroupId", String.valueOf(audienceGroupId)) | ||
.addFormDataPart("uploadDescription", uploadDescription) | ||
.addFormDataPart("file", file.getName(), | ||
RequestBody.create(MediaType.get("text/plain"), file)) | ||
.build(); | ||
|
||
return LineMessagingClientImpl.toBotApiFuture(retrofitImpl.addUserIdsToAudience( | ||
parts | ||
)); | ||
} | ||
|
||
private static <T> CompletableFuture<T> toFuture(Call<T> call) { | ||
final CallbackCompletableFuture<T> future = new CallbackCompletableFuture<>(); | ||
call.enqueue(future); | ||
return future; | ||
} | ||
|
||
private static class CallbackCompletableFuture<T> extends CompletableFuture<T> implements Callback<T> { | ||
@Override | ||
public void onResponse(final Call<T> call, final Response<T> response) { | ||
if (response.isSuccessful()) { | ||
complete(response.body()); | ||
return; | ||
} | ||
if (response.code() == 400) { | ||
try { | ||
completeExceptionally(objectMapper.readValue(response.errorBody().string(), | ||
ManageAudienceException.class)); | ||
return; | ||
} catch (IOException e) { | ||
completeExceptionally(e); | ||
} | ||
} | ||
completeExceptionally(new ManageAudienceException(response.message())); | ||
} | ||
|
||
@Override | ||
public void onFailure(final Call<T> call, final Throwable t) { | ||
completeExceptionally(new ManageAudienceException(t.getMessage(), t)); | ||
} | ||
} | ||
} |
Oops, something went wrong.