diff --git a/line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobClient.java b/line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobClient.java new file mode 100644 index 000000000..3d7dd64ca --- /dev/null +++ b/line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobClient.java @@ -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 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 addUserIdsToAudience( + long audienceGroupId, + String uploadDescription, + File file + ); + + static ManageAudienceBlobClientBuilder builder() { + return new ManageAudienceBlobClientBuilder(); + } +} diff --git a/line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobClientBuilder.java b/line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobClientBuilder.java new file mode 100644 index 000000000..207edaaaf --- /dev/null +++ b/line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobClientBuilder.java @@ -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. + * + *

Default value = "https://api-data.line.me/". + */ + private URI apiEndPoint = LineClientConstants.DEFAULT_BLOB_END_POINT; + + /** + * API Endpoint. + * + *

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. + * + *

Default value = {@value LineClientConstants#DEFAULT_CONNECT_TIMEOUT_MILLIS}ms. + */ + @Setter + private long connectTimeout = LineClientConstants.DEFAULT_CONNECT_TIMEOUT_MILLIS; + + /** + * Connection timeout. + * + *

Default value = {@value LineClientConstants#DEFAULT_READ_TIMEOUT_MILLIS}ms. + */ + @Setter + private long readTimeout = LineClientConstants.DEFAULT_READ_TIMEOUT_MILLIS; + + /** + * Write timeout. + * + *

Default value = {@value LineClientConstants#DEFAULT_WRITE_TIMEOUT_MILLIS}ms. + */ + @Setter + private long writeTimeout = LineClientConstants.DEFAULT_WRITE_TIMEOUT_MILLIS; + + /** + * Channel token supplier of this client. + * + *

MUST BE NULL except you configured your own + */ + @Setter + private ChannelTokenSupplier channelTokenSupplier; + + /** + * Custom {@link Retrofit.Builder} used internally. + * + *

If you want to use your own setting, specify {@link Retrofit.Builder} instance. + * Default builder is used in case of {@code null} (default). + * + *

To use this method, please add dependency to 'com.squareup.retrofit2:retrofit'. + * + * @see #createDefaultRetrofitBuilder() + */ + @Setter + private Retrofit.Builder retrofitBuilder; + + /** + * Add authentication header. + * + *

Default = {@value}. If you manage authentication header yourself, set to {@code false}. + */ + @Setter + private boolean addAuthenticationHeader = true; + + private OkHttpClient.Builder okHttpClientBuilder; + + /** + * Custom interceptors. + * + *

You can add your own interceptors. + * + *

Note: Authentication interceptor is automatically added by default. + * + * @see #addAuthenticationHeader(boolean) + */ + @Setter + private List 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. + * + *

In case of you need your own customized {@link OkHttpClient}, + * this builder allows specify {@link OkHttpClient.Builder} instance. + * + *

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 buildRetrofitIface(URI apiEndPoint, Class 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)); + } +} diff --git a/line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobClientImpl.java b/line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobClientImpl.java new file mode 100644 index 000000000..644e5834b --- /dev/null +++ b/line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobClientImpl.java @@ -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 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 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 CompletableFuture toFuture(Call call) { + final CallbackCompletableFuture future = new CallbackCompletableFuture<>(); + call.enqueue(future); + return future; + } + + private static class CallbackCompletableFuture extends CompletableFuture implements Callback { + @Override + public void onResponse(final Call call, final Response 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 call, final Throwable t) { + completeExceptionally(new ManageAudienceException(t.getMessage(), t)); + } + } +} diff --git a/line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobService.java b/line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobService.java new file mode 100644 index 000000000..a836099b4 --- /dev/null +++ b/line-bot-api-client/src/main/java/com/linecorp/bot/client/ManageAudienceBlobService.java @@ -0,0 +1,33 @@ +/* + * 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 com.linecorp.bot.model.manageaudience.response.CreateAudienceForUploadingResponse; + +import okhttp3.MultipartBody; +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.POST; +import retrofit2.http.PUT; + +public interface ManageAudienceBlobService { + @POST("v2/bot/audienceGroup/upload/byFile") + Call createAudienceForUploadingUserIds(@Body MultipartBody body); + + @PUT("v2/bot/audienceGroup/upload/byFile") + Call addUserIdsToAudience(@Body MultipartBody body); +} diff --git a/line-bot-api-client/src/test/java/com/linecorp/bot/client/ManageAudienceBlobClientImplTest.java b/line-bot-api-client/src/test/java/com/linecorp/bot/client/ManageAudienceBlobClientImplTest.java new file mode 100644 index 000000000..a31e889f1 --- /dev/null +++ b/line-bot-api-client/src/test/java/com/linecorp/bot/client/ManageAudienceBlobClientImplTest.java @@ -0,0 +1,197 @@ +/* + * 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.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +import org.assertj.core.util.Files; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.stubbing.OngoingStubbing; + +import com.linecorp.bot.model.manageaudience.response.CreateAudienceForUploadingResponse; +import com.linecorp.bot.model.response.BotApiResponse; + +import okhttp3.Headers; +import okhttp3.Request; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class ManageAudienceBlobClientImplTest { + private static final String REQUEST_ID_FIXTURE = "REQUEST_ID_FIXTURE"; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Rule + public final Timeout timeoutRule = Timeout.seconds(5); + + @Mock + private ManageAudienceBlobService retrofitMock; + + @InjectMocks + private ManageAudienceBlobClientImpl target; + + @Test + public void createAudienceForUploadingUserIds() throws Exception { + File tmpFile = Files.newTemporaryFile(); + tmpFile.deleteOnExit(); + + CreateAudienceForUploadingResponse response = + CreateAudienceForUploadingResponse.builder() + .build(); + + whenCall(retrofitMock.createAudienceForUploadingUserIds( + any()), response); + + final CreateAudienceForUploadingResponse actual = + target.createAudienceForUploadingUserIds( + "Hello", + false, + "UPLOAD!", + tmpFile + ).get(); + verify(retrofitMock, only()).createAudienceForUploadingUserIds(any()); + assertThat(actual).isEqualTo(response); + } + + @Test + public void addUserIdsToAudience() throws Exception { + File tmpFile = Files.newTemporaryFile(); + tmpFile.deleteOnExit(); + + when(retrofitMock.addUserIdsToAudience(any())).thenReturn(new VoidCall()); + + final BotApiResponse actual = + target.addUserIdsToAudience( + 5963L, + "UPLOAD!", + tmpFile + ).get(); + verify(retrofitMock, only()).addUserIdsToAudience(any()); + assertThat(actual).isEqualTo(new BotApiResponse(null, "", Arrays.asList())); + } + + // Utility methods + + private static void whenCall(Call call, T value) { + final OngoingStubbing> callOngoingStubbing = when(call); + callOngoingStubbing.thenReturn(enqueue(value)); + } + + private static Call enqueue(T value) { + return new Call() { + @Override + public Response execute() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void enqueue(Callback callback) { + final Headers headers = Headers.of(singletonMap("x-line-request-id", REQUEST_ID_FIXTURE)); + callback.onResponse(this, Response.success(value, headers)); + } + + @Override + public boolean isExecuted() { + throw new UnsupportedOperationException(); + } + + @Override + public void cancel() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isCanceled() { + throw new UnsupportedOperationException(); + } + + @Override + public Call clone() { + throw new UnsupportedOperationException(); + } + + @Override + public Request request() { + throw new UnsupportedOperationException(); + } + + @Override + public okio.Timeout timeout() { + throw new UnsupportedOperationException(); + } + }; + } + + private static class VoidCall implements Call { + @Override + public Response execute() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void enqueue(Callback callback) { + callback.onResponse(new VoidCall(), Response.success(null)); + } + + @Override + public boolean isExecuted() { + throw new UnsupportedOperationException(); + } + + @Override + public void cancel() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isCanceled() { + throw new UnsupportedOperationException(); + } + + @Override + public Call clone() { + throw new UnsupportedOperationException(); + } + + @Override + public Request request() { + throw new UnsupportedOperationException(); + } + + @Override + public okio.Timeout timeout() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/line-bot-model/src/main/java/com/linecorp/bot/model/manageaudience/ManageAudienceException.java b/line-bot-model/src/main/java/com/linecorp/bot/model/manageaudience/ManageAudienceException.java new file mode 100644 index 000000000..b59c64c24 --- /dev/null +++ b/line-bot-model/src/main/java/com/linecorp/bot/model/manageaudience/ManageAudienceException.java @@ -0,0 +1,60 @@ +/* + * 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.model.manageaudience; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Getter; +import lombok.ToString; + +/** + * A general {@link Exception} for LINE manage audience API. + */ +@Getter +@ToString +@SuppressWarnings("serial") +public class ManageAudienceException extends RuntimeException { + /** + * Error summary. + */ + private final String message; + + /** + * A description of the error. + */ + private final String details; + + @JsonCreator + public ManageAudienceException(@JsonProperty("message") String message, + @JsonProperty("details") String details) { + this.message = message; + this.details = details; + } + + public ManageAudienceException(String message) { + super(message); + this.message = message; + this.details = null; + } + + public ManageAudienceException(String message, Throwable t) { + super(message, t); + this.message = message; + this.details = t.getMessage(); + } +} diff --git a/line-bot-model/src/main/java/com/linecorp/bot/model/manageaudience/response/CreateAudienceForUploadingResponse.java b/line-bot-model/src/main/java/com/linecorp/bot/model/manageaudience/response/CreateAudienceForUploadingResponse.java new file mode 100644 index 000000000..6ac2ceb8b --- /dev/null +++ b/line-bot-model/src/main/java/com/linecorp/bot/model/manageaudience/response/CreateAudienceForUploadingResponse.java @@ -0,0 +1,53 @@ +/* + * 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.model.manageaudience.response; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; + +import lombok.Builder; +import lombok.Value; + +@Value +@Builder +@JsonDeserialize(builder = CreateAudienceForUploadingResponse.CreateAudienceForUploadingResponseBuilder.class) +public class CreateAudienceForUploadingResponse { + /** + * The audience ID. + */ + long audienceGroupId; + + /** + * Constant. "UPLOAD". + */ + String type; + + /** + * The audience's name. + */ + String description; + + /** + * When the audience was created (in UNIX time). + */ + long created; + + @JsonPOJOBuilder(withPrefix = "") + public static class CreateAudienceForUploadingResponseBuilder { + // Filled by lombok + } +} diff --git a/line-bot-spring-boot/src/main/java/com/linecorp/bot/spring/boot/LineBotAutoConfiguration.java b/line-bot-spring-boot/src/main/java/com/linecorp/bot/spring/boot/LineBotAutoConfiguration.java index 20e83bf12..fd19ad5f8 100644 --- a/line-bot-spring-boot/src/main/java/com/linecorp/bot/spring/boot/LineBotAutoConfiguration.java +++ b/line-bot-spring-boot/src/main/java/com/linecorp/bot/spring/boot/LineBotAutoConfiguration.java @@ -28,6 +28,7 @@ import com.linecorp.bot.client.FixedChannelTokenSupplier; import com.linecorp.bot.client.LineBlobClient; import com.linecorp.bot.client.LineMessagingClient; +import com.linecorp.bot.client.ManageAudienceBlobClient; import com.linecorp.bot.client.ManageAudienceClient; import com.linecorp.bot.spring.boot.support.LineMessageHandlerSupport; @@ -100,6 +101,23 @@ public ChannelManagementSyncClient channelManagementClient( .build(); } + /** + * Expose {@link ManageAudienceBlobClient} as {@link Bean}. + */ + @Bean + @ConditionalOnMissingBean + public ManageAudienceBlobClient manageAudienceBlobClient( + final ChannelTokenSupplier channelTokenSupplier) { + return ManageAudienceBlobClient + .builder() + .channelTokenSupplier(channelTokenSupplier) + .apiEndPoint(lineBotProperties.getBlobEndPoint()) + .connectTimeout(lineBotProperties.getConnectTimeout()) + .readTimeout(lineBotProperties.getReadTimeout()) + .writeTimeout(lineBotProperties.getWriteTimeout()) + .build(); + } + /** * Expose {@link ManageAudienceClient} as {@link Bean}. */ diff --git a/sample-manage-audience/src/main/java/com/linecorp/bot/messagingapidemoapp/controller/ManageAudienceController.java b/sample-manage-audience/src/main/java/com/linecorp/bot/messagingapidemoapp/controller/ManageAudienceController.java index de98f722f..ab08e1d7d 100644 --- a/sample-manage-audience/src/main/java/com/linecorp/bot/messagingapidemoapp/controller/ManageAudienceController.java +++ b/sample-manage-audience/src/main/java/com/linecorp/bot/messagingapidemoapp/controller/ManageAudienceController.java @@ -12,11 +12,12 @@ * 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.messagingapidemoapp.controller; +import java.io.File; +import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -28,8 +29,10 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.view.RedirectView; +import com.linecorp.bot.client.ManageAudienceBlobClient; import com.linecorp.bot.client.ManageAudienceClient; import com.linecorp.bot.model.manageaudience.AudienceGroup; import com.linecorp.bot.model.manageaudience.request.AddAudienceToAudienceGroupRequest; @@ -48,6 +51,7 @@ @Slf4j public class ManageAudienceController { private final ManageAudienceClient client; + private final ManageAudienceBlobClient blobClient; @GetMapping("/manage_audience/") public CompletableFuture list( @@ -109,6 +113,32 @@ public CompletableFuture postUpload( response -> new RedirectView("/manage_audience/" + response.getAudienceGroupId())); } + @GetMapping("/manage_audience/upload_by_file") + public String uploadByFile() { + return "manage_audience/upload_by_file"; + } + + @PostMapping("/manage_audience/upload_by_file") + public CompletableFuture postUploadByFile( + @RequestParam String description, + @RequestParam Boolean isIfaAudience, + @RequestParam(required = false) String uploadDescription, + @RequestParam MultipartFile file + ) throws IOException { + File convFile = File.createTempFile("temp", ".dat"); + convFile.deleteOnExit(); + file.transferTo(convFile); + + return blobClient.createAudienceForUploadingUserIds( + description, isIfaAudience, uploadDescription, convFile + ).thenApply( + response -> new RedirectView("/manage_audience/" + response.getAudienceGroupId()) + ).whenComplete((a, b) -> { + boolean deleted = convFile.delete(); + log.info("Deleted temporary file: {}", deleted); + }); + } + @GetMapping("/manage_audience/update_description/{audienceGroupId}") public CompletableFuture updateDescription(@PathVariable Long audienceGroupId, Model model) { @@ -202,5 +232,35 @@ public CompletableFuture postAddAudience( return client.addAudienceToAudienceGroup(request) .thenApply(it -> new RedirectView("/manage_audience/" + audienceGroupId)); } + + @GetMapping("/manage_audience/add_audience_by_file/{audienceGroupId}") + public CompletableFuture addAudienceByFile(@PathVariable Long audienceGroupId, + Model model) { + return client.getAudienceData(audienceGroupId) + .thenApply(response -> { + AudienceGroup audienceGroup = response.getAudienceGroup(); + model.addAttribute("audienceGroup", audienceGroup); + return "manage_audience/add_audience_by_file"; + }); + } + + @PostMapping("/manage_audience/add_audience_by_file/{audienceGroupId}") + public CompletableFuture postAddAudienceByFile( + @PathVariable Long audienceGroupId, + @RequestParam(required = false) String uploadDescription, + @RequestParam MultipartFile file) throws IOException { + File convFile = File.createTempFile("temp", ".dat"); + convFile.deleteOnExit(); + file.transferTo(convFile); + + return blobClient.addUserIdsToAudience( + audienceGroupId, uploadDescription, convFile + ).thenApply( + it -> new RedirectView("/manage_audience/" + audienceGroupId) + ).whenComplete((a, b) -> { + boolean deleted = convFile.delete(); + log.info("Deleted temporary file: {}", deleted); + }); + } } diff --git a/sample-manage-audience/src/main/resources/templates/error.ftlh b/sample-manage-audience/src/main/resources/templates/error.ftlh new file mode 100644 index 000000000..4af649227 --- /dev/null +++ b/sample-manage-audience/src/main/resources/templates/error.ftlh @@ -0,0 +1,15 @@ +<#-- @ftlvariable name="totalUsage" type="java.lang.Long" --> +<#import "__wrapper.ftlh" as wrapper> +<@wrapper.main> + +

ERRRORRRR

+ +
status: ${status!"-"}
+
error: ${error!"-"}
+
exception: ${exception!"-"}
+
message: ${message!"-"}
+
errors: ${errors!"-"}
+
trace: ${trace!"-"}
+
path: ${path!"-"}
+ + diff --git a/sample-manage-audience/src/main/resources/templates/manage_audience/add_audience_by_file.ftlh b/sample-manage-audience/src/main/resources/templates/manage_audience/add_audience_by_file.ftlh new file mode 100644 index 000000000..e043793e6 --- /dev/null +++ b/sample-manage-audience/src/main/resources/templates/manage_audience/add_audience_by_file.ftlh @@ -0,0 +1,21 @@ +<#-- @ftlvariable name="audienceGroup" type="com.linecorp.bot.model.manageaudience.response.GetAudienceDataResponse" --> +<#-- @ftlvariable name="jobs" type="java.util.List" --> +<#import "../__wrapper.ftlh" as wrapper> +<@wrapper.main> +

Add more audience into ${audienceGroup.description}(ID: ${audienceGroup.audienceGroupId})

+ +
+
+ + +
+
+ + + UserID list or IFA list split by new line. +
+ +
+ + diff --git a/sample-manage-audience/src/main/resources/templates/manage_audience/list.ftlh b/sample-manage-audience/src/main/resources/templates/manage_audience/list.ftlh index d7ba682d6..e38ec2402 100644 --- a/sample-manage-audience/src/main/resources/templates/manage_audience/list.ftlh +++ b/sample-manage-audience/src/main/resources/templates/manage_audience/list.ftlh @@ -5,6 +5,7 @@

LINE Messaging API Demo App

Upload new audience group + Upload new audience group(by file) Create new imp based audience Create new click based audience diff --git a/sample-manage-audience/src/main/resources/templates/manage_audience/show.ftlh b/sample-manage-audience/src/main/resources/templates/manage_audience/show.ftlh index 197b97358..1fab205c6 100644 --- a/sample-manage-audience/src/main/resources/templates/manage_audience/show.ftlh +++ b/sample-manage-audience/src/main/resources/templates/manage_audience/show.ftlh @@ -9,6 +9,9 @@ audience group Add audience for this audience group. + Add + audience for this audience group(By file) <#else>