최현준

line-bot-sdk-java reset

Showing 492 changed files with 4885 additions and 0 deletions
1 +codecov:
2 + notify:
3 + require_ci_to_pass: no
4 +
5 +ignore:
6 + - 'line-bot-model/src/test' # Workaround for line-bot-model's test source is wrongly included by coverage report.
7 +
8 +coverage:
9 + range: 50..100
10 + round: down
11 + precision: 2
12 + status:
13 + patch:
14 + default:
15 + # basic
16 + target: auto
17 + threshold: null
18 + base: auto
19 + # advanced
20 + branches: null
21 + if_no_uploads: error
22 + if_not_found: success
23 + if_ci_failed: error
24 + only_pulls: false
25 + flags: null
26 + paths: null
27 +
28 +comment:
29 + layout: "reach, diff, flags, files"
30 + behavior: default
31 + require_changes: false # if true: only post the comment if coverage changes
32 + require_base: no # [yes :: must have a base report to post]
33 + require_head: no # [yes :: must have a head report to post]
34 + branches: null
1 +root=true
2 +
3 +[*]
4 +charset=utf-8
5 +end_of_line=lf
6 +insert_final_newline=true
7 +trim_trailing_whitespace=true
8 +indent_style=space
9 +indent_size=4
10 +
11 +[*.java]
12 +continuation_indent_size=8
13 +wildcard_import_limit=0
14 +
15 +[*.{yaml,yml}]
16 +indent_size=2
17 +
18 +[*.json]
19 +indent_size=2
20 +
21 +[*.xml]
22 +indent_size=2
1 +## Before creating an issue
2 +
3 +- Check [documentation](https://developers.line.me/en/docs/) and [FAQ](https://developers.line.me/en/faq/messaging-api/) page for more information on LINE bots and Messaging API
4 +- Make sure your issue is **related to** the LINE Bot SDK
5 +- For general queries about LINE bots, post a question to [LINE developers community](https://www.line-community.me/questions) website
6 +- Note that we don't provide technical support
7 +
8 +## If you still need to create an issue
9 +
10 +- Provide detailed information about the issue you are facing with the SDK
11 +- Provide logs whenever possible
12 +
13 +Thank you :)
1 +---
2 +name: Bug report
3 +about: Create a report to help us improve
4 +title: '[BUG]'
5 +labels: 'bug'
6 +assignees: 'imasahiro'
7 +
8 +---
9 +
10 +## Bug Report
11 +<!-- First of all: Have you checked the docs https://developers.line.biz/en/docs/messaging-api/overview/, Q&A page https://developers.line.biz/en/faq/, https://www.line-community.me/questions, GitHub issues whether someone else has already reported your issue? -->
12 +
13 +**Describe the bug**
14 +A clear and concise description of what the bug is.
15 +
16 +**To Reproduce**
17 +<!-- It would be appreciate if you share the minimal complete reproducible Java/Kotlin/Scala/Groovy/… code or Repo link: -->
18 +Steps to reproduce the behavior:
19 +1. Send message on '...'
20 +2. Receive message on '....'
21 +3. Scroll down to '....'
22 +4. See error
23 +
24 +**Expected behavior**
25 +A clear and concise description of what you expected to happen.
26 +
27 +**Screenshots**
28 +If applicable, add screenshots to help explain your problem.
29 +
30 +**Environment (please complete the following information):**
31 + - OS: [e.g. Ubuntu]
32 + - JDK Version [e.g. openjdk 1.8.0_211]
33 + - line-bot-sdk-java version(s) [e.g. 2.6.1]
34 +
35 +**Additional context**
36 +Add any other context about the problem here.
1 +---
2 +name: Feature request
3 +about: Suggest an idea for this project
4 +title: "[Feature Request]"
5 +labels: feature request
6 +assignees: imasahiro
7 +
8 +---
9 +
10 +## Feature Request
11 +<!-- First of all: Have you checked the docs https://developers.line.biz/en/docs/messaging-api/overview/, Q&A page https://developers.line.biz/en/faq/, https://www.line-community.me/questions, GitHub issues whether someone else has already reported your issue? -->
12 +
13 +**Is your feature request related to a problem? Please describe.**
14 +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
15 +
16 +**Describe the solution you'd like**
17 +A clear and concise description of what you want to happen.
18 +
19 +**Describe alternatives you've considered**
20 +A clear and concise description of any alternative solutions or features you've considered.
21 +
22 +**Additional context**
23 +Add any other context or screenshots about the feature request here.
1 +# Build output
2 +target/
3 +build/
4 +out/
5 +
6 +# gradle
7 +/.gradle/
8 +
9 +# Mobile Tools for Java (J2ME)
10 +.mtj.tmp/
11 +
12 +# Package Files #
13 +*.jar
14 +*.war
15 +*.ear
16 +
17 +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
18 +hs_err_pid*
19 +
20 +# IntelliJ
21 +.idea
22 +*.iml
23 +*.iws
24 +*.ipr
25 +*.ids
26 +*.orig
27 +
28 +# Eclipse
29 +*.pydevproject
30 +.project
31 +.metadata
32 +/bin/**
33 +/tmp/**
34 +/tmp/**/*
35 +*.tmp
36 +*.bak
37 +*.swp
38 +*~.nib
39 +local.properties
40 +.classpath
41 +.settings/**
42 +.loadpath
43 +/src/main/resources/rebel.xml
44 +
45 +# External tool builders
46 +.externalToolBuilders/**
47 +
48 +# Locally stored "Eclipse launch configurations"
49 +*.launch
50 +
51 +# CDT-specific
52 +.cproject
53 +
54 +# PDT-specific
55 +.buildpath
56 +integration_test_settings.yml
1 +language: java
2 +sudo: false
3 +jdk:
4 + - openjdk8
5 +script:
6 + - ./gradlew check
7 + - ./gradlew codeCoverageReport
8 +after_success:
9 + - bash <(curl -s https://codecov.io/bash)
1 +# Contributor Covenant Code of Conduct
2 +
3 +## Our Pledge
4 +
5 +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 +
7 +## Our Standards
8 +
9 +Examples of behavior that contributes to creating a positive environment include:
10 +
11 +* Using welcoming and inclusive language
12 +* Being respectful of differing viewpoints and experiences
13 +* Gracefully accepting constructive criticism
14 +* Focusing on what is best for the community
15 +* Showing empathy towards other community members
16 +
17 +Examples of unacceptable behavior by participants include:
18 +
19 +* The use of sexualized language or imagery and unwelcome sexual attention or advances
20 +* Trolling, insulting/derogatory comments, and personal or political attacks
21 +* Public or private harassment
22 +* Publishing others' private information, such as a physical or electronic address, without explicit permission
23 +* Other conduct which could reasonably be considered inappropriate in a professional setting
24 +
25 +## Our Responsibilities
26 +
27 +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 +
29 +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 +
31 +## Scope
32 +
33 +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 +
35 +## Enforcement
36 +
37 +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dl_oss_dev@linecorp.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 +
39 +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 +
41 +## Attribution
42 +
43 +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 +
45 +[homepage]: http://contributor-covenant.org
46 +[version]: http://contributor-covenant.org/version/1/4/
1 +## How to contribute to LINE Bot SDK for Java project
2 +
3 +First of all, thank you so much for taking your time to contribute! LINE Bot SDK for Java is not very different from any other open
4 +source projects you are aware of. It will be amazing if you could help us by doing any of the following:
5 +
6 +- File an issue in [the issue tracker](https://github.com/line/line-bot-sdk-java/issues) to report bugs and propose new features and
7 + improvements.
8 +- Ask a question using [the issue tracker](https://github.com/line/line-bot-sdk-java/issues).
9 +- Contribute your work by sending [a pull request](https://github.com/line/line-bot-sdk-java/pulls).
10 +
11 +### Contributor license agreement
12 +
13 +When you are sending a pull request and it's a non-trivial change beyond fixing typos, please make sure to sign
14 +[the ICLA (individual contributor license agreement)](https://cla-assistant.io/line/line-bot-sdk-java). Please
15 +[contact us](mailto:dl_oss_dev@linecorp.com) if you need the CCLA (corporate contributor license agreement).
16 +
17 +# Detect outdated dependencies
18 +
19 +Run `./gradlew dependencyUpdates -Drevision=release` checks dependency list
20 +and reports outdated dependencies excepts SpringManaged dependency.
21 +
22 +## ./gradlew dependencyUpdates example
23 +```
24 +% ./gradlew dependencyUpdates -Drevision=release
25 +Download https://plugins.gradle.org/m2/org/springframework/boot/spring-boot-gradle-plugin/maven-metadata.xml
26 +
27 +> Task :dependencyUpdates
28 +
29 +------------------------------------------------------------
30 +: Project Dependency Updates (report to plain text file)
31 +------------------------------------------------------------
32 +
33 +The following dependencies are using the latest release version:
34 + - com.github.ben-manes:gradle-versions-plugin:0.20.0
35 + - com.github.stefanbirkner:system-rules:1.18.0
36 + - com.squareup.okhttp3:logging-interceptor:3.11.0
37 + - com.squareup.okhttp3:mockwebserver:3.11.0
38 + - com.squareup.retrofit2:converter-jackson:2.4.0
39 + - com.squareup.retrofit2:retrofit:2.4.0
40 + - io.franzbecker:gradle-lombok:1.14
41 + - io.spring.gradle:dependency-management-plugin:1.0.6.RELEASE
42 + - org.projectlombok:lombok:1.18.2
43 + - org.slf4j:slf4j-api:1.7.25
44 +
45 +The following dependencies have later release versions:
46 + - com.google.guava:guava [25.1-jre -> 26.0-jre]
47 + https://github.com/google/guava
48 + - gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin [1.6.2 -> 1.6.3]
49 + - gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties [1.4.17 -> 1.5.2]
50 + - io.spring.gradle:propdeps-plugin [0.0.9.RELEASE -> 0.0.10.RELEASE]
51 + - org.jetbrains.kotlin:kotlin-gradle-plugin [1.2.61 -> 1.2.70]
52 + https://kotlinlang.org/
53 + - org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable [1.2.61 -> 1.2.70]
54 + https://kotlinlang.org/
55 + - org.jetbrains.kotlin:kotlin-stdlib-jdk8 [1.2.61 -> 1.2.70]
56 + https://kotlinlang.org/
57 +
58 +Failed to determine the latest version for the following dependencies (use --info for details):
59 + - com.fasterxml.jackson.core:jackson-annotations
60 + - com.fasterxml.jackson.core:jackson-core
61 + - com.fasterxml.jackson.core:jackson-databind
62 + - com.fasterxml.jackson.datatype:jackson-datatype-jsr310
63 + - com.fasterxml.jackson.module:jackson-module-parameter-names
64 + - javax.servlet:javax.servlet-api
65 + - javax.validation:validation-api
66 + - org.hibernate:hibernate-validator
67 + - org.springframework.boot:spring-boot-autoconfigure
68 + - org.springframework.boot:spring-boot-configuration-processor
69 + - org.springframework.boot:spring-boot-gradle-plugin
70 + - org.springframework.boot:spring-boot-starter-logging
71 + - org.springframework.boot:spring-boot-starter-test
72 + - org.springframework.boot:spring-boot-starter-web
73 +
74 +Gradle updates:
75 + - Gradle: [4.8 -> 4.10.1]
76 +
77 +Generated report file build/dependencyUpdates/report.txt
78 +```
79 +
This diff is collapsed. Click to expand it.
1 +web: java $JAVA_OPTS -jar sample-spring-boot-kitchensink/build/libs/sample-spring-boot-kitchensink-*.jar --server.port=$PORT
1 +# LINE Messaging API SDK for Java
2 +
3 +[![Build Status](https://travis-ci.org/line/line-bot-sdk-java.svg?branch=master)](https://travis-ci.org/line/line-bot-sdk-java)
4 +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.linecorp.bot/line-bot-model/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.linecorp.bot/line-bot-model)
5 +[![javadoc](https://javadoc.io/badge2/com.linecorp.bot/line-bot-model/javadoc.svg)](https://javadoc.io/doc/com.linecorp.bot/line-bot-model)
6 +[![codecov](https://codecov.io/gh/line/line-bot-sdk-java/branch/master/graph/badge.svg)](https://codecov.io/gh/line/line-bot-sdk-java)
7 +
8 +
9 +## Introduction
10 +
11 +The LINE Messaging API SDK for Java makes it easy to develop bots using LINE Messaging API, and you can create a sample bot within minutes.
12 +
13 +
14 +## Documentation
15 +
16 +See the official API documentation for more information.
17 +
18 +- English: https://developers.line.biz/en/docs/messaging-api/overview/
19 +- Japanese: https://developers.line.biz/ja/docs/messaging-api/overview/
20 +
21 +
22 +## Requirements
23 +
24 +This library requires Java 8 or later.
25 +
26 +
27 +## Installation
28 +
29 +We've uploaded this library to the Maven Central Repository. You can install the modules using Maven or Gradle.
30 +
31 +http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.linecorp.bot%22
32 +
33 +
34 +## Modules
35 +
36 +This project contains the following modules:
37 +
38 + * line-bot-api-client: API client library for the Messaging API
39 + * line-bot-model: Model classes for the Messaging API
40 + * line-bot-servlet: Java servlet utilities for bot servers
41 + * line-bot-spring-boot: Spring Boot auto configuration library for bot servers
42 +
43 +This project contains the following sample projects:
44 +
45 + * sample-spring-boot-echo: A simple echo server. It includes a Heroku button.
46 + * sample-spring-boot-kitchensink: Full featured sample code.
47 +
48 +
49 +## Spring Boot integration
50 +
51 +The line-bot-spring-boot module lets you build a bot application as a Spring Boot application.
52 +
53 +```java
54 +/*
55 + * Copyright 2016 LINE Corporation
56 + *
57 + * LINE Corporation licenses this file to you under the Apache License,
58 + * version 2.0 (the "License"); you may not use this file except in compliance
59 + * with the License. You may obtain a copy of the License at:
60 + *
61 + * http://www.apache.org/licenses/LICENSE-2.0
62 + *
63 + * Unless required by applicable law or agreed to in writing, software
64 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
65 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
66 + * License for the specific language governing permissions and limitations
67 + * under the License.
68 + */
69 +
70 +package com.example.bot.spring.echo;
71 +
72 +import org.springframework.boot.SpringApplication;
73 +import org.springframework.boot.autoconfigure.SpringBootApplication;
74 +
75 +import com.linecorp.bot.model.event.Event;
76 +import com.linecorp.bot.model.event.MessageEvent;
77 +import com.linecorp.bot.model.event.message.TextMessageContent;
78 +import com.linecorp.bot.model.message.TextMessage;
79 +import com.linecorp.bot.spring.boot.annotation.EventMapping;
80 +import com.linecorp.bot.spring.boot.annotation.LineMessageHandler;
81 +
82 +@SpringBootApplication
83 +@LineMessageHandler
84 +public class EchoApplication {
85 + public static void main(String[] args) {
86 + SpringApplication.run(EchoApplication.class, args);
87 + }
88 +
89 + @EventMapping
90 + public TextMessage handleTextMessageEvent(MessageEvent<TextMessageContent> event) {
91 + System.out.println("event: " + event);
92 + return new TextMessage(event.getMessage().getText());
93 + }
94 +
95 + @EventMapping
96 + public void handleDefaultMessageEvent(Event event) {
97 + System.out.println("event: " + event);
98 + }
99 +}
100 +```
101 +
102 +## How do I use a proxy server?
103 +
104 +You can use `LineMessagingServiceBuilder` to configure a proxy server. It accepts your own OkHttpBuilder instance.
105 +
106 +Note: You don't need to use an add-on like Fixie to have static IP addresses for proxy servers. You can make API calls without entering IP addresses on the server IP whitelist.
107 +
108 +
109 +## Help and media
110 +FAQ: https://developers.line.biz/en/faq/
111 +
112 +Community Q&A: https://www.line-community.me/questions
113 +
114 +News: https://developers.line.biz/en/news/
115 +
116 +Twitter: [@LINE_DEV](https://twitter.com/LINE_DEV)
117 +
118 +
119 +## Versioning
120 +
121 +This project respects semantic versioning.
122 +
123 +See http://semver.org/.
124 +
125 +
126 +## Contributing
127 +
128 +Please check [CONTRIBUTING](CONTRIBUTING.md) before making a contribution.
129 +
130 +
131 +## License
132 +
133 + Copyright (C) 2016 LINE Corp.
134 +
135 + Licensed under the Apache License, Version 2.0 (the "License");
136 + you may not use this file except in compliance with the License.
137 + You may obtain a copy of the License at
138 +
139 + http://www.apache.org/licenses/LICENSE-2.0
140 +
141 + Unless required by applicable law or agreed to in writing, software
142 + distributed under the License is distributed on an "AS IS" BASIS,
143 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
144 + See the License for the specific language governing permissions and
145 + limitations under the License.
1 +{
2 + "name": "echo bot - LINE Messaging API",
3 + "description": "This is a sample application for the LINE Messaging API",
4 + "keywords": ["chatbot", "line", "java"],
5 + "env": {
6 + "LINE_BOT_CHANNEL_TOKEN": {
7 + "description": "LINE bot Channel access token from the Channel Console",
8 + "required": true
9 + },
10 + "LINE_BOT_CHANNEL_SECRET": {
11 + "description": "LINE bot Channel secret from the Channel Console",
12 + "required": true
13 + }
14 + }
15 +}
...\ No newline at end of file ...\ No newline at end of file
This diff is collapsed. Click to expand it.
1 +<?xml version="1.0"?>
2 +<!DOCTYPE suppressions PUBLIC
3 + "-//Puppy Crawl//DTD Suppressions 1.1//EN"
4 + "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
5 +
6 +<suppressions>
7 + <!-- Suppress Javadoc-related checks in test directories -->
8 + <suppress checks="JavadocPackage" files="[\\/](jmh|test|internal|example)[\\/]" />
9 + <suppress checks="JavadocMethod" files="[\\/](jmh|test|internal|example)[\\/]" />
10 + <suppress checks="VisibilityModifier" files="[\\/](jmh|test|internal|example)[\\/]" />
11 +
12 + <!-- Suppress all checks in generated sources -->
13 + <suppress checks=".*" files="[\\/]gen-src[\\/]" />
14 +</suppressions>
This diff is collapsed. Click to expand it.
1 +<FindBugsFilter>
2 + <Match>
3 + <Bug pattern="OBL_UNSATISFIED_OBLIGATION" />
4 + </Match>
5 +
6 + <!-- False positive with Java 11 https://github.com/spotbugs/spotbugs/issues/878 -->
7 + <Match>
8 + <Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE" />
9 + </Match>
10 +
11 + <!-- Ignore lombok's redundant check -->
12 + <Match>
13 + <Class name="com.linecorp.bot.client.IntegrationTestSettings" />
14 + <Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" />
15 + </Match>
16 +</FindBugsFilter>
1 +org.gradle.warning.mode=all
1 +distributionBase=GRADLE_USER_HOME
2 +distributionPath=wrapper/dists
3 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-bin.zip
4 +zipStoreBase=GRADLE_USER_HOME
5 +zipStorePath=wrapper/dists
1 +#!/usr/bin/env sh
2 +
3 +#
4 +# Copyright 2015 the original author or authors.
5 +#
6 +# Licensed under the Apache License, Version 2.0 (the "License");
7 +# you may not use this file except in compliance with the License.
8 +# You may obtain a copy of the License at
9 +#
10 +# https://www.apache.org/licenses/LICENSE-2.0
11 +#
12 +# Unless required by applicable law or agreed to in writing, software
13 +# distributed under the License is distributed on an "AS IS" BASIS,
14 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 +# See the License for the specific language governing permissions and
16 +# limitations under the License.
17 +#
18 +
19 +##############################################################################
20 +##
21 +## Gradle start up script for UN*X
22 +##
23 +##############################################################################
24 +
25 +# Attempt to set APP_HOME
26 +# Resolve links: $0 may be a link
27 +PRG="$0"
28 +# Need this for relative symlinks.
29 +while [ -h "$PRG" ] ; do
30 + ls=`ls -ld "$PRG"`
31 + link=`expr "$ls" : '.*-> \(.*\)$'`
32 + if expr "$link" : '/.*' > /dev/null; then
33 + PRG="$link"
34 + else
35 + PRG=`dirname "$PRG"`"/$link"
36 + fi
37 +done
38 +SAVED="`pwd`"
39 +cd "`dirname \"$PRG\"`/" >/dev/null
40 +APP_HOME="`pwd -P`"
41 +cd "$SAVED" >/dev/null
42 +
43 +APP_NAME="Gradle"
44 +APP_BASE_NAME=`basename "$0"`
45 +
46 +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 +
49 +# Use the maximum available, or set MAX_FD != -1 to use that value.
50 +MAX_FD="maximum"
51 +
52 +warn () {
53 + echo "$*"
54 +}
55 +
56 +die () {
57 + echo
58 + echo "$*"
59 + echo
60 + exit 1
61 +}
62 +
63 +# OS specific support (must be 'true' or 'false').
64 +cygwin=false
65 +msys=false
66 +darwin=false
67 +nonstop=false
68 +case "`uname`" in
69 + CYGWIN* )
70 + cygwin=true
71 + ;;
72 + Darwin* )
73 + darwin=true
74 + ;;
75 + MINGW* )
76 + msys=true
77 + ;;
78 + NONSTOP* )
79 + nonstop=true
80 + ;;
81 +esac
82 +
83 +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 +
85 +
86 +# Determine the Java command to use to start the JVM.
87 +if [ -n "$JAVA_HOME" ] ; then
88 + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 + # IBM's JDK on AIX uses strange locations for the executables
90 + JAVACMD="$JAVA_HOME/jre/sh/java"
91 + else
92 + JAVACMD="$JAVA_HOME/bin/java"
93 + fi
94 + if [ ! -x "$JAVACMD" ] ; then
95 + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 +
97 +Please set the JAVA_HOME variable in your environment to match the
98 +location of your Java installation."
99 + fi
100 +else
101 + JAVACMD="java"
102 + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 +
104 +Please set the JAVA_HOME variable in your environment to match the
105 +location of your Java installation."
106 +fi
107 +
108 +# Increase the maximum file descriptors if we can.
109 +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 + MAX_FD_LIMIT=`ulimit -H -n`
111 + if [ $? -eq 0 ] ; then
112 + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 + MAX_FD="$MAX_FD_LIMIT"
114 + fi
115 + ulimit -n $MAX_FD
116 + if [ $? -ne 0 ] ; then
117 + warn "Could not set maximum file descriptor limit: $MAX_FD"
118 + fi
119 + else
120 + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 + fi
122 +fi
123 +
124 +# For Darwin, add options to specify how the application appears in the dock
125 +if $darwin; then
126 + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 +fi
128 +
129 +# For Cygwin or MSYS, switch paths to Windows format before running java
130 +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 + APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 +
134 + JAVACMD=`cygpath --unix "$JAVACMD"`
135 +
136 + # We build the pattern for arguments to be converted via cygpath
137 + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 + SEP=""
139 + for dir in $ROOTDIRSRAW ; do
140 + ROOTDIRS="$ROOTDIRS$SEP$dir"
141 + SEP="|"
142 + done
143 + OURCYGPATTERN="(^($ROOTDIRS))"
144 + # Add a user-defined pattern to the cygpath arguments
145 + if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 + fi
148 + # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 + i=0
150 + for arg in "$@" ; do
151 + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 +
154 + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 + else
157 + eval `echo args$i`="\"$arg\""
158 + fi
159 + i=`expr $i + 1`
160 + done
161 + case $i in
162 + 0) set -- ;;
163 + 1) set -- "$args0" ;;
164 + 2) set -- "$args0" "$args1" ;;
165 + 3) set -- "$args0" "$args1" "$args2" ;;
166 + 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 + esac
173 +fi
174 +
175 +# Escape application args
176 +save () {
177 + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 + echo " "
179 +}
180 +APP_ARGS=`save "$@"`
181 +
182 +# Collect all arguments for the java command, following the shell quoting and substitution rules
183 +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 +
185 +exec "$JAVACMD" "$@"
1 +@rem
2 +@rem Copyright 2015 the original author or authors.
3 +@rem
4 +@rem Licensed under the Apache License, Version 2.0 (the "License");
5 +@rem you may not use this file except in compliance with the License.
6 +@rem You may obtain a copy of the License at
7 +@rem
8 +@rem https://www.apache.org/licenses/LICENSE-2.0
9 +@rem
10 +@rem Unless required by applicable law or agreed to in writing, software
11 +@rem distributed under the License is distributed on an "AS IS" BASIS,
12 +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 +@rem See the License for the specific language governing permissions and
14 +@rem limitations under the License.
15 +@rem
16 +
17 +@if "%DEBUG%" == "" @echo off
18 +@rem ##########################################################################
19 +@rem
20 +@rem Gradle startup script for Windows
21 +@rem
22 +@rem ##########################################################################
23 +
24 +@rem Set local scope for the variables with windows NT shell
25 +if "%OS%"=="Windows_NT" setlocal
26 +
27 +set DIRNAME=%~dp0
28 +if "%DIRNAME%" == "" set DIRNAME=.
29 +set APP_BASE_NAME=%~n0
30 +set APP_HOME=%DIRNAME%
31 +
32 +@rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 +
35 +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 +
38 +@rem Find java.exe
39 +if defined JAVA_HOME goto findJavaFromJavaHome
40 +
41 +set JAVA_EXE=java.exe
42 +%JAVA_EXE% -version >NUL 2>&1
43 +if "%ERRORLEVEL%" == "0" goto init
44 +
45 +echo.
46 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 +echo.
48 +echo Please set the JAVA_HOME variable in your environment to match the
49 +echo location of your Java installation.
50 +
51 +goto fail
52 +
53 +:findJavaFromJavaHome
54 +set JAVA_HOME=%JAVA_HOME:"=%
55 +set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 +
57 +if exist "%JAVA_EXE%" goto init
58 +
59 +echo.
60 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 +echo.
62 +echo Please set the JAVA_HOME variable in your environment to match the
63 +echo location of your Java installation.
64 +
65 +goto fail
66 +
67 +:init
68 +@rem Get command-line arguments, handling Windows variants
69 +
70 +if not "%OS%" == "Windows_NT" goto win9xME_args
71 +
72 +:win9xME_args
73 +@rem Slurp the command line arguments.
74 +set CMD_LINE_ARGS=
75 +set _SKIP=2
76 +
77 +:win9xME_args_slurp
78 +if "x%~1" == "x" goto execute
79 +
80 +set CMD_LINE_ARGS=%*
81 +
82 +:execute
83 +@rem Setup the command line
84 +
85 +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
86 +
87 +
88 +@rem Execute Gradle
89 +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
90 +
91 +:end
92 +@rem End local scope for the variables with windows NT shell
93 +if "%ERRORLEVEL%"=="0" goto mainEnd
94 +
95 +:fail
96 +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
97 +rem the _cmd.exe /c_ return code!
98 +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
99 +exit /b 1
100 +
101 +:mainEnd
102 +if "%OS%"=="Windows_NT" endlocal
103 +
104 +:omega
1 +# line-bot-api-client
2 +
3 +LINE Messaging API client for Java 8 or later.
4 +
5 +## Synopsis
6 +
7 +```java
8 +LineMessagingClient client = LineMessagingClient.builder("YOUR_CHANNEL_TOKEN").build();
9 +```
10 +
11 +## Description
12 +
13 +This module provides an API client for the LINE Messaging API.
14 +
15 +## Integration test
16 +
17 +By default, the integration test suite does nothing. You need to put the configuration file to run the integration test.
18 +If you want to run this test suite, put `src/test/resources/integration_test_settings.yml`.
19 +
20 +The YAML file is mapped to `com.linecorp.bot.client.IntegrationTestSettings`.
21 +
22 +(It's bit hard to run all test cases since some test cases depend on the number of the targeted reaches).
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +// https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests
18 +sourceSets {
19 + integrationTest {
20 + compileClasspath += sourceSets.main.output
21 + runtimeClasspath += sourceSets.main.output
22 + }
23 +}
24 +
25 +configurations {
26 + integrationTestImplementation.extendsFrom implementation
27 + integrationTestRuntimeOnly.extendsFrom runtimeOnly
28 + integrationTestCompileOnly.extendsFrom compileOnly
29 + integrationTestAnnotationProcessor.extendsFrom annotationProcessor
30 +}
31 +
32 +task integrationTest(type: Test) {
33 + description = 'Runs integration tests.'
34 + group = 'verification'
35 +
36 + testClassesDirs = sourceSets.integrationTest.output.classesDirs
37 + classpath = sourceSets.integrationTest.runtimeClasspath
38 + shouldRunAfter test
39 +}
40 +
41 +check.dependsOn integrationTest
42 +
43 +dependencies {
44 + api project(':line-bot-model')
45 + implementation 'org.slf4j:slf4j-api'
46 + implementation 'com.squareup.okhttp3:logging-interceptor'
47 + implementation 'com.squareup.retrofit2:converter-jackson'
48 + implementation 'com.squareup.retrofit2:retrofit'
49 +
50 + testCompileOnly 'org.projectlombok:lombok'
51 + testAnnotationProcessor 'org.projectlombok:lombok'
52 +
53 + integrationTestCompileOnly 'org.projectlombok:lombok'
54 + integrationTestAnnotationProcessor 'org.projectlombok:lombok'
55 + integrationTestImplementation 'com.google.guava:guava'
56 + integrationTestImplementation 'io.jsonwebtoken:jjwt-api'
57 + integrationTestImplementation 'io.jsonwebtoken:jjwt-jackson'
58 + integrationTestImplementation 'org.springframework.boot:spring-boot-starter-test'
59 + integrationTestImplementation 'org.springframework.boot:spring-boot-starter-logging'
60 + integrationTestImplementation 'com.fasterxml.jackson.core:jackson-core'
61 + integrationTestImplementation 'com.fasterxml.jackson.core:jackson-databind'
62 + integrationTestImplementation 'com.fasterxml.jackson.core:jackson-annotations'
63 + integrationTestImplementation 'com.fasterxml.jackson.module:jackson-module-parameter-names'
64 + integrationTestImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml'
65 + integrationTestRuntime 'io.jsonwebtoken:jjwt-impl'
66 +}
1 +/*
2 + * Copyright 2020 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.io.IOException;
20 +import java.util.Collections;
21 +
22 +import org.junit.Before;
23 +import org.junit.Test;
24 +
25 +import com.linecorp.bot.model.Narrowcast;
26 +import com.linecorp.bot.model.message.TextMessage;
27 +import com.linecorp.bot.model.narrowcast.Filter;
28 +import com.linecorp.bot.model.narrowcast.filter.GenderDemographicFilter;
29 +import com.linecorp.bot.model.narrowcast.filter.GenderDemographicFilter.Gender;
30 +import com.linecorp.bot.model.response.BotApiResponse;
31 +import com.linecorp.bot.model.response.GetMessageEventResponse;
32 +import com.linecorp.bot.model.response.NarrowcastProgressResponse;
33 +import com.linecorp.bot.model.response.NarrowcastProgressResponse.Phase;
34 +
35 +import lombok.extern.slf4j.Slf4j;
36 +
37 +@Slf4j
38 +public class InsightIntegrationTest {
39 + private LineMessagingClient target;
40 +
41 + @Before
42 + public void setUp() throws IOException {
43 + IntegrationTestSettings settings = IntegrationTestSettingsLoader.load();
44 + target = LineMessagingClientFactory.create(settings);
45 + }
46 +
47 + @Test
48 + public void testGetMessageEvent() throws Exception {
49 + // Send narrowcast message.
50 + BotApiResponse response = target.narrowcast(
51 + new Narrowcast(new TextMessage("Narrowcast test(gender=male)"),
52 + Filter.builder()
53 + .demographic(
54 + GenderDemographicFilter
55 + .builder()
56 + .oneOf(Collections.singletonList(Gender.MALE))
57 + .build()
58 + ).build())).get();
59 + log.info("Narrowcast response={}", response);
60 +
61 + // Waiting sending process
62 + for (int i = 0; i < 10; i++) {
63 + NarrowcastProgressResponse progressResponse = target.getNarrowcastProgress(
64 + response.getRequestId()).get();
65 + log.info("Progress={}", progressResponse);
66 + log.info("Progress response={}", progressResponse);
67 + if (progressResponse.getPhase() == Phase.SUCCEEDED
68 + || progressResponse.getPhase() == Phase.FAILED) {
69 + break;
70 + }
71 + Thread.sleep(1000);
72 + }
73 +
74 + GetMessageEventResponse messageEvent = target.getMessageEvent(
75 + response.getRequestId()).get();
76 + log.info("messageEvent={}", messageEvent);
77 + }
78 +}
1 +/*
2 + * Copyright 2020 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.util.List;
20 +
21 +import org.junit.Assume;
22 +
23 +import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
24 +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
25 +
26 +import com.linecorp.bot.client.IntegrationTestSettings.IntegrationTestSettingsBuilder;
27 +
28 +import lombok.AllArgsConstructor;
29 +import lombok.Builder;
30 +import lombok.Value;
31 +
32 +@AllArgsConstructor
33 +@Value
34 +@Builder
35 +@JsonDeserialize(builder = IntegrationTestSettingsBuilder.class)
36 +public class IntegrationTestSettings {
37 + private String token;
38 + private String endpoint;
39 + private String userId;
40 + private List<String> audienceIfas;
41 + private String retargetingRequestId;
42 + private boolean failOnUnknownProperties = true;
43 +
44 + public String getUserId() {
45 + Assume.assumeNotNull(userId);
46 + return userId;
47 + }
48 +
49 + public List<String> getAudienceIfas() {
50 + Assume.assumeNotNull(audienceIfas);
51 + Assume.assumeFalse(audienceIfas.isEmpty());
52 + return audienceIfas;
53 + }
54 +
55 + public String getRetargetingRequestId() {
56 + Assume.assumeNotNull(retargetingRequestId);
57 + return retargetingRequestId;
58 + }
59 +
60 + @JsonPOJOBuilder(withPrefix = "")
61 + public static class IntegrationTestSettingsBuilder {
62 + // filled by lombok.
63 + }
64 +}
1 +/*
2 + * Copyright 2020 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.io.IOException;
20 +import java.net.URL;
21 +
22 +import org.junit.Assume;
23 +
24 +import com.fasterxml.jackson.databind.ObjectMapper;
25 +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
26 +import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
27 +
28 +public class IntegrationTestSettingsLoader {
29 + private static final URL TEST_RESOURCE = ClassLoader.getSystemResource("integration_test_settings.yml");
30 +
31 + public static IntegrationTestSettings load() throws IOException {
32 + // Do not run all test cases in this class when src/test/resources/integration_test_settings.yml doesn't
33 + // exist.
34 + Assume.assumeTrue("exists integration_test_settings.yml in resource directory",
35 + TEST_RESOURCE != null);
36 +
37 + return new ObjectMapper(new YAMLFactory())
38 + .registerModule(new ParameterNamesModule())
39 + .readValue(TEST_RESOURCE, IntegrationTestSettings.class);
40 + }
41 +}
1 +/*
2 + * Copyright 2020 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.net.URI;
20 +
21 +import com.fasterxml.jackson.databind.DeserializationFeature;
22 +import com.fasterxml.jackson.databind.ObjectMapper;
23 +
24 +import com.linecorp.bot.model.objectmapper.ModelObjectMapper;
25 +
26 +import retrofit2.Retrofit;
27 +import retrofit2.converter.jackson.JacksonConverterFactory;
28 +
29 +public class LineMessagingClientFactory {
30 + public static LineMessagingClient create(IntegrationTestSettings settings) {
31 + ObjectMapper objectMapper = ModelObjectMapper
32 + .createNewObjectMapper()
33 + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
34 + settings.isFailOnUnknownProperties());
35 +
36 + Retrofit.Builder retrofitBuilder = new Retrofit.Builder()
37 + .addConverterFactory(JacksonConverterFactory.create(objectMapper));
38 +
39 + return LineMessagingClient
40 + .builder(settings.getToken())
41 + .apiEndPoint(URI.create(settings.getEndpoint()))
42 + .retrofitBuilder(retrofitBuilder)
43 + .build();
44 + }
45 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static java.util.Collections.singleton;
20 +
21 +import java.io.IOException;
22 +import java.util.concurrent.Callable;
23 +
24 +import org.junit.Before;
25 +import org.junit.Test;
26 +
27 +import com.linecorp.bot.model.Broadcast;
28 +import com.linecorp.bot.model.Multicast;
29 +import com.linecorp.bot.model.PushMessage;
30 +import com.linecorp.bot.model.message.TextMessage;
31 +import com.linecorp.bot.model.response.GetNumberOfFollowersResponse;
32 +import com.linecorp.bot.model.response.GetNumberOfMessageDeliveriesResponse;
33 +import com.linecorp.bot.model.response.NumberOfMessagesResponse;
34 +
35 +import lombok.extern.slf4j.Slf4j;
36 +
37 +/**
38 + * Integration test of {@link LineMessagingClient}.
39 + *
40 + * <p>To run this test, please put config file resources/integration_test_settings.yml.
41 + */
42 +@Slf4j
43 +public class LineMessagingClientImplIntegrationTest {
44 + private LineMessagingClient target;
45 + private IntegrationTestSettings settings;
46 +
47 + @Before
48 + public void setUp() throws IOException {
49 + settings = IntegrationTestSettingsLoader.load();
50 + target = LineMessagingClientFactory.create(settings);
51 + }
52 +
53 + private static void testApiCall(Callable<Object> f) throws Exception {
54 + final Object response = f.call();
55 + log.info(response.toString());
56 + }
57 +
58 + @Test
59 + public void broadcast() throws Exception {
60 + testApiCall(
61 + () -> target.broadcast(new Broadcast(new TextMessage("Broadcast"), true)).get()
62 + );
63 + testApiCall(
64 + () -> target.broadcast(new Broadcast(new TextMessage("Broadcast"))).get()
65 + );
66 + }
67 +
68 + @Test
69 + public void multicast() throws Exception {
70 + testApiCall(
71 + () -> target.multicast(
72 + new Multicast(singleton(settings.getUserId()), new TextMessage("Multicast"), true))
73 + .get()
74 + );
75 + testApiCall(
76 + () -> target
77 + .multicast(new Multicast(singleton(settings.getUserId()), new TextMessage("Multicast")))
78 + .get()
79 + );
80 + }
81 +
82 + @Test
83 + public void pushMessage() throws Exception {
84 + testApiCall(
85 + () -> target.pushMessage(new PushMessage(settings.getUserId(), new TextMessage("Push"), true))
86 + .get()
87 + );
88 + testApiCall(
89 + () -> target.pushMessage(new PushMessage(settings.getUserId(), new TextMessage("Push"))).get()
90 + );
91 + }
92 +
93 + @Test
94 + public void getNumberOfMessageDeliveries() throws Exception {
95 + final GetNumberOfMessageDeliveriesResponse getNumberOfMessageDeliveriesResponse =
96 + target.getNumberOfMessageDeliveries("20191231").get();
97 +
98 + log.info(getNumberOfMessageDeliveriesResponse.toString());
99 + }
100 +
101 + @Test
102 + public void getNumberOfSentBroadcastMessages() throws Exception {
103 + final NumberOfMessagesResponse getNumberOfSentBroadcastMessages =
104 + target.getNumberOfSentBroadcastMessages("20191231").get();
105 +
106 + log.info(getNumberOfSentBroadcastMessages.toString());
107 + }
108 +
109 + @Test
110 + public void getNumberOfFollowers() throws Exception {
111 + final GetNumberOfFollowersResponse getNumberOfFollowersResponse =
112 + target.getNumberOfFollowers("20191231").get();
113 +
114 + log.info(getNumberOfFollowersResponse.toString());
115 + }
116 +}
1 +/*
2 + * Copyright 2020 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static org.assertj.core.api.Assertions.assertThat;
20 +
21 +import java.io.IOException;
22 +import java.net.URL;
23 +import java.security.KeyFactory;
24 +import java.security.PrivateKey;
25 +import java.security.spec.PKCS8EncodedKeySpec;
26 +import java.time.Duration;
27 +import java.time.Instant;
28 +import java.util.Map;
29 +
30 +import org.junit.Assume;
31 +import org.junit.Before;
32 +import org.junit.Test;
33 +import org.slf4j.bridge.SLF4JBridgeHandler;
34 +import org.yaml.snakeyaml.Yaml;
35 +
36 +import com.fasterxml.jackson.databind.ObjectMapper;
37 +import com.google.common.collect.ImmutableMap;
38 +import com.google.common.io.BaseEncoding;
39 +
40 +import com.linecorp.bot.model.oauth.IssueChannelAccessTokenResponse;
41 +
42 +import io.jsonwebtoken.Jwts;
43 +import io.jsonwebtoken.SignatureAlgorithm;
44 +import io.jsonwebtoken.jackson.io.JacksonSerializer;
45 +import lombok.extern.slf4j.Slf4j;
46 +
47 +@Slf4j
48 +public class LineOAuthClientIntegrationTest {
49 + private static final URL TEST_RESOURCE = ClassLoader.getSystemResource("integration_test_settings.yml");
50 +
51 + private LineOAuthClient target;
52 + private String endpoint;
53 + private String pemPrivateKey;
54 + private String channelId;
55 + private String channelSecret;
56 + private String kid;
57 +
58 + @Before
59 + public void setUp() throws IOException {
60 + Assume.assumeTrue(TEST_RESOURCE != null);
61 +
62 + final Map<?, ?> map = new ObjectMapper()
63 + .convertValue(new Yaml().load(TEST_RESOURCE.openStream()), Map.class);
64 +
65 + endpoint = (String) map.get("endpoint");
66 + target = LineOAuthClient
67 + .builder()
68 + .apiEndPoint(endpoint)
69 + .build();
70 +
71 + pemPrivateKey = ((String) map.get("pemPrivateKey")).replaceAll("\n", "");
72 + kid = (String) map.get("kid");
73 + channelId = String.valueOf(map.get("channelId"));
74 + channelSecret = (String) map.get("channelSecret");
75 + }
76 +
77 + static {
78 + SLF4JBridgeHandler.removeHandlersForRootLogger();
79 + SLF4JBridgeHandler.install();
80 + }
81 +
82 + @Test
83 + public void gwtTokenIntegrationTest() throws Exception {
84 + final Map<String, Object> header = ImmutableMap.of(
85 + "alg", "RS256",
86 + "typ", "JWT",
87 + "kid", kid
88 + );
89 +
90 + final Map<String, Object> body = ImmutableMap.of(
91 + "iss", channelId,
92 + "sub", channelId,
93 + "aud", endpoint,
94 + "exp", Instant.now().plusSeconds(10).getEpochSecond(),
95 + "token_exp", Duration.ofMinutes(1).getSeconds()
96 + );
97 +
98 + byte[] bytes = BaseEncoding.base64().decode(pemPrivateKey);
99 +
100 + KeyFactory kf = KeyFactory.getInstance("RSA");
101 + PrivateKey privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(bytes));
102 +
103 + String jws = Jwts.builder()
104 + .serializeToJsonWith(new JacksonSerializer(new ObjectMapper()))
105 + .setHeader(header)
106 + .setClaims(body)
107 + .signWith(privateKey, SignatureAlgorithm.RS256)
108 + .compact();
109 +
110 + log.info("{}", jws);
111 +
112 + // Issue
113 + IssueChannelAccessTokenResponse issueChannelAccessTokenResponse =
114 + target.issueChannelTokenByJWT(jws).get();
115 +
116 + log.info("{}", issueChannelAccessTokenResponse);
117 + assertThat(issueChannelAccessTokenResponse.getExpiresInSecs()).isEqualTo(60);
118 +
119 + // Revoke
120 + target.revokeChannelTokenByJWT(
121 + channelId,
122 + channelSecret,
123 + issueChannelAccessTokenResponse.getAccessToken())
124 + .get();
125 + }
126 +}
1 +/*
2 + * Copyright 2020 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.net.URI;
20 +
21 +import com.fasterxml.jackson.databind.DeserializationFeature;
22 +import com.fasterxml.jackson.databind.ObjectMapper;
23 +
24 +import com.linecorp.bot.model.objectmapper.ModelObjectMapper;
25 +
26 +import retrofit2.Retrofit;
27 +import retrofit2.converter.jackson.JacksonConverterFactory;
28 +
29 +public class ManageAudienceClientFactory {
30 + public static ManageAudienceClient create(IntegrationTestSettings settings) {
31 + ObjectMapper objectMapper = ModelObjectMapper
32 + .createNewObjectMapper()
33 + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
34 + settings.isFailOnUnknownProperties());
35 +
36 + Retrofit.Builder retrofitBuilder = new Retrofit.Builder()
37 + .addConverterFactory(JacksonConverterFactory.create(objectMapper));
38 +
39 + return ManageAudienceClient
40 + .builder()
41 + .channelToken(settings.getToken())
42 + .apiEndPoint(URI.create(settings.getEndpoint()))
43 + .retrofitBuilder(retrofitBuilder)
44 + .build();
45 + }
46 +}
1 +/*
2 + * Copyright 2020 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.io.IOException;
20 +import java.util.List;
21 +import java.util.UUID;
22 +import java.util.concurrent.ExecutionException;
23 +import java.util.stream.Collectors;
24 +
25 +import org.junit.Assert;
26 +import org.junit.Before;
27 +import org.junit.Test;
28 +
29 +import com.linecorp.bot.model.manageaudience.AudienceGroup;
30 +import com.linecorp.bot.model.manageaudience.AudienceGroupAuthorityLevel;
31 +import com.linecorp.bot.model.manageaudience.AudienceGroupCreateRoute;
32 +import com.linecorp.bot.model.manageaudience.request.AddAudienceToAudienceGroupRequest;
33 +import com.linecorp.bot.model.manageaudience.request.Audience;
34 +import com.linecorp.bot.model.manageaudience.request.CreateAudienceGroupRequest;
35 +import com.linecorp.bot.model.manageaudience.request.CreateClickBasedAudienceGroupRequest;
36 +import com.linecorp.bot.model.manageaudience.request.CreateImpBasedAudienceGroupRequest;
37 +import com.linecorp.bot.model.manageaudience.request.UpdateAudienceGroupAuthorityLevelRequest;
38 +import com.linecorp.bot.model.manageaudience.request.UpdateAudienceGroupDescriptionRequest;
39 +import com.linecorp.bot.model.manageaudience.response.CreateAudienceGroupResponse;
40 +import com.linecorp.bot.model.manageaudience.response.CreateClickBasedAudienceGroupResponse;
41 +import com.linecorp.bot.model.manageaudience.response.CreateImpBasedAudienceGroupResponse;
42 +import com.linecorp.bot.model.manageaudience.response.GetAudienceDataResponse;
43 +import com.linecorp.bot.model.manageaudience.response.GetAudienceGroupAuthorityLevelResponse;
44 +import com.linecorp.bot.model.manageaudience.response.GetAudienceGroupsResponse;
45 +import com.linecorp.bot.model.response.BotApiResponse;
46 +
47 +import lombok.extern.slf4j.Slf4j;
48 +
49 +@Slf4j
50 +public class ManageAudienceIntegrationTest {
51 + private ManageAudienceClient target;
52 + private IntegrationTestSettings settings;
53 +
54 + @Before
55 + public void setUp() throws IOException {
56 + settings = IntegrationTestSettingsLoader.load();
57 + target = ManageAudienceClientFactory.create(settings);
58 + }
59 +
60 + @Test
61 + public void createAudienceGroup() throws Exception {
62 + CreateAudienceGroupResponse createResponse = target
63 + .createAudienceGroup(CreateAudienceGroupRequest
64 + .builder()
65 + .description("test" + UUID.randomUUID())
66 + .isIfaAudience(true)
67 + .uploadDescription("test")
68 + .audiences(
69 + settings.getAudienceIfas().stream()
70 + .map(Audience::new)
71 + .collect(Collectors.toList())
72 + ).build()
73 + ).get();
74 + log.info(createResponse.toString());
75 +
76 + long audienceGroupId = createResponse.getAudienceGroupId();
77 +
78 + BotApiResponse addResponse = target
79 + .addAudienceToAudienceGroup(
80 + AddAudienceToAudienceGroupRequest
81 + .builder()
82 + .audienceGroupId(audienceGroupId)
83 + .audiences(settings.getAudienceIfas().stream()
84 + .map(Audience::new)
85 + .collect(Collectors
86 + .toList()))
87 + .build()
88 + )
89 + .get();
90 + log.info(addResponse.toString());
91 +
92 + BotApiResponse updateResponse = target.updateAudienceGroupDescription(
93 + audienceGroupId,
94 + UpdateAudienceGroupDescriptionRequest
95 + .builder()
96 + .description("Hello" + UUID.randomUUID())
97 + .build()
98 + ).get();
99 + log.info(updateResponse.toString());
100 +
101 + BotApiResponse deleteResponse = target.deleteAudienceGroup(audienceGroupId).get();
102 + log.info(deleteResponse.toString());
103 + }
104 +
105 + @Test
106 + public void createClickBasedAudienceGroup() throws Exception {
107 + CreateClickBasedAudienceGroupResponse response = target
108 + .createClickBasedAudienceGroup(CreateClickBasedAudienceGroupRequest
109 + .builder()
110 + .description("test " + UUID.randomUUID())
111 + .requestId(settings.getRetargetingRequestId())
112 + .build()
113 + )
114 + .get();
115 + log.info(response.toString());
116 + }
117 +
118 + @Test
119 + public void createImpBasedAudienceGroup() throws Exception {
120 + CreateImpBasedAudienceGroupResponse response = target
121 + .createImpBasedAudienceGroup(CreateImpBasedAudienceGroupRequest
122 + .builder()
123 + .description("test " + UUID.randomUUID())
124 + .requestId(settings.getRetargetingRequestId())
125 + .build()
126 + )
127 + .get();
128 + log.info(response.toString());
129 + }
130 +
131 + @Test
132 + public void getAudienceGroups() throws ExecutionException, InterruptedException {
133 + GetAudienceGroupsResponse response = target
134 + .getAudienceGroups(1L, null, null, 40L,
135 + false, AudienceGroupCreateRoute.OA_MANAGER)
136 + .get();
137 + Assert.assertEquals(1L, response.getPage().longValue());
138 + Assert.assertEquals(40L, response.getSize().longValue());
139 + Assert.assertNotNull(response.getTotalCount());
140 + log.info(response.toString());
141 +
142 + List<AudienceGroup> audienceGroups = response.getAudienceGroups();
143 + for (AudienceGroup audienceGroup : audienceGroups) {
144 + GetAudienceDataResponse dataResponse = target.getAudienceData(
145 + audienceGroup.getAudienceGroupId()).get();
146 + Assert.assertNotNull(dataResponse.getAudienceGroup());
147 + Assert.assertEquals(audienceGroup.getAudienceGroupId(),
148 + dataResponse.getAudienceGroup().getAudienceGroupId());
149 + log.info("id={} data={}", audienceGroup.getAudienceGroupId(), dataResponse);
150 + }
151 + }
152 +
153 + @Test
154 + public void getAudienceGroupAuthorityLevel() throws ExecutionException, InterruptedException {
155 + GetAudienceGroupAuthorityLevelResponse response = target
156 + .getAudienceGroupAuthorityLevel()
157 + .get();
158 + log.info(response.toString());
159 +
160 + AudienceGroupAuthorityLevel origLevel = response.getAuthorityLevel();
161 + AudienceGroupAuthorityLevel inverted = origLevel == AudienceGroupAuthorityLevel.PRIVATE
162 + ? AudienceGroupAuthorityLevel.PUBLIC
163 + : AudienceGroupAuthorityLevel.PRIVATE;
164 +
165 + BotApiResponse invertResponse = target.updateAudienceGroupAuthorityLevel(
166 + UpdateAudienceGroupAuthorityLevelRequest
167 + .builder()
168 + .authorityLevel(inverted)
169 + .build()).get();
170 + log.info(invertResponse.toString());
171 +
172 + BotApiResponse revertResponse = target.updateAudienceGroupAuthorityLevel(
173 + UpdateAudienceGroupAuthorityLevelRequest
174 + .builder()
175 + .authorityLevel(origLevel)
176 + .build()).get();
177 + log.info(revertResponse.toString());
178 + }
179 +}
1 +/*
2 + * Copyright 2020 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.io.IOException;
20 +import java.util.Collections;
21 +
22 +import org.junit.Before;
23 +import org.junit.Test;
24 +
25 +import com.linecorp.bot.model.Narrowcast;
26 +import com.linecorp.bot.model.message.TextMessage;
27 +import com.linecorp.bot.model.narrowcast.Filter;
28 +import com.linecorp.bot.model.narrowcast.filter.AgeDemographicFilter;
29 +import com.linecorp.bot.model.narrowcast.filter.AgeDemographicFilter.Age;
30 +import com.linecorp.bot.model.narrowcast.filter.AppTypeDemographicFilter;
31 +import com.linecorp.bot.model.narrowcast.filter.AppTypeDemographicFilter.AppType;
32 +import com.linecorp.bot.model.narrowcast.filter.GenderDemographicFilter;
33 +import com.linecorp.bot.model.narrowcast.filter.GenderDemographicFilter.Gender;
34 +import com.linecorp.bot.model.response.BotApiResponse;
35 +import com.linecorp.bot.model.response.NarrowcastProgressResponse;
36 +import com.linecorp.bot.model.response.NarrowcastProgressResponse.Phase;
37 +
38 +import lombok.extern.slf4j.Slf4j;
39 +
40 +@Slf4j
41 +public class NarrowcastIntegrationTest {
42 + private LineMessagingClient target;
43 +
44 + @Before
45 + public void setUp() throws IOException {
46 + IntegrationTestSettings settings = IntegrationTestSettingsLoader.load();
47 + target = LineMessagingClientFactory.create(settings);
48 + }
49 +
50 + @Test
51 + public void narrowcastGender() throws Exception {
52 + testNarrowcast(new Narrowcast(new TextMessage("Narrowcast test(gender=male)"),
53 + Filter.builder()
54 + .demographic(
55 + GenderDemographicFilter
56 + .builder()
57 + .oneOf(Collections.singletonList(Gender.MALE))
58 + .build()
59 + ).build()));
60 + }
61 +
62 + @Test
63 + public void narrowcastAge() throws Exception {
64 + testNarrowcast(new Narrowcast(new TextMessage("Narrowcast test(Age)"),
65 + Filter.builder()
66 + .demographic(
67 + AgeDemographicFilter
68 + .builder()
69 + .gte(Age.AGE_15)
70 + .lt(Age.AGE_40)
71 + .build()
72 + ).build()));
73 + }
74 +
75 + @Test
76 + public void narrowcastAppType() throws Exception {
77 + testNarrowcast(new Narrowcast(new TextMessage("Narrowcast test(AppType)"),
78 + Filter.builder()
79 + .demographic(
80 + AppTypeDemographicFilter
81 + .builder()
82 + .oneOf(Collections.singletonList(AppType.IOS))
83 + .build()
84 + ).build()));
85 + }
86 +
87 + private void testNarrowcast(Narrowcast narrowcast) throws Exception {
88 + BotApiResponse response = target.narrowcast(narrowcast).get();
89 + log.info("Narrowcast response={}", response);
90 + for (int i = 0; i < 10; i++) {
91 + NarrowcastProgressResponse progressResponse = target.getNarrowcastProgress(
92 + response.getRequestId()).get();
93 + log.info("Progress={}", progressResponse);
94 + log.info("Progress response={}", progressResponse);
95 + if (progressResponse.getPhase() == Phase.SUCCEEDED
96 + || progressResponse.getPhase() == Phase.FAILED) {
97 + break;
98 + }
99 + Thread.sleep(1000);
100 + }
101 + }
102 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static java.util.Collections.emptyList;
20 +
21 +import java.util.List;
22 +
23 +import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
24 +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
25 +
26 +import com.linecorp.bot.client.BotApiResponseBody.BotApiResponseBodyBuilder;
27 +import com.linecorp.bot.model.response.BotApiResponse;
28 +
29 +import lombok.Builder;
30 +import lombok.Builder.Default;
31 +import lombok.Value;
32 +
33 +/**
34 + * Response body of BotApiResponse.
35 + *
36 + * <p>Developer will use {@link BotApiResponse} returned by {@link #withRequestId} method.
37 + *
38 + * @see BotApiResponse
39 + */
40 +@Value
41 +@Builder
42 +@JsonDeserialize(builder = BotApiResponseBodyBuilder.class)
43 +class BotApiResponseBody {
44 + @JsonPOJOBuilder(withPrefix = "")
45 + public static class BotApiResponseBodyBuilder {
46 + // filled by lombok.
47 + }
48 +
49 + String message;
50 + @Default
51 + List<String> details = emptyList();
52 +
53 + BotApiResponse withRequestId(final String requestId) {
54 + return new BotApiResponse(requestId, message, details);
55 + }
56 +}
1 +/*
2 + * Copyright 2018 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static com.linecorp.bot.client.LineMessagingClientBuilder.buildAuthenticationInterceptor;
20 +import static com.linecorp.bot.client.LineMessagingClientBuilder.buildLoggingInterceptor;
21 +import static com.linecorp.bot.client.LineMessagingClientBuilder.createDefaultRetrofitBuilder;
22 +
23 +import java.net.URI;
24 +
25 +import lombok.NonNull;
26 +import lombok.Setter;
27 +import lombok.experimental.Accessors;
28 +import okhttp3.OkHttpClient;
29 +import okhttp3.OkHttpClient.Builder;
30 +import retrofit2.Retrofit;
31 +
32 +@Setter
33 +@Accessors(fluent = true)
34 +public class ChannelManagementClientBuilder {
35 + private URI apiEndPoint = LineClientConstants.DEFAULT_API_END_POINT;
36 + private ChannelTokenSupplier channelTokenSupplier;
37 +
38 + /**
39 + * Create a new {@link ChannelManagementClientBuilder} with specified {@link ChannelTokenSupplier}.
40 + */
41 + public static ChannelManagementClientBuilder create(@NonNull ChannelTokenSupplier channelTokenSupplier) {
42 + return new ChannelManagementClientBuilder()
43 + .channelTokenSupplier(channelTokenSupplier);
44 + }
45 +
46 + /**
47 + * Build a new {@link ChannelManagementSyncClient}.
48 + */
49 + public ChannelManagementSyncClient build() {
50 + final Builder okHttpClientBuilder = new Builder();
51 +
52 + okHttpClientBuilder
53 + .addInterceptor(buildAuthenticationInterceptor(channelTokenSupplier))
54 + .addInterceptor(buildLoggingInterceptor());
55 +
56 + final OkHttpClient okHttpClient = okHttpClientBuilder.build();
57 +
58 + final Retrofit.Builder retrofitBuilder = createDefaultRetrofitBuilder();
59 +
60 + retrofitBuilder.client(okHttpClient);
61 + retrofitBuilder.baseUrl(apiEndPoint.toString());
62 + final Retrofit retrofit = retrofitBuilder.build();
63 +
64 + final ChannelManagementClientRetrofitIface retrofitIface =
65 + retrofit.create(ChannelManagementClientRetrofitIface.class);
66 + return ChannelManagementSyncClientImpl.of(retrofitIface);
67 + }
68 +}
1 +/*
2 + * Copyright 2018 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import com.linecorp.bot.liff.LiffView;
20 +import com.linecorp.bot.liff.request.LiffAppAddRequest;
21 +import com.linecorp.bot.liff.response.LiffAppAddResponse;
22 +import com.linecorp.bot.liff.response.LiffAppsResponse;
23 +
24 +import retrofit2.Call;
25 +import retrofit2.http.Body;
26 +import retrofit2.http.DELETE;
27 +import retrofit2.http.GET;
28 +import retrofit2.http.POST;
29 +import retrofit2.http.PUT;
30 +import retrofit2.http.Path;
31 +
32 +/**
33 + * Package private interface for Retrofit binding.
34 + *
35 + * <p>You can use LIFF API via {@link ChannelManagementSyncClient}.
36 + * It's independent from Retrofit implementation.
37 + */
38 +interface ChannelManagementClientRetrofitIface {
39 + @POST("liff/v1/apps")
40 + Call<LiffAppAddResponse> addLiffApp(@Body LiffAppAddRequest liffAppAddRequest);
41 +
42 + @PUT("liff/v1/apps/{liffId}/view")
43 + Call<Void> updateLiffApp(@Path("liffId") String liffId, @Body LiffView liffView);
44 +
45 + @GET("liff/v1/apps")
46 + Call<LiffAppsResponse> getAllLiffApps();
47 +
48 + @DELETE("liff/v1/apps/{liffId}")
49 + Call<Void> deleteLiffApp(@Path("liffId") String liffId);
50 +}
1 +/*
2 + * Copyright 2018 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import com.linecorp.bot.liff.LiffView;
20 +import com.linecorp.bot.liff.request.LiffAppAddRequest;
21 +import com.linecorp.bot.liff.response.LiffAppAddResponse;
22 +import com.linecorp.bot.liff.response.LiffAppsResponse;
23 +
24 +public interface ChannelManagementSyncClient {
25 + LiffAppAddResponse addLiffApp(LiffAppAddRequest liffView);
26 +
27 + void updateLiffApp(String liffId, LiffView liffView);
28 +
29 + LiffAppsResponse getAllLiffApps();
30 +
31 + void deleteLiffApp(String liffId);
32 +
33 + static ChannelManagementClientBuilder builder(final ChannelTokenSupplier channelTokenSupplier) {
34 + return ChannelManagementClientBuilder.create(channelTokenSupplier);
35 + }
36 +}
1 +/*
2 + * Copyright 2018 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static com.linecorp.bot.client.LineMessagingClientImpl.toFuture;
20 +
21 +import java.util.concurrent.ExecutionException;
22 +
23 +import com.linecorp.bot.client.exception.LineMessagingException;
24 +import com.linecorp.bot.liff.LiffView;
25 +import com.linecorp.bot.liff.request.LiffAppAddRequest;
26 +import com.linecorp.bot.liff.response.LiffAppAddResponse;
27 +import com.linecorp.bot.liff.response.LiffAppsResponse;
28 +
29 +import lombok.AllArgsConstructor;
30 +import retrofit2.Call;
31 +
32 +@AllArgsConstructor(staticName = "of")
33 +public class ChannelManagementSyncClientImpl implements ChannelManagementSyncClient {
34 + ChannelManagementClientRetrofitIface retrofitImpl;
35 +
36 + @Override
37 + public LiffAppAddResponse addLiffApp(final LiffAppAddRequest liffAppAddRequest) {
38 + return syncGet(retrofitImpl.addLiffApp(liffAppAddRequest));
39 + }
40 +
41 + @Override
42 + public void updateLiffApp(String liffId, LiffView liffView) {
43 + syncGet(retrofitImpl.updateLiffApp(liffId, liffView));
44 + }
45 +
46 + @Override
47 + public LiffAppsResponse getAllLiffApps() {
48 + return syncGet(retrofitImpl.getAllLiffApps());
49 + }
50 +
51 + @Override
52 + public void deleteLiffApp(final String liffId) {
53 + syncGet(retrofitImpl.deleteLiffApp(liffId));
54 + }
55 +
56 + private <T> T syncGet(Call<T> wrap) {
57 + try {
58 + return toFuture(wrap).get();
59 + } catch (ExecutionException | InterruptedException e) {
60 + final Throwable cause;
61 + if (e.getCause() instanceof LineMessagingException) {
62 + cause = e.getCause();
63 + } else {
64 + cause = e;
65 + }
66 + throw new RuntimeException(cause);
67 + }
68 + }
69 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.util.function.Supplier;
20 +
21 +/**
22 + * Special {@link Supplier} for Channel Access Token.
23 + *
24 + * <p>You can implement it to return same channel tokens.
25 + * Or refresh tokens internally.
26 + */
27 +@FunctionalInterface
28 +public interface ChannelTokenSupplier extends Supplier<String> {
29 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static java.util.Collections.singletonMap;
20 +
21 +import java.io.IOException;
22 +import java.util.function.Function;
23 +
24 +import com.fasterxml.jackson.databind.InjectableValues;
25 +import com.fasterxml.jackson.databind.ObjectMapper;
26 +import com.fasterxml.jackson.databind.ObjectReader;
27 +
28 +import com.linecorp.bot.client.exception.BadRequestException;
29 +import com.linecorp.bot.client.exception.ForbiddenException;
30 +import com.linecorp.bot.client.exception.GeneralLineMessagingException;
31 +import com.linecorp.bot.client.exception.LineMessagingException;
32 +import com.linecorp.bot.client.exception.LineServerException;
33 +import com.linecorp.bot.client.exception.NotFoundException;
34 +import com.linecorp.bot.client.exception.TooManyRequestsException;
35 +import com.linecorp.bot.client.exception.UnauthorizedException;
36 +import com.linecorp.bot.model.error.ErrorResponse;
37 +
38 +import okhttp3.ResponseBody;
39 +import retrofit2.Response;
40 +
41 +class ExceptionConverter implements Function<Response<?>, LineMessagingException> {
42 + public static final ObjectReader OBJECT_READER = new ObjectMapper().readerFor(ErrorResponse.class);
43 +
44 + @Override
45 + public LineMessagingException apply(Response<?> response) {
46 + final String requestId = response.headers().get("x-line-request-id");
47 + try {
48 + return applyInternal(requestId, response);
49 + } catch (Exception e) {
50 + final ErrorResponse errorResponse = new ErrorResponse(requestId, null, null);
51 + return new GeneralLineMessagingException(e.getMessage(), errorResponse, e);
52 + }
53 + }
54 +
55 + private static LineMessagingException applyInternal(final String requestId, final Response<?> response)
56 + throws IOException {
57 + final int code = response.code();
58 + final ResponseBody responseBody = response.errorBody();
59 +
60 + final ErrorResponse errorResponse = OBJECT_READER
61 + .with(new InjectableValues.Std(singletonMap("requestId", requestId)))
62 + .readValue(responseBody.byteStream());
63 +
64 + switch (code) {
65 + case 400:
66 + return new BadRequestException(
67 + errorResponse.getMessage(), errorResponse);
68 + case 401:
69 + return new UnauthorizedException(
70 + errorResponse.getMessage(), errorResponse);
71 + case 403:
72 + return new ForbiddenException(
73 + errorResponse.getMessage(), errorResponse);
74 + case 404:
75 + return new NotFoundException(
76 + errorResponse.getMessage(), errorResponse);
77 + case 429:
78 + return new TooManyRequestsException(
79 + errorResponse.getMessage(), errorResponse);
80 + case 500:
81 + return new LineServerException(
82 + errorResponse.getMessage(), errorResponse);
83 + }
84 +
85 + return new GeneralLineMessagingException(errorResponse.getMessage(), errorResponse, null);
86 + }
87 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import lombok.AllArgsConstructor;
20 +import lombok.NonNull;
21 +import lombok.ToString;
22 +
23 +/**
24 + * Implementation of {@link ChannelTokenSupplier} which always return fixed channel token.
25 + */
26 +@AllArgsConstructor(staticName = "of")
27 +@ToString
28 +public final class FixedChannelTokenSupplier implements ChannelTokenSupplier {
29 + @NonNull
30 + private final String channelToken;
31 +
32 + @Override
33 + public String get() {
34 + return channelToken;
35 + }
36 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.io.IOException;
20 +
21 +import lombok.AllArgsConstructor;
22 +import okhttp3.Interceptor;
23 +import okhttp3.Request;
24 +import okhttp3.Response;
25 +
26 +@AllArgsConstructor(staticName = "forChannelTokenSupplier")
27 +class HeaderInterceptor implements Interceptor {
28 + private static final String USER_AGENT =
29 + "line-botsdk-java/" + HeaderInterceptor.class.getPackage().getImplementationVersion();
30 + private final ChannelTokenSupplier channelTokenSupplier;
31 +
32 + @Override
33 + public Response intercept(Chain chain) throws IOException {
34 + final String channelToken = channelTokenSupplier.get();
35 + Request request = chain.request().newBuilder()
36 + .addHeader("Authorization", "Bearer " + channelToken)
37 + .addHeader("User-Agent", USER_AGENT)
38 + .build();
39 + return chain.proceed(request);
40 + }
41 +
42 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.util.concurrent.CompletableFuture;
20 +
21 +import com.linecorp.bot.model.response.BotApiResponse;
22 +
23 +/**
24 + * API client of blog (binary large object).
25 + */
26 +public interface LineBlobClient {
27 + /**
28 + * Download image, video, and audio data sent from users.
29 + *
30 + * @see <a href="https://developers.line.me/en/reference/messaging-api/#get-content">//developers.line.me/en/reference/messaging-api/#get-content</a>
31 + */
32 + CompletableFuture<MessageContentResponse> getMessageContent(String messageId);
33 +
34 + /**
35 + * Download rich menu image.
36 + *
37 + * @see <a href="https://developers.line.me/en/docs/messaging-api/reference/#download-rich-menu-image">//developers.line.me/en/docs/messaging-api/reference/#download-rich-menu-image</a>
38 + */
39 + CompletableFuture<MessageContentResponse> getRichMenuImage(String richMenuId);
40 +
41 + /**
42 + * Set RichMenu image.
43 + *
44 + * @see <a href="https://developers.line.me/en/docs/messaging-api/reference/#upload-rich-menu-image">//developers.line.me/en/docs/messaging-api/reference/#upload-rich-menu-image</a>
45 + */
46 + CompletableFuture<BotApiResponse> setRichMenuImage(
47 + String richMenuId, String contentType, byte[] content);
48 +
49 + static LineBlobClientBuilder builder(String channelToken) {
50 + return builder(FixedChannelTokenSupplier.of(channelToken));
51 + }
52 +
53 + static LineBlobClientBuilder builder(ChannelTokenSupplier channelTokenSupplier) {
54 + return new LineBlobClientBuilder().channelTokenSupplier(channelTokenSupplier);
55 + }
56 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static java.util.Objects.requireNonNull;
20 +
21 +import java.net.URI;
22 +import java.util.ArrayList;
23 +import java.util.List;
24 +import java.util.concurrent.TimeUnit;
25 +
26 +import org.slf4j.Logger;
27 +import org.slf4j.LoggerFactory;
28 +
29 +import com.fasterxml.jackson.databind.ObjectMapper;
30 +
31 +import com.linecorp.bot.model.objectmapper.ModelObjectMapper;
32 +
33 +import lombok.NonNull;
34 +import lombok.Setter;
35 +import lombok.ToString;
36 +import lombok.experimental.Accessors;
37 +import lombok.experimental.PackagePrivate;
38 +import okhttp3.Interceptor;
39 +import okhttp3.OkHttpClient;
40 +import okhttp3.logging.HttpLoggingInterceptor;
41 +import okhttp3.logging.HttpLoggingInterceptor.Level;
42 +import retrofit2.Retrofit;
43 +import retrofit2.converter.jackson.JacksonConverterFactory;
44 +
45 +@ToString
46 +@Accessors(fluent = true)
47 +public class LineBlobClientBuilder {
48 + private static final ObjectMapper objectMapper = ModelObjectMapper.createNewObjectMapper();
49 +
50 + /**
51 + * Use {@link LineBlobClient#builder} to create instance.
52 + */
53 + @PackagePrivate
54 + LineBlobClientBuilder() {
55 + }
56 +
57 + /**
58 + * API Endpoint.
59 + *
60 + * <p>Default value = "https://api-data.line.me/".
61 + */
62 + private URI apiEndPoint = LineClientConstants.DEFAULT_BLOB_END_POINT;
63 +
64 + /**
65 + * API Endpoint.
66 + *
67 + * @deprecated use {@link #apiEndPoint(URI)}.
68 + */
69 + @Deprecated
70 + public LineBlobClientBuilder apiEndPoint(String apiEndPoint) {
71 + return apiEndPoint(URI.create(apiEndPoint));
72 + }
73 +
74 + /**
75 + * API Endpoint.
76 + *
77 + * <p>Default value = "https://api-data.line.me/".
78 + */ // We can remove this after delete `setApiEndPoint(String apiEndPoint)`.
79 + public LineBlobClientBuilder apiEndPoint(URI apiEndPoint) {
80 + this.apiEndPoint = requireNonNull(apiEndPoint, "apiEndPoint");
81 + return this;
82 + }
83 +
84 + /**
85 + * Connection timeout.
86 + *
87 + * <p>Default value = {@value LineClientConstants#DEFAULT_CONNECT_TIMEOUT_MILLIS}ms.
88 + */
89 + @Setter
90 + private long connectTimeout = LineClientConstants.DEFAULT_CONNECT_TIMEOUT_MILLIS;
91 +
92 + /**
93 + * Connection timeout.
94 + *
95 + * <p>Default value = {@value LineClientConstants#DEFAULT_READ_TIMEOUT_MILLIS}ms.
96 + */
97 + @Setter
98 + private long readTimeout = LineClientConstants.DEFAULT_READ_TIMEOUT_MILLIS;
99 +
100 + /**
101 + * Write timeout.
102 + *
103 + * <p>Default value = {@value LineClientConstants#DEFAULT_WRITE_TIMEOUT_MILLIS}ms.
104 + */
105 + @Setter
106 + private long writeTimeout = LineClientConstants.DEFAULT_WRITE_TIMEOUT_MILLIS;
107 +
108 + /**
109 + * Channel token supplier of this client.
110 + *
111 + * <p>MUST BE NULL except you configured your own
112 + */
113 + @Setter
114 + private ChannelTokenSupplier channelTokenSupplier;
115 +
116 + /**
117 + * Custom {@link Retrofit.Builder} used internally.
118 + *
119 + * <p>If you want to use your own setting, specify {@link Retrofit.Builder} instance.
120 + * Default builder is used in case of {@code null} (default).
121 + *
122 + * <p>To use this method, please add dependency to 'com.squareup.retrofit2:retrofit'.
123 + *
124 + * @see #createDefaultRetrofitBuilder()
125 + */
126 + @Setter
127 + private Retrofit.Builder retrofitBuilder;
128 +
129 + /**
130 + * Add authentication header.
131 + *
132 + * <p>Default = {@value}. If you manage authentication header yourself, set to {@code false}.
133 + */
134 + @Setter
135 + private boolean addAuthenticationHeader = true;
136 +
137 + private OkHttpClient.Builder okHttpClientBuilder;
138 +
139 + /**
140 + * Custom interceptors.
141 + *
142 + * <p>You can add your own interceptors.
143 + *
144 + * <p>Note: Authentication interceptor is automatically added by default.
145 + *
146 + * @see #addAuthenticationHeader(boolean)
147 + */
148 + @Setter
149 + private List<Interceptor> additionalInterceptors = new ArrayList<>();
150 +
151 + /**
152 + * Set fixed channel token. This overwrites {@link #channelTokenSupplier(ChannelTokenSupplier)}.
153 + *
154 + * @see #channelTokenSupplier(ChannelTokenSupplier)
155 + */
156 + public LineBlobClientBuilder channelToken(String channelToken) {
157 + channelTokenSupplier(FixedChannelTokenSupplier.of(channelToken));
158 + return this;
159 + }
160 +
161 + /**
162 + * Set customized OkHttpClient.Builder.
163 + *
164 + * <p>In case of you need your own customized {@link OkHttpClient},
165 + * this builder allows specify {@link OkHttpClient.Builder} instance.
166 + *
167 + * <p>To use this method, please add dependency to 'com.squareup.retrofit2:retrofit'.
168 + *
169 + * @param addAuthenticationHeader If true, all default okhttp interceptors ignored.
170 + * You should insert authentication headers yourself.
171 + */
172 + public LineBlobClientBuilder okHttpClientBuilder(
173 + final @NonNull OkHttpClient.Builder okHttpClientBuilder,
174 + final boolean addAuthenticationHeader) {
175 + this.okHttpClientBuilder = okHttpClientBuilder;
176 + this.addAuthenticationHeader = addAuthenticationHeader;
177 +
178 + return this;
179 + }
180 +
181 + /**
182 + * Creates a new {@link LineBlobService}.
183 + */
184 + <T> T buildRetrofitIface(URI apiEndPoint, Class<T> retrofitIFace) {
185 + if (okHttpClientBuilder == null) {
186 + okHttpClientBuilder = new OkHttpClient.Builder();
187 + }
188 +
189 + // Add interceptors.
190 + if (addAuthenticationHeader) {
191 + okHttpClientBuilder.addInterceptor(buildAuthenticationInterceptor(channelTokenSupplier));
192 + }
193 + if (additionalInterceptors != null) {
194 + additionalInterceptors.forEach(okHttpClientBuilder::addInterceptor);
195 + }
196 + okHttpClientBuilder.addInterceptor(buildLoggingInterceptor());
197 +
198 + // Set timeout.
199 + okHttpClientBuilder
200 + .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
201 + .readTimeout(readTimeout, TimeUnit.MILLISECONDS)
202 + .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS);
203 +
204 + final OkHttpClient okHttpClient = okHttpClientBuilder.build();
205 +
206 + if (retrofitBuilder == null) {
207 + retrofitBuilder = createDefaultRetrofitBuilder();
208 + }
209 + retrofitBuilder.client(okHttpClient);
210 + retrofitBuilder.baseUrl(apiEndPoint.toString());
211 +
212 + final Retrofit retrofit = retrofitBuilder.build();
213 +
214 + return retrofit.create(retrofitIFace);
215 + }
216 +
217 + static HeaderInterceptor buildAuthenticationInterceptor(ChannelTokenSupplier channelTokenSupplier) {
218 + requireNonNull(channelTokenSupplier, "channelTokenSupplier");
219 + return HeaderInterceptor.forChannelTokenSupplier(channelTokenSupplier);
220 + }
221 +
222 + static Interceptor buildLoggingInterceptor() {
223 + final Logger slf4jLogger = LoggerFactory.getLogger("com.linecorp.bot.client.wire");
224 +
225 + return new HttpLoggingInterceptor(slf4jLogger::info)
226 + .setLevel(Level.BODY);
227 + }
228 +
229 + static Retrofit.Builder createDefaultRetrofitBuilder() {
230 + return new Retrofit.Builder()
231 + .addConverterFactory(JacksonConverterFactory.create(objectMapper));
232 + }
233 +
234 + /**
235 + * Creates a new {@link LineBlobService}.
236 + */
237 + public LineBlobClient build() {
238 + return new LineBlobClientImpl(
239 + buildRetrofitIface(apiEndPoint, LineBlobService.class));
240 + }
241 +
242 + /**
243 + * Creates a new {@link LineBlobService}.
244 + */
245 + public LineBlobClient buildBlobClient() {
246 + return new LineBlobClientImpl(buildRetrofitIface(
247 + apiEndPoint,
248 + LineBlobService.class));
249 + }
250 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.util.concurrent.CompletableFuture;
20 +
21 +import com.linecorp.bot.client.exception.GeneralLineMessagingException;
22 +import com.linecorp.bot.model.response.BotApiResponse;
23 +
24 +import lombok.AllArgsConstructor;
25 +import okhttp3.MediaType;
26 +import okhttp3.RequestBody;
27 +import okhttp3.ResponseBody;
28 +import retrofit2.Call;
29 +import retrofit2.Callback;
30 +import retrofit2.Response;
31 +
32 +@AllArgsConstructor
33 +class LineBlobClientImpl implements LineBlobClient {
34 + private final LineBlobService retrofitImpl;
35 +
36 + @Override
37 + public CompletableFuture<MessageContentResponse> getMessageContent(final String messageId) {
38 + return toMessageContentResponseFuture(retrofitImpl.getMessageContent(messageId));
39 + }
40 +
41 + @Override
42 + public CompletableFuture<MessageContentResponse> getRichMenuImage(final String richMenuId) {
43 + return toMessageContentResponseFuture(retrofitImpl.getRichMenuImage(richMenuId));
44 + }
45 +
46 + @Override
47 + public CompletableFuture<BotApiResponse> setRichMenuImage(
48 + final String richMenuId, final String contentType, final byte[] content) {
49 + final RequestBody requestBody = RequestBody.create(MediaType.parse(contentType), content);
50 + return LineMessagingClientImpl.toBotApiFuture(
51 + retrofitImpl.uploadRichMenuImage(richMenuId, requestBody));
52 + }
53 +
54 + private static CompletableFuture<MessageContentResponse> toMessageContentResponseFuture(
55 + final Call<ResponseBody> callToWrap) {
56 + final ResponseBodyCallbackAdaptor future = new ResponseBodyCallbackAdaptor();
57 + callToWrap.enqueue(future);
58 + return future;
59 + }
60 +
61 + static class ResponseBodyCallbackAdaptor
62 + extends CompletableFuture<MessageContentResponse>
63 + implements Callback<ResponseBody> {
64 +
65 + @Override
66 + public void onResponse(final Call<ResponseBody> call, final Response<ResponseBody> response) {
67 + if (!response.isSuccessful()) {
68 + completeExceptionally(LineMessagingClientImpl.EXCEPTION_CONVERTER.apply(response));
69 + return;
70 + }
71 +
72 + try {
73 + complete(convert(response));
74 + } catch (RuntimeException exceptionInConvert) {
75 + completeExceptionally(
76 + new GeneralLineMessagingException(exceptionInConvert.getMessage(),
77 + null, exceptionInConvert));
78 + }
79 + }
80 +
81 + @Override
82 + public void onFailure(final Call<ResponseBody> call, final Throwable t) {
83 + completeExceptionally(
84 + new GeneralLineMessagingException(t.getMessage(), null, t));
85 + }
86 +
87 + private MessageContentResponse convert(final Response<ResponseBody> response) {
88 + return MessageContentResponse
89 + .builder()
90 + .length(response.body().contentLength())
91 + .allHeaders(response.headers().toMultimap())
92 + .mimeType(response.body().contentType().toString())
93 + .stream(response.body().byteStream())
94 + .build();
95 + }
96 + }
97 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import okhttp3.RequestBody;
20 +import okhttp3.ResponseBody;
21 +import retrofit2.Call;
22 +import retrofit2.http.Body;
23 +import retrofit2.http.GET;
24 +import retrofit2.http.POST;
25 +import retrofit2.http.Path;
26 +import retrofit2.http.Streaming;
27 +
28 +interface LineBlobService {
29 + /**
30 + * Method for Retrofit.
31 + *
32 + * @see LineBlobClient#getMessageContent(String)
33 + */
34 + @Streaming
35 + @GET("v2/bot/message/{messageId}/content")
36 + Call<ResponseBody> getMessageContent(@Path("messageId") String messageId);
37 +
38 + /**
39 + * Method for Retrofit.
40 + *
41 + * @see LineBlobClient#getRichMenuImage(String)
42 + */
43 + @Streaming
44 + @GET("v2/bot/richmenu/{richMenuId}/content")
45 + Call<ResponseBody> getRichMenuImage(@Path("richMenuId") String richMenuId);
46 +
47 + /**
48 + * Method for Retrofit.
49 + *
50 + * @see LineBlobClient#setRichMenuImage(String, String, byte[])
51 + */
52 + @POST("v2/bot/richmenu/{richMenuId}/content")
53 + Call<Void> uploadRichMenuImage(
54 + @Path("richMenuId") String richMenuId,
55 + @Body RequestBody requestBody);
56 +}
1 +/*
2 + * Copyright 2018 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.net.URI;
20 +
21 +/**
22 + * Common constant holder.
23 + */
24 +public enum LineClientConstants {
25 + /* Only public static final fields in this enum. */;
26 + public static final URI DEFAULT_API_END_POINT = URI.create("https://api.line.me/");
27 + public static final URI DEFAULT_BLOB_END_POINT = URI.create("https://api-data.line.me/");
28 + public static final long DEFAULT_CONNECT_TIMEOUT_MILLIS = 10_000;
29 + public static final long DEFAULT_READ_TIMEOUT_MILLIS = 10_000;
30 + public static final long DEFAULT_WRITE_TIMEOUT_MILLIS = 10_000;
31 +}
1 +/*
2 + * Copyright 2018 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static java.util.Objects.requireNonNull;
20 +
21 +import java.net.URI;
22 +import java.util.ArrayList;
23 +import java.util.List;
24 +import java.util.concurrent.TimeUnit;
25 +
26 +import org.slf4j.Logger;
27 +import org.slf4j.LoggerFactory;
28 +
29 +import com.fasterxml.jackson.databind.ObjectMapper;
30 +
31 +import com.linecorp.bot.model.objectmapper.ModelObjectMapper;
32 +
33 +import lombok.NonNull;
34 +import lombok.Setter;
35 +import lombok.ToString;
36 +import lombok.experimental.Accessors;
37 +import lombok.experimental.PackagePrivate;
38 +import okhttp3.Interceptor;
39 +import okhttp3.OkHttpClient;
40 +import okhttp3.logging.HttpLoggingInterceptor;
41 +import okhttp3.logging.HttpLoggingInterceptor.Level;
42 +import retrofit2.Retrofit;
43 +import retrofit2.converter.jackson.JacksonConverterFactory;
44 +
45 +@ToString
46 +@Accessors(fluent = true)
47 +public class LineMessagingClientBuilder {
48 + private static final ObjectMapper objectMapper = ModelObjectMapper.createNewObjectMapper();
49 +
50 + /**
51 + * Use {@link LineMessagingClient#builder(String)} to create instance.
52 + *
53 + * @see LineMessagingClient#builder(String)
54 + * @see LineMessagingClient#builder(ChannelTokenSupplier)
55 + */
56 + @PackagePrivate
57 + LineMessagingClientBuilder() {
58 + }
59 +
60 + /**
61 + * API Endpoint.
62 + *
63 + * <p>Default value = "https://api.line.me/".
64 + */
65 + private URI apiEndPoint = LineClientConstants.DEFAULT_API_END_POINT;
66 +
67 + /**
68 + * API Endpoint.
69 + *
70 + * @deprecated use {@link #apiEndPoint(URI apiEndPoint)}.
71 + */
72 + @Deprecated
73 + public LineMessagingClientBuilder apiEndPoint(String apiEndPoint) {
74 + return apiEndPoint(URI.create(apiEndPoint));
75 + }
76 +
77 + /**
78 + * API Endpoint.
79 + *
80 + * <p>Default value = "https://api.line.me/".
81 + */ // We can remove this after delete `setApiEndPoint(String apiEndPoint)`.
82 + public LineMessagingClientBuilder apiEndPoint(URI apiEndPoint) {
83 + this.apiEndPoint = requireNonNull(apiEndPoint, "apiEndPoint");
84 + return this;
85 + }
86 +
87 + /**
88 + * Blob Endpoint.
89 + *
90 + * <p>Default value = "https://api-data.line.me/".
91 + */
92 + @Setter
93 + private URI blobEndPoint = LineClientConstants.DEFAULT_BLOB_END_POINT;
94 +
95 + /**
96 + * Connection timeout.
97 + *
98 + * <p>Default value = {@value LineClientConstants#DEFAULT_CONNECT_TIMEOUT_MILLIS}ms.
99 + */
100 + @Setter
101 + private long connectTimeout = LineClientConstants.DEFAULT_CONNECT_TIMEOUT_MILLIS;
102 +
103 + /**
104 + * Connection timeout.
105 + *
106 + * <p>Default value = {@value LineClientConstants#DEFAULT_READ_TIMEOUT_MILLIS}ms.
107 + */
108 + @Setter
109 + private long readTimeout = LineClientConstants.DEFAULT_READ_TIMEOUT_MILLIS;
110 +
111 + /**
112 + * Write timeout.
113 + *
114 + * <p>Default value = {@value LineClientConstants#DEFAULT_WRITE_TIMEOUT_MILLIS}ms.
115 + */
116 + @Setter
117 + private long writeTimeout = LineClientConstants.DEFAULT_WRITE_TIMEOUT_MILLIS;
118 +
119 + /**
120 + * Channel token supplier of this client.
121 + *
122 + * <p>MUST BE NULL except you configured your own
123 + */
124 + @Setter
125 + private ChannelTokenSupplier channelTokenSupplier;
126 +
127 + /**
128 + * Custom {@link Retrofit.Builder} used internally.
129 + *
130 + * <p>If you want to use your own setting, specify {@link Retrofit.Builder} instance.
131 + * Default builder is used in case of {@code null} (default).
132 + *
133 + * <p>To use this method, please add dependency to 'com.squareup.retrofit2:retrofit'.
134 + *
135 + * @see #createDefaultRetrofitBuilder()
136 + */
137 + @Setter
138 + private Retrofit.Builder retrofitBuilder;
139 +
140 + /**
141 + * Add authentication header.
142 + *
143 + * <p>Default = {@value}. If you manage authentication header yourself, set to {@doe false}.
144 + */
145 + @Setter
146 + private boolean addAuthenticationHeader = true;
147 +
148 + private OkHttpClient.Builder okHttpClientBuilder;
149 +
150 + /**
151 + * Custom interceptors.
152 + *
153 + * <p>You can add your own interceptors.
154 + *
155 + * <p>Note: Authentication interceptor is automatically added by default.
156 + *
157 + * @see #addAuthenticationHeader(boolean)
158 + */
159 + @Setter
160 + private List<Interceptor> additionalInterceptors = new ArrayList<>();
161 +
162 + /**
163 + * Set fixed channel token. This overwrites {@link #channelTokenSupplier(ChannelTokenSupplier)}.
164 + *
165 + * @see #channelTokenSupplier(ChannelTokenSupplier)
166 + */
167 + public LineMessagingClientBuilder channelToken(String channelToken) {
168 + channelTokenSupplier(FixedChannelTokenSupplier.of(channelToken));
169 + return this;
170 + }
171 +
172 + /**
173 + * Set customized OkHttpClient.Builder.
174 + *
175 + * <p>In case of you need your own customized {@link OkHttpClient},
176 + * this builder allows specify {@link OkHttpClient.Builder} instance.
177 + *
178 + * <p>To use this method, please add dependency to 'com.squareup.retrofit2:retrofit'.
179 + *
180 + * @param addAuthenticationHeader If true, all default okhttp interceptors ignored.
181 + * You should insert authentication headers yourself.
182 + */
183 + public LineMessagingClientBuilder okHttpClientBuilder(
184 + final @NonNull OkHttpClient.Builder okHttpClientBuilder,
185 + final boolean addAuthenticationHeader) {
186 + this.okHttpClientBuilder = okHttpClientBuilder;
187 + this.addAuthenticationHeader = addAuthenticationHeader;
188 +
189 + return this;
190 + }
191 +
192 + /**
193 + * Creates a new {@link LineMessagingService}.
194 + */
195 + <T> T buildRetrofitIface(URI apiEndPoint, Class<T> retrofitIFace) {
196 + if (okHttpClientBuilder == null) {
197 + okHttpClientBuilder = new OkHttpClient.Builder();
198 + }
199 +
200 + // Add interceptors.
201 + if (addAuthenticationHeader) {
202 + okHttpClientBuilder.addInterceptor(buildAuthenticationInterceptor(channelTokenSupplier));
203 + }
204 + if (additionalInterceptors != null) {
205 + additionalInterceptors.forEach(okHttpClientBuilder::addInterceptor);
206 + }
207 + okHttpClientBuilder.addInterceptor(buildLoggingInterceptor());
208 +
209 + // Set timeout.
210 + okHttpClientBuilder
211 + .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
212 + .readTimeout(readTimeout, TimeUnit.MILLISECONDS)
213 + .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS);
214 +
215 + final OkHttpClient okHttpClient = okHttpClientBuilder.build();
216 +
217 + if (retrofitBuilder == null) {
218 + retrofitBuilder = createDefaultRetrofitBuilder();
219 + }
220 + retrofitBuilder.client(okHttpClient);
221 + retrofitBuilder.baseUrl(apiEndPoint.toString());
222 +
223 + final Retrofit retrofit = retrofitBuilder.build();
224 +
225 + return retrofit.create(retrofitIFace);
226 + }
227 +
228 + static HeaderInterceptor buildAuthenticationInterceptor(ChannelTokenSupplier channelTokenSupplier) {
229 + requireNonNull(channelTokenSupplier, "channelTokenSupplier");
230 + return HeaderInterceptor.forChannelTokenSupplier(channelTokenSupplier);
231 + }
232 +
233 + static Interceptor buildLoggingInterceptor() {
234 + final Logger slf4jLogger = LoggerFactory.getLogger("com.linecorp.bot.client.wire");
235 +
236 + return new HttpLoggingInterceptor(slf4jLogger::info)
237 + .setLevel(Level.BODY);
238 + }
239 +
240 + static Retrofit.Builder createDefaultRetrofitBuilder() {
241 + return new Retrofit.Builder()
242 + .addConverterFactory(JacksonConverterFactory.create(objectMapper));
243 + }
244 +
245 + /**
246 + * Creates a new {@link LineMessagingService}.
247 + */
248 + public LineMessagingClient build() {
249 + return new LineMessagingClientImpl(
250 + buildRetrofitIface(apiEndPoint, LineMessagingService.class),
251 + buildBlobClient());
252 + }
253 +
254 + /**
255 + * Creates a new {@link LineMessagingService}.
256 + */
257 + private LineBlobClient buildBlobClient() {
258 + return new LineBlobClientImpl(buildRetrofitIface(
259 + blobEndPoint,
260 + LineBlobService.class));
261 + }
262 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.util.concurrent.CompletableFuture;
20 +
21 +import com.linecorp.bot.model.oauth.ChannelAccessTokenException;
22 +import com.linecorp.bot.model.oauth.IssueChannelAccessTokenRequest;
23 +import com.linecorp.bot.model.oauth.IssueChannelAccessTokenResponse;
24 +
25 +/**
26 + * An OAuth client that issues or revokes channel access tokens. See
27 + * <a href="https://developers.line.biz/en/reference/messaging-api/#issue-channel-access-token">document</a>
28 + * for detail.
29 + */
30 +public interface LineOAuthClient {
31 + /**
32 + * Creates a {@link LineOAuthClientBuilder}.
33 + */
34 + static LineOAuthClientBuilder builder() {
35 + return new LineOAuthClientBuilder();
36 + }
37 +
38 + /**
39 + * Issues a channel access token. This method lets you use JWT assertion for authentication.
40 + *
41 + * <p>You can issue up to 30 tokens.
42 + * If you reach the maximum limit, additional requests of issuing channel access tokens are blocked.
43 + *
44 + * @param clientAssertion A JSON Web Token the client needs to create and sign with the private key created
45 + * when issuing an assertion signing key.
46 + * @see <a href="https://developers.line.biz/en/reference/messaging-api/#issue-channel-access-token-v2-1">Issue channel access token v2.1</a>
47 + */
48 + CompletableFuture<IssueChannelAccessTokenResponse> issueChannelTokenByJWT(
49 + String clientAssertion);
50 +
51 + /**
52 + * Revokes a channel access token.
53 + *
54 + * @param clientId Channel ID
55 + * @param clientSecret Channel Secret
56 + * @param accessToken Channel access token
57 + * @see <a href="https://developers.line.biz/en/reference/messaging-api/#revoke-channel-access-token-v2-1">Revoke channel access token v2.1</a>
58 + */
59 + CompletableFuture<Void> revokeChannelTokenByJWT(
60 + String clientId,
61 + String clientSecret,
62 + String accessToken);
63 +
64 + /**
65 + * Issues a short-lived channel access token. Up to 30 tokens can be issued. If the maximum is exceeded,
66 + * existing channel access tokens are revoked in the order of when they were first issued.
67 + * It will return a failed {@link CompletableFuture} with {@link ChannelAccessTokenException} if
68 + * it has an error during calling the API.
69 + */
70 + CompletableFuture<IssueChannelAccessTokenResponse> issueChannelToken(IssueChannelAccessTokenRequest req);
71 +
72 + /**
73 + * Revokes a channel access token. It will return a failed {@link CompletableFuture} with
74 + * {@link ChannelAccessTokenException} if it has an error during calling the API.
75 + */
76 + CompletableFuture<Void> revokeChannelToken(String accessToken);
77 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static java.util.Objects.requireNonNull;
20 +
21 +import java.net.URI;
22 +import java.util.ArrayList;
23 +import java.util.List;
24 +import java.util.concurrent.TimeUnit;
25 +
26 +import org.slf4j.Logger;
27 +import org.slf4j.LoggerFactory;
28 +
29 +import com.fasterxml.jackson.databind.ObjectMapper;
30 +
31 +import com.linecorp.bot.model.objectmapper.ModelObjectMapper;
32 +
33 +import lombok.NonNull;
34 +import lombok.Setter;
35 +import lombok.ToString;
36 +import lombok.experimental.Accessors;
37 +import lombok.experimental.PackagePrivate;
38 +import okhttp3.Interceptor;
39 +import okhttp3.OkHttpClient;
40 +import okhttp3.logging.HttpLoggingInterceptor;
41 +import okhttp3.logging.HttpLoggingInterceptor.Level;
42 +import retrofit2.Retrofit;
43 +import retrofit2.converter.jackson.JacksonConverterFactory;
44 +
45 +@ToString
46 +@Accessors(fluent = true)
47 +public class LineOAuthClientBuilder {
48 + /**
49 + * Creates a {@link LineOAuthClient}. Use {@link LineOAuthClient#builder()} instead.
50 + */
51 + @PackagePrivate
52 + LineOAuthClientBuilder() {
53 + }
54 +
55 + /**
56 + * API Endpoint.
57 + *
58 + * <p>Default value = "https://api.line.me/".
59 + */
60 + private URI apiEndPoint = LineClientConstants.DEFAULT_API_END_POINT;
61 +
62 + /**
63 + * API Endpoint.
64 + *
65 + * @deprecated use {@link #apiEndPoint(URI apiEndPoint)}.
66 + */
67 + @Deprecated
68 + public LineOAuthClientBuilder apiEndPoint(String apiEndPoint) {
69 + return apiEndPoint(URI.create(apiEndPoint));
70 + }
71 +
72 + /**
73 + * API Endpoint.
74 + *
75 + * <p>Default value = "https://api.line.me/".
76 + */
77 + public LineOAuthClientBuilder apiEndPoint(URI apiEndPoint) {
78 + this.apiEndPoint = requireNonNull(apiEndPoint, "apiEndPoint");
79 + return this;
80 + }
81 +
82 + /**
83 + * Connection timeout.
84 + *
85 + * <p>Default value = {@value LineClientConstants#DEFAULT_CONNECT_TIMEOUT_MILLIS}ms.
86 + */
87 + @Setter
88 + private long connectTimeout = LineClientConstants.DEFAULT_CONNECT_TIMEOUT_MILLIS;
89 +
90 + /**
91 + * Connection timeout.
92 + *
93 + * <p>Default value = {@value LineClientConstants#DEFAULT_READ_TIMEOUT_MILLIS}ms.
94 + */
95 + @Setter
96 + private long readTimeout = LineClientConstants.DEFAULT_READ_TIMEOUT_MILLIS;
97 +
98 + /**
99 + * Write timeout.
100 + *
101 + * <p>Default value = {@value LineClientConstants#DEFAULT_WRITE_TIMEOUT_MILLIS}ms.
102 + */
103 + @Setter
104 + private long writeTimeout = LineClientConstants.DEFAULT_WRITE_TIMEOUT_MILLIS;
105 +
106 + /**
107 + * Custom {@link Retrofit.Builder} used internally.
108 + *
109 + * <p>If you want to use your own setting, specify {@link Retrofit.Builder} instance.
110 + * Default builder is used in case of {@code null} (default).
111 + *
112 + * <p>To use this method, please add dependency to 'com.squareup.retrofit2:retrofit'.
113 + *
114 + * @see #createDefaultRetrofitBuilder()
115 + */
116 + @Setter
117 + private Retrofit.Builder retrofitBuilder;
118 +
119 + private OkHttpClient.Builder okHttpClientBuilder;
120 +
121 + /**
122 + * Custom interceptors.
123 + *
124 + * <p>You can add your own interceptors.
125 + */
126 + @Setter
127 + private List<Interceptor> additionalInterceptors = new ArrayList<>();
128 +
129 + /**
130 + * Set customized {@link OkHttpClient.Builder}.
131 + *
132 + * <p>In case of you need your own customized {@link OkHttpClient},
133 + * this builder allows specify {@link OkHttpClient.Builder} instance.
134 + */
135 + public LineOAuthClientBuilder okHttpClientBuilder(@NonNull OkHttpClient.Builder okHttpClientBuilder) {
136 + this.okHttpClientBuilder = okHttpClientBuilder;
137 + return this;
138 + }
139 +
140 + /**
141 + * Creates a new {@link LineOAuthService}.
142 + */
143 + private LineOAuthService buildRetrofit() {
144 + if (okHttpClientBuilder == null) {
145 + okHttpClientBuilder = new OkHttpClient.Builder();
146 + }
147 +
148 + if (additionalInterceptors != null) {
149 + additionalInterceptors.forEach(okHttpClientBuilder::addInterceptor);
150 + }
151 + okHttpClientBuilder.addInterceptor(buildLoggingInterceptor());
152 +
153 + // Set timeout.
154 + okHttpClientBuilder
155 + .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
156 + .readTimeout(readTimeout, TimeUnit.MILLISECONDS)
157 + .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS);
158 +
159 + final OkHttpClient okHttpClient = okHttpClientBuilder.build();
160 +
161 + if (retrofitBuilder == null) {
162 + retrofitBuilder = createDefaultRetrofitBuilder();
163 + }
164 + retrofitBuilder.client(okHttpClient);
165 + retrofitBuilder.baseUrl(apiEndPoint.toString());
166 +
167 + final Retrofit retrofit = retrofitBuilder.build();
168 +
169 + return retrofit.create(LineOAuthService.class);
170 + }
171 +
172 + private static Interceptor buildLoggingInterceptor() {
173 + final Logger slf4jLogger = LoggerFactory.getLogger("com.linecorp.bot.client.wire");
174 +
175 + return new HttpLoggingInterceptor(slf4jLogger::info)
176 + .setLevel(Level.BODY);
177 + }
178 +
179 + private static Retrofit.Builder createDefaultRetrofitBuilder() {
180 + final ObjectMapper objectMapper = ModelObjectMapper.createNewObjectMapper();
181 +
182 + return new Retrofit.Builder()
183 + .addConverterFactory(JacksonConverterFactory.create(objectMapper));
184 + }
185 +
186 + /**
187 + * Creates a new {@link LineOAuthClient}.
188 + */
189 + public LineOAuthClient build() {
190 + return new LineOAuthClientImpl(buildRetrofit());
191 + }
192 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.io.IOException;
20 +import java.util.concurrent.CompletableFuture;
21 +
22 +import com.fasterxml.jackson.databind.ObjectMapper;
23 +
24 +import com.linecorp.bot.model.oauth.ChannelAccessTokenException;
25 +import com.linecorp.bot.model.oauth.IssueChannelAccessTokenRequest;
26 +import com.linecorp.bot.model.oauth.IssueChannelAccessTokenResponse;
27 +import com.linecorp.bot.model.objectmapper.ModelObjectMapper;
28 +
29 +import lombok.AllArgsConstructor;
30 +import lombok.experimental.PackagePrivate;
31 +import retrofit2.Call;
32 +import retrofit2.Callback;
33 +import retrofit2.Response;
34 +
35 +/**
36 + * An implementation of {@link LineOAuthClient} that issues or revokes channel access tokens.
37 + */
38 +@AllArgsConstructor
39 +@PackagePrivate
40 +class LineOAuthClientImpl implements LineOAuthClient {
41 + private static final ObjectMapper objectMapper = ModelObjectMapper.createNewObjectMapper();
42 +
43 + private final LineOAuthService service;
44 +
45 + @Override
46 + public CompletableFuture<IssueChannelAccessTokenResponse> issueChannelTokenByJWT(final String jwt) {
47 + return toFuture(service.issueChannelTokenByJWT(
48 + "client_credentials",
49 + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
50 + jwt));
51 + }
52 +
53 + @Override
54 + public CompletableFuture<Void> revokeChannelTokenByJWT(
55 + String clientId, String clientSecret, String accessToken) {
56 + return toFuture(service.revokeChannelTokenByJWT(clientId, clientSecret, accessToken));
57 + }
58 +
59 + @Override
60 + public CompletableFuture<IssueChannelAccessTokenResponse> issueChannelToken(
61 + IssueChannelAccessTokenRequest req) {
62 + return toFuture(service.issueChannelToken(req.getGrantType(),
63 + req.getClientId(),
64 + req.getClientSecret()));
65 + }
66 +
67 + @Override
68 + public CompletableFuture<Void> revokeChannelToken(String accessToken) {
69 + return toFuture(service.revokeChannelToken(accessToken));
70 + }
71 +
72 + private static <T> CompletableFuture<T> toFuture(Call<T> call) {
73 + final CallbackCompletableFuture<T> future = new CallbackCompletableFuture<>();
74 + call.enqueue(future);
75 + return future;
76 + }
77 +
78 + static class CallbackCompletableFuture<T> extends CompletableFuture<T> implements Callback<T> {
79 + @Override
80 + public void onResponse(final Call<T> call, final Response<T> response) {
81 + if (response.isSuccessful()) {
82 + complete(response.body());
83 + return;
84 + }
85 + if (response.code() == 400) {
86 + try {
87 + completeExceptionally(objectMapper.readValue(response.errorBody().string(),
88 + ChannelAccessTokenException.class));
89 + return;
90 + } catch (IOException e) {
91 + completeExceptionally(e);
92 + }
93 + }
94 + completeExceptionally(new ChannelAccessTokenException(response.message()));
95 + }
96 +
97 + @Override
98 + public void onFailure(final Call<T> call, final Throwable t) {
99 + completeExceptionally(new ChannelAccessTokenException(t.getMessage(), t));
100 + }
101 + }
102 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.util.concurrent.CompletableFuture;
20 +
21 +import com.linecorp.bot.model.oauth.ChannelAccessTokenException;
22 +import com.linecorp.bot.model.oauth.IssueChannelAccessTokenResponse;
23 +
24 +import lombok.experimental.PackagePrivate;
25 +import retrofit2.Call;
26 +import retrofit2.http.Field;
27 +import retrofit2.http.FormUrlEncoded;
28 +import retrofit2.http.POST;
29 +
30 +/**
31 + * An OAuth client that issues or revokes channel access tokens. See {@link LineOAuthClient} and
32 + * <a href="https://developers.line.biz/en/reference/messaging-api/#issue-channel-access-token">document</a>
33 + * for detail.
34 + */
35 +@PackagePrivate
36 +interface LineOAuthService {
37 + @POST("oauth2/v2.1/token")
38 + @FormUrlEncoded
39 + Call<IssueChannelAccessTokenResponse> issueChannelTokenByJWT(
40 + @Field("grant_type") String grantType,
41 + @Field("client_assertion_type") String clientAssertionType,
42 + @Field("client_assertion") String clientAssertion);
43 +
44 + @POST("/oauth2/v2.1/revoke")
45 + @FormUrlEncoded
46 + Call<Void> revokeChannelTokenByJWT(
47 + @Field("client_id") String clientId,
48 + @Field("client_secret") String clientSecret,
49 + @Field("access_token") String accessToken);
50 +
51 + /**
52 + * Issues a short-lived channel access token. Up to 30 tokens can be issued. If the maximum is exceeded,
53 + * existing channel access tokens are revoked in the order of when they were first issued.
54 + * It will return a failed {@link CompletableFuture} with {@link ChannelAccessTokenException} if
55 + * it has an error during calling the API.
56 + */
57 + @FormUrlEncoded
58 + @POST("v2/oauth/accessToken")
59 + Call<IssueChannelAccessTokenResponse> issueChannelToken(@Field("grant_type") String grantType,
60 + @Field("client_id") String clientId,
61 + @Field("client_secret") String clientSecret);
62 +
63 + /**
64 + * Revokes a channel access token. It will return a failed {@link CompletableFuture} with
65 + * {@link ChannelAccessTokenException} if it has an error during calling the API.
66 + */
67 + @FormUrlEncoded
68 + @POST("v2/oauth/revoke")
69 + Call<Void> revokeChannelToken(@Field("access_token") String accessToken);
70 +}
1 +/*
2 + * Copyright 2020 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +package com.linecorp.bot.client;
17 +
18 +import java.util.concurrent.CompletableFuture;
19 +
20 +import com.linecorp.bot.model.manageaudience.AudienceGroupCreateRoute;
21 +import com.linecorp.bot.model.manageaudience.AudienceGroupStatus;
22 +import com.linecorp.bot.model.manageaudience.request.AddAudienceToAudienceGroupRequest;
23 +import com.linecorp.bot.model.manageaudience.request.CreateAudienceGroupRequest;
24 +import com.linecorp.bot.model.manageaudience.request.CreateClickBasedAudienceGroupRequest;
25 +import com.linecorp.bot.model.manageaudience.request.CreateImpBasedAudienceGroupRequest;
26 +import com.linecorp.bot.model.manageaudience.request.UpdateAudienceGroupAuthorityLevelRequest;
27 +import com.linecorp.bot.model.manageaudience.request.UpdateAudienceGroupDescriptionRequest;
28 +import com.linecorp.bot.model.manageaudience.response.CreateAudienceGroupResponse;
29 +import com.linecorp.bot.model.manageaudience.response.CreateClickBasedAudienceGroupResponse;
30 +import com.linecorp.bot.model.manageaudience.response.CreateImpBasedAudienceGroupResponse;
31 +import com.linecorp.bot.model.manageaudience.response.GetAudienceDataResponse;
32 +import com.linecorp.bot.model.manageaudience.response.GetAudienceGroupAuthorityLevelResponse;
33 +import com.linecorp.bot.model.manageaudience.response.GetAudienceGroupsResponse;
34 +import com.linecorp.bot.model.response.BotApiResponse;
35 +
36 +import retrofit2.http.Body;
37 +
38 +public interface ManageAudienceClient {
39 +
40 + /**
41 + * Creates an audience for uploading user IDs. You can create up to 1,000 audiences.
42 + *
43 + * @see <a href="https://developers.line.biz/en/reference/messaging-api/#create-upload-audience-group">
44 + * Create audience for uploading user IDs</a>
45 + */
46 + CompletableFuture<CreateAudienceGroupResponse> createAudienceGroup(CreateAudienceGroupRequest request);
47 +
48 + /**
49 + * Adds new user IDs or IFAs to an audience for uploading user IDs.
50 + *
51 + * @see <a href="https://developers.line.biz/en/reference/messaging-api/#update-upload-audience-group">
52 + * Add user IDs or Identifiers for Advertisers (IFAs) to an audience for uploading user IDs</a>
53 + */
54 + CompletableFuture<BotApiResponse> addAudienceToAudienceGroup(
55 + AddAudienceToAudienceGroupRequest request);
56 +
57 + /**
58 + * Creates an audience for click-based retargeting.
59 + *
60 + * @see <a href="https://developers.line.biz/en/reference/messaging-api/#create-click-audience-group">
61 + * Create audience for click-based retargeting</a>
62 + */
63 + CompletableFuture<CreateClickBasedAudienceGroupResponse> createClickBasedAudienceGroup(
64 + CreateClickBasedAudienceGroupRequest request);
65 +
66 + /**
67 + * Creates an audience for impression-based retargeting.
68 + *
69 + * @see <a href="https://developers.line.biz/en/reference/messaging-api/#create-imp-audience-group">
70 + * Create audience for impression-based retargeting</a>
71 + */
72 + CompletableFuture<CreateImpBasedAudienceGroupResponse> createImpBasedAudienceGroup(
73 + CreateImpBasedAudienceGroupRequest request);
74 +
75 + /**
76 + * Renames an existing audience.
77 + *
78 + * @param audienceGroupId The audience ID.
79 + * @see <a href="https://developers.line.biz/en/reference/messaging-api/#set-description-audience-group">
80 + * Rename an audience</a>
81 + */
82 + CompletableFuture<BotApiResponse> updateAudienceGroupDescription(
83 + long audienceGroupId, UpdateAudienceGroupDescriptionRequest request);
84 +
85 + /**
86 + * Deletes an audience.
87 + *
88 + * @param audienceGroupId The audience ID.
89 + * @see <a href="https://developers.line.biz/en/reference/messaging-api/#delete-audience-group">
90 + * Delete audience</a>
91 + */
92 + CompletableFuture<BotApiResponse> deleteAudienceGroup(long audienceGroupId);
93 +
94 + /**
95 + * Gets audience data.
96 + *
97 + * @param audienceGroupId The audience ID.
98 + * @see <a href="https://developers.line.biz/en/reference/messaging-api/#get-audience-group">
99 + * Get audience data</a>
100 + */
101 + CompletableFuture<GetAudienceDataResponse> getAudienceData(long audienceGroupId);
102 +
103 + /**
104 + * Gets data for more than one audience.
105 + *
106 + * @param page The page to return when getting (paginated) results. Specify a value of 1 or more.
107 + * @param description The name of the audience(s) to return. You can search for partial matches.
108 + * Comparisons are case-insensitive, so the names AUDIENCE and audience are considered
109 + * identical.
110 + * @param status The audience's status.
111 + * @param size The number of audiences per page. This is 20 by default.
112 + * Max: 40
113 + * @see <a href="https://developers.line.biz/en/reference/messaging-api/#get-audience-groups">
114 + * Get data for multiple audiences</a>
115 + */
116 + CompletableFuture<GetAudienceGroupsResponse> getAudienceGroups(
117 + long page, String description, AudienceGroupStatus status, Long size,
118 + Boolean includesExternalPublicGroups, AudienceGroupCreateRoute createRoute);
119 +
120 + /**
121 + * Get audience group authority level.
122 + *
123 + * @see <a href="https://developers.line.biz/en/reference/messaging-api/#get-authority-level">
124 + * Get authority level</a>
125 + */
126 + CompletableFuture<GetAudienceGroupAuthorityLevelResponse> getAudienceGroupAuthorityLevel();
127 +
128 + /**
129 + * Update audience group authority level.
130 + *
131 + * @see <a href="https://developers.line.biz/en/reference/messaging-api/#change-authority-level">
132 + * Change authority level</a>
133 + */
134 + CompletableFuture<BotApiResponse> updateAudienceGroupAuthorityLevel(
135 + @Body UpdateAudienceGroupAuthorityLevelRequest request);
136 +
137 + static ManageAudienceClientBuilder builder() {
138 + return new ManageAudienceClientBuilder();
139 + }
140 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static java.util.Objects.requireNonNull;
20 +
21 +import java.net.URI;
22 +import java.util.ArrayList;
23 +import java.util.List;
24 +import java.util.concurrent.TimeUnit;
25 +
26 +import org.slf4j.Logger;
27 +import org.slf4j.LoggerFactory;
28 +
29 +import com.fasterxml.jackson.databind.ObjectMapper;
30 +
31 +import com.linecorp.bot.model.objectmapper.ModelObjectMapper;
32 +
33 +import lombok.NonNull;
34 +import lombok.Setter;
35 +import lombok.ToString;
36 +import lombok.experimental.Accessors;
37 +import lombok.experimental.PackagePrivate;
38 +import okhttp3.Interceptor;
39 +import okhttp3.OkHttpClient;
40 +import okhttp3.logging.HttpLoggingInterceptor;
41 +import okhttp3.logging.HttpLoggingInterceptor.Level;
42 +import retrofit2.Retrofit;
43 +import retrofit2.converter.jackson.JacksonConverterFactory;
44 +
45 +@ToString
46 +@Accessors(fluent = true)
47 +public class ManageAudienceClientBuilder {
48 + private static final ObjectMapper objectMapper = ModelObjectMapper.createNewObjectMapper();
49 +
50 + /**
51 + * Use {@link ManageAudienceClient#builder()} to create instance.
52 + */
53 + @PackagePrivate
54 + ManageAudienceClientBuilder() {
55 + }
56 +
57 + /**
58 + * API Endpoint.
59 + *
60 + * <p>Default value = "https://api.line.me/".
61 + */
62 + private URI apiEndPoint = LineClientConstants.DEFAULT_API_END_POINT;
63 +
64 + /**
65 + * API Endpoint.
66 + *
67 + * @deprecated use {@link #apiEndPoint(URI apiEndPoint)}.
68 + */
69 + @Deprecated
70 + public ManageAudienceClientBuilder apiEndPoint(String apiEndPoint) {
71 + return apiEndPoint(URI.create(apiEndPoint));
72 + }
73 +
74 + /**
75 + * API Endpoint.
76 + *
77 + * <p>Default value = "https://api.line.me/".
78 + */ // We can remove this after delete `setApiEndPoint(String apiEndPoint)`.
79 + public ManageAudienceClientBuilder apiEndPoint(URI apiEndPoint) {
80 + this.apiEndPoint = requireNonNull(apiEndPoint, "apiEndPoint");
81 + return this;
82 + }
83 +
84 + /**
85 + * Connection timeout.
86 + *
87 + * <p>Default value = {@value LineClientConstants#DEFAULT_CONNECT_TIMEOUT_MILLIS}ms.
88 + */
89 + @Setter
90 + private long connectTimeout = LineClientConstants.DEFAULT_CONNECT_TIMEOUT_MILLIS;
91 +
92 + /**
93 + * Connection timeout.
94 + *
95 + * <p>Default value = {@value LineClientConstants#DEFAULT_READ_TIMEOUT_MILLIS}ms.
96 + */
97 + @Setter
98 + private long readTimeout = LineClientConstants.DEFAULT_READ_TIMEOUT_MILLIS;
99 +
100 + /**
101 + * Write timeout.
102 + *
103 + * <p>Default value = {@value LineClientConstants#DEFAULT_WRITE_TIMEOUT_MILLIS}ms.
104 + */
105 + @Setter
106 + private long writeTimeout = LineClientConstants.DEFAULT_WRITE_TIMEOUT_MILLIS;
107 +
108 + /**
109 + * Channel token supplier of this client.
110 + *
111 + * <p>MUST BE NULL except you configured your own
112 + */
113 + @Setter
114 + private ChannelTokenSupplier channelTokenSupplier;
115 +
116 + /**
117 + * Custom {@link Retrofit.Builder} used internally.
118 + *
119 + * <p>If you want to use your own setting, specify {@link Retrofit.Builder} instance.
120 + * Default builder is used in case of {@code null} (default).
121 + *
122 + * <p>To use this method, please add dependency to 'com.squareup.retrofit2:retrofit'.
123 + *
124 + * @see #createDefaultRetrofitBuilder()
125 + */
126 + @Setter
127 + private Retrofit.Builder retrofitBuilder;
128 +
129 + /**
130 + * Add authentication header.
131 + *
132 + * <p>Default = {@value}. If you manage authentication header yourself, set to {@code false}.
133 + */
134 + @Setter
135 + private boolean addAuthenticationHeader = true;
136 +
137 + private OkHttpClient.Builder okHttpClientBuilder;
138 +
139 + /**
140 + * Custom interceptors.
141 + *
142 + * <p>You can add your own interceptors.
143 + *
144 + * <p>Note: Authentication interceptor is automatically added by default.
145 + *
146 + * @see #addAuthenticationHeader(boolean)
147 + */
148 + @Setter
149 + private List<Interceptor> additionalInterceptors = new ArrayList<>();
150 +
151 + /**
152 + * Set fixed channel token. This overwrites {@link #channelTokenSupplier(ChannelTokenSupplier)}.
153 + *
154 + * @see #channelTokenSupplier(ChannelTokenSupplier)
155 + */
156 + public ManageAudienceClientBuilder channelToken(String channelToken) {
157 + channelTokenSupplier(FixedChannelTokenSupplier.of(channelToken));
158 + return this;
159 + }
160 +
161 + /**
162 + * Set customized OkHttpClient.Builder.
163 + *
164 + * <p>In case of you need your own customized {@link OkHttpClient},
165 + * this builder allows specify {@link OkHttpClient.Builder} instance.
166 + *
167 + * <p>To use this method, please add dependency to 'com.squareup.retrofit2:retrofit'.
168 + *
169 + * @param addAuthenticationHeader If true, all default okhttp interceptors ignored.
170 + * You should insert authentication headers yourself.
171 + */
172 + public ManageAudienceClientBuilder okHttpClientBuilder(
173 + final @NonNull OkHttpClient.Builder okHttpClientBuilder,
174 + final boolean addAuthenticationHeader) {
175 + this.okHttpClientBuilder = okHttpClientBuilder;
176 + this.addAuthenticationHeader = addAuthenticationHeader;
177 +
178 + return this;
179 + }
180 +
181 + /**
182 + * Creates a new {@link LineMessagingService}.
183 + */
184 + <T> T buildRetrofitIface(URI apiEndPoint, Class<T> retrofitIFace) {
185 + if (okHttpClientBuilder == null) {
186 + okHttpClientBuilder = new OkHttpClient.Builder();
187 + }
188 +
189 + // Add interceptors.
190 + if (addAuthenticationHeader) {
191 + okHttpClientBuilder.addInterceptor(buildAuthenticationInterceptor(channelTokenSupplier));
192 + }
193 + if (additionalInterceptors != null) {
194 + additionalInterceptors.forEach(okHttpClientBuilder::addInterceptor);
195 + }
196 + okHttpClientBuilder.addInterceptor(buildLoggingInterceptor());
197 +
198 + // Set timeout.
199 + okHttpClientBuilder
200 + .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
201 + .readTimeout(readTimeout, TimeUnit.MILLISECONDS)
202 + .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS);
203 +
204 + final OkHttpClient okHttpClient = okHttpClientBuilder.build();
205 +
206 + if (retrofitBuilder == null) {
207 + retrofitBuilder = createDefaultRetrofitBuilder();
208 + }
209 + retrofitBuilder.client(okHttpClient);
210 + retrofitBuilder.baseUrl(apiEndPoint.toString());
211 +
212 + final Retrofit retrofit = retrofitBuilder.build();
213 +
214 + return retrofit.create(retrofitIFace);
215 + }
216 +
217 + static HeaderInterceptor buildAuthenticationInterceptor(ChannelTokenSupplier channelTokenSupplier) {
218 + requireNonNull(channelTokenSupplier, "channelTokenSupplier");
219 + return HeaderInterceptor.forChannelTokenSupplier(channelTokenSupplier);
220 + }
221 +
222 + static Interceptor buildLoggingInterceptor() {
223 + final Logger slf4jLogger = LoggerFactory.getLogger("com.linecorp.bot.client.wire");
224 +
225 + return new HttpLoggingInterceptor(slf4jLogger::info)
226 + .setLevel(Level.BODY);
227 + }
228 +
229 + static Retrofit.Builder createDefaultRetrofitBuilder() {
230 + return new Retrofit.Builder()
231 + .addConverterFactory(JacksonConverterFactory.create(objectMapper));
232 + }
233 +
234 + /**
235 + * Creates a new {@link LineMessagingService}.
236 + */
237 + public ManageAudienceClient build() {
238 + return new ManageAudienceClientImpl(
239 + buildRetrofitIface(apiEndPoint, ManageAudienceService.class));
240 + }
241 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.io.IOException;
20 +import java.util.concurrent.CompletableFuture;
21 +
22 +import com.fasterxml.jackson.databind.ObjectMapper;
23 +
24 +import com.linecorp.bot.client.LineMessagingClientImpl.VoidToBotApiCallbackAdaptor;
25 +import com.linecorp.bot.model.manageaudience.AudienceGroupCreateRoute;
26 +import com.linecorp.bot.model.manageaudience.AudienceGroupStatus;
27 +import com.linecorp.bot.model.manageaudience.request.AddAudienceToAudienceGroupRequest;
28 +import com.linecorp.bot.model.manageaudience.request.CreateAudienceGroupRequest;
29 +import com.linecorp.bot.model.manageaudience.request.CreateClickBasedAudienceGroupRequest;
30 +import com.linecorp.bot.model.manageaudience.request.CreateImpBasedAudienceGroupRequest;
31 +import com.linecorp.bot.model.manageaudience.request.UpdateAudienceGroupAuthorityLevelRequest;
32 +import com.linecorp.bot.model.manageaudience.request.UpdateAudienceGroupDescriptionRequest;
33 +import com.linecorp.bot.model.manageaudience.response.CreateAudienceGroupResponse;
34 +import com.linecorp.bot.model.manageaudience.response.CreateClickBasedAudienceGroupResponse;
35 +import com.linecorp.bot.model.manageaudience.response.CreateImpBasedAudienceGroupResponse;
36 +import com.linecorp.bot.model.manageaudience.response.GetAudienceDataResponse;
37 +import com.linecorp.bot.model.manageaudience.response.GetAudienceGroupAuthorityLevelResponse;
38 +import com.linecorp.bot.model.manageaudience.response.GetAudienceGroupsResponse;
39 +import com.linecorp.bot.model.oauth.ChannelAccessTokenException;
40 +import com.linecorp.bot.model.objectmapper.ModelObjectMapper;
41 +import com.linecorp.bot.model.response.BotApiResponse;
42 +
43 +import lombok.AllArgsConstructor;
44 +import retrofit2.Call;
45 +import retrofit2.Callback;
46 +import retrofit2.Response;
47 +import retrofit2.http.Body;
48 +
49 +/**
50 + * An implementation of {@link ManageAudienceClient} that issues or revokes channel access tokens.
51 + */
52 +@AllArgsConstructor
53 +class ManageAudienceClientImpl implements ManageAudienceClient {
54 + private static final ObjectMapper objectMapper = ModelObjectMapper.createNewObjectMapper();
55 +
56 + private final ManageAudienceService retrofitImpl;
57 +
58 + @Override
59 + public CompletableFuture<CreateAudienceGroupResponse> createAudienceGroup(
60 + CreateAudienceGroupRequest request) {
61 + return toFuture(retrofitImpl.createAudienceGroup(request));
62 + }
63 +
64 + @Override
65 + public CompletableFuture<BotApiResponse> addAudienceToAudienceGroup(
66 + AddAudienceToAudienceGroupRequest request) {
67 + return toBotApiFuture(retrofitImpl.addAudienceToAudienceGroup(request));
68 + }
69 +
70 + @Override
71 + public CompletableFuture<CreateClickBasedAudienceGroupResponse> createClickBasedAudienceGroup(
72 + CreateClickBasedAudienceGroupRequest request) {
73 + return toFuture(retrofitImpl.createClickBasedAudienceGroup(request));
74 + }
75 +
76 + @Override
77 + public CompletableFuture<CreateImpBasedAudienceGroupResponse> createImpBasedAudienceGroup(
78 + CreateImpBasedAudienceGroupRequest request) {
79 + return toFuture(retrofitImpl.createImpBasedAudienceGroup(request));
80 + }
81 +
82 + @Override
83 + public CompletableFuture<BotApiResponse> updateAudienceGroupDescription(
84 + long audienceGroupId, UpdateAudienceGroupDescriptionRequest request) {
85 + return toBotApiFuture(retrofitImpl.updateAudienceGroupDescription(audienceGroupId, request));
86 + }
87 +
88 + @Override
89 + public CompletableFuture<BotApiResponse> deleteAudienceGroup(long audienceGroupId) {
90 + return toBotApiFuture(retrofitImpl.deleteAudienceGroup(audienceGroupId));
91 + }
92 +
93 + @Override
94 + public CompletableFuture<GetAudienceGroupsResponse> getAudienceGroups(
95 + long page, String description, AudienceGroupStatus status, Long size,
96 + Boolean includesExternalPublicGroups, AudienceGroupCreateRoute createRoute) {
97 + return toFuture(retrofitImpl.getAudienceGroups(
98 + page, description, status, size,
99 + includesExternalPublicGroups, createRoute));
100 + }
101 +
102 + @Override
103 + public CompletableFuture<GetAudienceDataResponse> getAudienceData(long audienceGroupId) {
104 + return toFuture(retrofitImpl.getAudienceData(audienceGroupId));
105 + }
106 +
107 + @Override
108 + public CompletableFuture<GetAudienceGroupAuthorityLevelResponse> getAudienceGroupAuthorityLevel() {
109 + return toFuture(retrofitImpl.getAudienceGroupAuthorityLevel());
110 + }
111 +
112 + @Override
113 + public CompletableFuture<BotApiResponse> updateAudienceGroupAuthorityLevel(
114 + @Body UpdateAudienceGroupAuthorityLevelRequest request) {
115 + return toBotApiFuture(retrofitImpl.updateAudienceGroupAuthorityLevel(request));
116 + }
117 +
118 + private static <T> CompletableFuture<T> toFuture(Call<T> call) {
119 + final CallbackCompletableFuture<T> future = new CallbackCompletableFuture<>();
120 + call.enqueue(future);
121 + return future;
122 + }
123 +
124 + static class CallbackCompletableFuture<T> extends CompletableFuture<T> implements Callback<T> {
125 + @Override
126 + public void onResponse(final Call<T> call, final Response<T> response) {
127 + if (response.isSuccessful()) {
128 + complete(response.body());
129 + return;
130 + }
131 + if (response.code() == 400) {
132 + try {
133 + completeExceptionally(objectMapper.readValue(response.errorBody().string(),
134 + ChannelAccessTokenException.class));
135 + return;
136 + } catch (IOException e) {
137 + completeExceptionally(e);
138 + }
139 + }
140 + completeExceptionally(new ChannelAccessTokenException(response.message()));
141 + }
142 +
143 + @Override
144 + public void onFailure(final Call<T> call, final Throwable t) {
145 + completeExceptionally(new ChannelAccessTokenException(t.getMessage(), t));
146 + }
147 + }
148 +
149 + static CompletableFuture<BotApiResponse> toBotApiFuture(Call<Void> callToWrap) {
150 + final VoidToBotApiCallbackAdaptor completableFuture = new VoidToBotApiCallbackAdaptor();
151 + callToWrap.enqueue(completableFuture);
152 + return completableFuture;
153 + }
154 +}
1 +/*
2 + * Copyright 2020 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +package com.linecorp.bot.client;
17 +
18 +import com.linecorp.bot.model.manageaudience.AudienceGroupCreateRoute;
19 +import com.linecorp.bot.model.manageaudience.AudienceGroupStatus;
20 +import com.linecorp.bot.model.manageaudience.request.AddAudienceToAudienceGroupRequest;
21 +import com.linecorp.bot.model.manageaudience.request.CreateAudienceGroupRequest;
22 +import com.linecorp.bot.model.manageaudience.request.CreateClickBasedAudienceGroupRequest;
23 +import com.linecorp.bot.model.manageaudience.request.CreateImpBasedAudienceGroupRequest;
24 +import com.linecorp.bot.model.manageaudience.request.UpdateAudienceGroupAuthorityLevelRequest;
25 +import com.linecorp.bot.model.manageaudience.request.UpdateAudienceGroupDescriptionRequest;
26 +import com.linecorp.bot.model.manageaudience.response.CreateAudienceGroupResponse;
27 +import com.linecorp.bot.model.manageaudience.response.CreateClickBasedAudienceGroupResponse;
28 +import com.linecorp.bot.model.manageaudience.response.CreateImpBasedAudienceGroupResponse;
29 +import com.linecorp.bot.model.manageaudience.response.GetAudienceDataResponse;
30 +import com.linecorp.bot.model.manageaudience.response.GetAudienceGroupAuthorityLevelResponse;
31 +import com.linecorp.bot.model.manageaudience.response.GetAudienceGroupsResponse;
32 +
33 +import retrofit2.Call;
34 +import retrofit2.http.Body;
35 +import retrofit2.http.DELETE;
36 +import retrofit2.http.GET;
37 +import retrofit2.http.POST;
38 +import retrofit2.http.PUT;
39 +import retrofit2.http.Path;
40 +import retrofit2.http.Query;
41 +
42 +interface ManageAudienceService {
43 + @POST("v2/bot/audienceGroup/upload")
44 + Call<CreateAudienceGroupResponse> createAudienceGroup(@Body CreateAudienceGroupRequest request);
45 +
46 + @PUT("v2/bot/audienceGroup/upload")
47 + Call<Void> addAudienceToAudienceGroup(@Body AddAudienceToAudienceGroupRequest request);
48 +
49 + @POST("v2/bot/audienceGroup/click")
50 + Call<CreateClickBasedAudienceGroupResponse> createClickBasedAudienceGroup(
51 + @Body CreateClickBasedAudienceGroupRequest request);
52 +
53 + @POST("v2/bot/audienceGroup/imp")
54 + Call<CreateImpBasedAudienceGroupResponse> createImpBasedAudienceGroup(
55 + @Body CreateImpBasedAudienceGroupRequest request);
56 +
57 + @PUT("v2/bot/audienceGroup/{audienceGroupId}/updateDescription")
58 + Call<Void> updateAudienceGroupDescription(
59 + @Path("audienceGroupId") long audienceGroupId, @Body UpdateAudienceGroupDescriptionRequest request);
60 +
61 + @DELETE("v2/bot/audienceGroup/{audienceGroupId}")
62 + Call<Void> deleteAudienceGroup(@Path("audienceGroupId") long audienceGroupId);
63 +
64 + @GET("v2/bot/audienceGroup/{audienceGroupId}")
65 + Call<GetAudienceDataResponse> getAudienceData(@Path("audienceGroupId") long audienceGroupId);
66 +
67 + @GET("v2/bot/audienceGroup/list")
68 + Call<GetAudienceGroupsResponse> getAudienceGroups(
69 + @Query("page") long page,
70 + @Query("description") String description,
71 + @Query("status") AudienceGroupStatus status,
72 + @Query("size") Long size,
73 + @Query("includesExternalPublicGroups") Boolean includesExternalPublicGroups,
74 + @Query("createRoute") AudienceGroupCreateRoute createRoute);
75 +
76 + @GET("v2/bot/audienceGroup/authorityLevel")
77 + Call<GetAudienceGroupAuthorityLevelResponse> getAudienceGroupAuthorityLevel();
78 +
79 + @PUT("v2/bot/audienceGroup/authorityLevel")
80 + Call<Void> updateAudienceGroupAuthorityLevel(@Body UpdateAudienceGroupAuthorityLevelRequest request);
81 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.io.IOException;
20 +import java.io.InputStream;
21 +import java.util.List;
22 +import java.util.Map;
23 +
24 +import lombok.Builder;
25 +import lombok.Getter;
26 +import lombok.ToString;
27 +
28 +@Getter
29 +@Builder
30 +@ToString
31 +public class MessageContentResponse implements AutoCloseable {
32 + /** File size of this content. */
33 + final long length;
34 +
35 + /** File input stream of this content. */
36 + final InputStream stream;
37 +
38 + /** File contents type represented by MIME. */
39 + final String mimeType;
40 +
41 + /**
42 + * All HTTP headers of API response.
43 + *
44 + * <p>Note: there are no SPEC for those headers.
45 + * Current field values are provided AS-IS and can be changed/removed without announces.
46 + */
47 + final Map<String, List<String>> allHeaders;
48 +
49 + @Override
50 + public void close() throws IOException {
51 + stream.close();
52 + }
53 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client.exception;
18 +
19 +import com.linecorp.bot.model.error.ErrorResponse;
20 +
21 +public class BadRequestException extends LineMessagingException {
22 + private static final long serialVersionUID = SERIAL_VERSION_UID;
23 +
24 + public BadRequestException(
25 + final String message,
26 + final ErrorResponse errorResponse) {
27 + super(message, errorResponse, null);
28 + }
29 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client.exception;
18 +
19 +import com.linecorp.bot.model.error.ErrorResponse;
20 +
21 +public class ForbiddenException extends LineMessagingException {
22 + private static final long serialVersionUID = SERIAL_VERSION_UID;
23 +
24 + public ForbiddenException(
25 + final String message,
26 + final ErrorResponse errorResponse) {
27 + super(message, errorResponse, null);
28 + }
29 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client.exception;
18 +
19 +import com.linecorp.bot.model.error.ErrorResponse;
20 +
21 +/**
22 + * General exceptions both in api server and SDK internal exceptions.
23 + */
24 +public class GeneralLineMessagingException extends LineMessagingException {
25 + private static final long serialVersionUID = SERIAL_VERSION_UID;
26 +
27 + public GeneralLineMessagingException(
28 + final String message, final ErrorResponse errorResponse, final Throwable cause) {
29 + super(message, errorResponse, cause);
30 + }
31 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client.exception;
18 +
19 +import com.linecorp.bot.model.error.ErrorResponse;
20 +
21 +import lombok.Getter;
22 +
23 +@Getter
24 +public abstract class LineMessagingException extends Exception {
25 + static final long SERIAL_VERSION_UID = 0x001_003; // 1.3.x
26 + private static final long serialVersionUID = SERIAL_VERSION_UID;
27 +
28 + /**
29 + * Original error response from server.
30 + *
31 + * <p>Null when error response is not exist.
32 + */
33 + private final ErrorResponse errorResponse;
34 +
35 + LineMessagingException(final String message, final ErrorResponse errorResponse,
36 + final Throwable cause) {
37 + super(message + (errorResponse != null ? " : " + errorResponse : ""), cause);
38 + this.errorResponse = errorResponse;
39 + }
40 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client.exception;
18 +
19 +import com.linecorp.bot.model.error.ErrorResponse;
20 +
21 +public class LineServerException extends LineMessagingException {
22 + private static final long serialVersionUID = SERIAL_VERSION_UID;
23 +
24 + public LineServerException(
25 + final String message,
26 + final ErrorResponse errorResponse) {
27 + super(message, errorResponse, null);
28 + }
29 +}
1 +/*
2 + * Copyright 2018 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client.exception;
18 +
19 +import com.linecorp.bot.model.error.ErrorResponse;
20 +
21 +public class NotFoundException extends LineMessagingException {
22 + private static final long serialVersionUID = SERIAL_VERSION_UID;
23 +
24 + public NotFoundException(
25 + final String message,
26 + final ErrorResponse errorResponse) {
27 + super(message, errorResponse, null);
28 + }
29 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client.exception;
18 +
19 +import com.linecorp.bot.model.error.ErrorResponse;
20 +
21 +public class TooManyRequestsException extends LineMessagingException {
22 + private static final long serialVersionUID = SERIAL_VERSION_UID;
23 +
24 + public TooManyRequestsException(
25 + final String message,
26 + final ErrorResponse errorResponse) {
27 + super(message, errorResponse, null);
28 + }
29 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client.exception;
18 +
19 +import com.linecorp.bot.model.error.ErrorResponse;
20 +
21 +public class UnauthorizedException extends LineMessagingException {
22 + private static final long serialVersionUID = SERIAL_VERSION_UID;
23 +
24 + public UnauthorizedException(
25 + final String message,
26 + final ErrorResponse errorResponse) {
27 + super(message, errorResponse, null);
28 + }
29 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import java.net.URI;
20 +
21 +import org.junit.After;
22 +import org.junit.Before;
23 +import org.slf4j.bridge.SLF4JBridgeHandler;
24 +
25 +import com.fasterxml.jackson.databind.ObjectMapper;
26 +import com.fasterxml.jackson.databind.ObjectWriter;
27 +
28 +import com.linecorp.bot.model.error.ErrorResponse;
29 +
30 +import lombok.SneakyThrows;
31 +import okhttp3.mockwebserver.MockResponse;
32 +import okhttp3.mockwebserver.MockWebServer;
33 +
34 +public abstract class AbstractWiremockTest {
35 + public static final int ASYNC_TEST_TIMEOUT = 1_000;
36 + private static final ObjectWriter ERROR_RESPONSE_READER = new ObjectMapper().writerFor(ErrorResponse.class);
37 +
38 + static {
39 + SLF4JBridgeHandler.install();
40 + SLF4JBridgeHandler.removeHandlersForRootLogger();
41 + }
42 +
43 + protected MockWebServer mockWebServer;
44 + protected LineMessagingClient lineMessagingClient;
45 + protected LineBlobClient lineBlobClient;
46 + protected ChannelManagementSyncClient channelManagementSyncClient;
47 +
48 + @Before
49 + public void setUpWireMock() {
50 + mockWebServer = new MockWebServer();
51 + lineMessagingClient = createLineMessagingClient(mockWebServer);
52 + lineBlobClient = createLineBlobClient(mockWebServer);
53 + channelManagementSyncClient = createChannelManagementSyncClient(mockWebServer);
54 + }
55 +
56 + @After
57 + public void shutDownWireMock() throws Exception {
58 + mockWebServer.shutdown();
59 + }
60 +
61 + @SneakyThrows
62 + public void mocking(final int responseCode, final ErrorResponse errorResponse) {
63 + mockWebServer
64 + .enqueue(new MockResponse()
65 + .setResponseCode(responseCode)
66 + .setBody(ERROR_RESPONSE_READER.writeValueAsString(errorResponse)));
67 + }
68 +
69 + protected LineMessagingClient createLineMessagingClient(final MockWebServer mockWebServer) {
70 + return LineMessagingClient.builder("token")
71 + .apiEndPoint(URI.create("http://localhost:" + mockWebServer.getPort()))
72 + .blobEndPoint(URI.create("http://localhost:" + mockWebServer.getPort()))
73 + .build();
74 + }
75 +
76 + protected LineBlobClient createLineBlobClient(MockWebServer mockWebServer) {
77 + return LineBlobClient.builder("token")
78 + .apiEndPoint(URI.create("http://localhost:" + mockWebServer.getPort()))
79 + .build();
80 + }
81 +
82 + protected ChannelManagementSyncClient createChannelManagementSyncClient(final MockWebServer mockWebServer) {
83 + return ChannelManagementSyncClient
84 + .builder(() -> "token")
85 + .apiEndPoint(URI.create("http://localhost:" + mockWebServer.getPort()))
86 + .build();
87 + }
88 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static okhttp3.MediaType.parse;
20 +import static org.assertj.core.api.Assertions.assertThat;
21 +import static org.mockito.Mockito.mock;
22 +import static org.mockito.Mockito.when;
23 +
24 +import org.junit.Before;
25 +import org.junit.Rule;
26 +import org.junit.Test;
27 +import org.junit.rules.Timeout;
28 +import org.mockito.Mock;
29 +import org.mockito.junit.MockitoJUnit;
30 +import org.mockito.junit.MockitoRule;
31 +
32 +import com.linecorp.bot.client.LineMessagingClientImpl.CallbackAdaptor;
33 +import com.linecorp.bot.client.exception.GeneralLineMessagingException;
34 +
35 +import okhttp3.ResponseBody;
36 +import retrofit2.Call;
37 +import retrofit2.Response;
38 +
39 +public class CallbackAdaptorTest {
40 + private CallbackAdaptor<Object> target;
41 +
42 + @Rule
43 + public final MockitoRule mockitoRule = MockitoJUnit.rule();
44 +
45 + @Rule
46 + public final Timeout timeoutRule = Timeout.seconds(10);
47 +
48 + @Mock
49 + private Call<Object> call;
50 +
51 + @Before
52 + public void setUp() throws Exception {
53 + target = new CallbackAdaptor<>();
54 + }
55 +
56 + @Test
57 + public void onResponseSuccessfullyTest() throws Exception {
58 + final Object value = new Object();
59 + Response<Object> response = Response.success(value);
60 +
61 + // Do
62 + target.onResponse(call, response);
63 +
64 + // Verify
65 + assertThat(target).isCompletedWithValue(value);
66 + }
67 +
68 + @Test
69 + public void onResponseWithErrorTest() throws Exception {
70 + Response<Object> response =
71 + Response.error(400, ResponseBody.create(parse("application/json"), "{}"));
72 +
73 + // Do
74 + target.onResponse(call, response);
75 +
76 + // Verify
77 + assertThat(target).isCompletedExceptionally();
78 + }
79 +
80 + @Test
81 + public void onFailureTest() throws Exception {
82 + Throwable t = mock(Throwable.class);
83 + when(t.getMessage()).thenReturn("Message");
84 +
85 + // Do
86 + target.onFailure(call, t);
87 +
88 + // Verify
89 + assertThat(target).isCompletedExceptionally();
90 + assertThat(target.handle((ignored, e) -> e).get())
91 + .isInstanceOf(GeneralLineMessagingException.class)
92 + .hasMessageContaining("Message");
93 + }
94 +}
1 +/*
2 + * Copyright 2018 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static org.assertj.core.api.Assertions.assertThat;
20 +
21 +import java.net.URI;
22 +import java.nio.charset.StandardCharsets;
23 +
24 +import org.junit.Test;
25 +
26 +import com.fasterxml.jackson.databind.ObjectMapper;
27 +
28 +import com.linecorp.bot.liff.LiffView;
29 +import com.linecorp.bot.liff.LiffView.Type;
30 +import com.linecorp.bot.liff.request.LiffAppAddRequest;
31 +import com.linecorp.bot.liff.response.LiffAppAddResponse;
32 +import com.linecorp.bot.model.objectmapper.ModelObjectMapper;
33 +
34 +import okhttp3.mockwebserver.MockResponse;
35 +import okhttp3.mockwebserver.RecordedRequest;
36 +
37 +public class ChannelManagementSyncClientIntegrationWiremockTest
38 + extends AbstractWiremockTest {
39 + private static final ObjectMapper OBJECT_MAPPER = ModelObjectMapper.createNewObjectMapper();
40 +
41 + @Test(timeout = ASYNC_TEST_TIMEOUT)
42 + public void testAddLiffMenu() throws Exception {
43 + // Mocking
44 + LiffAppAddResponse response = new LiffAppAddResponse("NEW_LIFF_ID");
45 + mockWebServer.enqueue(new MockResponse().setResponseCode(200)
46 + .setBody(OBJECT_MAPPER.writeValueAsString(response)));
47 +
48 + // Do
49 + LiffView liffView = new LiffView(Type.COMPACT, URI.create("https://example.com"));
50 + LiffAppAddRequest request = new LiffAppAddRequest(liffView);
51 + final LiffAppAddResponse liffAppAddResponse = channelManagementSyncClient.addLiffApp(request);
52 +
53 + // Verify
54 + final RecordedRequest recordedRequest = mockWebServer.takeRequest();
55 + final LiffAppAddRequest requestedBody = OBJECT_MAPPER
56 + .readValue(recordedRequest.getBody().readString(StandardCharsets.UTF_8),
57 + LiffAppAddRequest.class);
58 + assertThat(requestedBody)
59 + .isEqualTo(request);
60 + assertThat(recordedRequest.getPath())
61 + .isEqualTo("/liff/v1/apps");
62 + assertThat(liffAppAddResponse.getLiffId())
63 + .isEqualTo("NEW_LIFF_ID");
64 + }
65 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static org.assertj.core.api.Assertions.assertThat;
20 +import static org.mockito.Mockito.mock;
21 +import static org.mockito.Mockito.when;
22 +
23 +import org.junit.Test;
24 +
25 +import com.linecorp.bot.client.exception.GeneralLineMessagingException;
26 +import com.linecorp.bot.client.exception.LineMessagingException;
27 +import com.linecorp.bot.client.exception.UnauthorizedException;
28 +
29 +import okhttp3.MediaType;
30 +import okhttp3.Protocol;
31 +import okhttp3.Request;
32 +import okhttp3.Response.Builder;
33 +import okhttp3.ResponseBody;
34 +import retrofit2.Response;
35 +
36 +public class ExceptionConverterTest {
37 + private final ExceptionConverter target = new ExceptionConverter();
38 +
39 + @Test
40 + public void convertTest() {
41 + final ResponseBody responseBody =
42 + ResponseBody.create(MediaType.parse("application/json"),
43 + "{}");
44 + final LineMessagingException result =
45 + target.apply(Response.error(401, responseBody));
46 +
47 + assertThat(result)
48 + .isInstanceOf(UnauthorizedException.class);
49 + }
50 +
51 + @Test
52 + public void convertUnknownExceptionTest() {
53 + final ResponseBody responseBody =
54 + ResponseBody.create(MediaType.parse("application/json"),
55 + "{}");
56 + final LineMessagingException result =
57 + target.apply(Response.error(999, responseBody));
58 +
59 + assertThat(result)
60 + .isInstanceOf(GeneralLineMessagingException.class);
61 + }
62 +
63 + @Test
64 + public void exceptionInConvertFallbackedTest() {
65 + final ResponseBody responseBody = mock(ResponseBody.class);
66 + when(responseBody.source()).thenThrow(new RuntimeException());
67 +
68 + final LineMessagingException result =
69 + target.apply(Response.error(401, responseBody));
70 +
71 + assertThat(result)
72 + .isInstanceOf(GeneralLineMessagingException.class);
73 + }
74 +
75 + @Test
76 + public void requestIdDeserializationTest() {
77 + final ResponseBody responseBody =
78 + ResponseBody.create(MediaType.parse("application/json"),
79 + "{\"message\":\"Invalid reply token\"}");
80 +
81 + final okhttp3.Response rawResponse = new Builder()
82 + .code(400)
83 + .message("")
84 + .request(new Request.Builder().get().url("https://api.line.me/v2/bot/message/reply").build())
85 + .addHeader("X-Line-Request-Id", "5ac44e02-e6be-49c3-a55f-6b2a29bc3aa4")
86 + .protocol(Protocol.HTTP_1_1)
87 + .build();
88 +
89 + // Precondition
90 + assertThat(rawResponse.header("X-Line-Request-Id")).isEqualTo("5ac44e02-e6be-49c3-a55f-6b2a29bc3aa4");
91 +
92 + // Do
93 + final LineMessagingException result =
94 + target.apply(Response.error(responseBody, rawResponse));
95 +
96 + // Verify
97 + assertThat(result.getErrorResponse().getRequestId()).isEqualTo("5ac44e02-e6be-49c3-a55f-6b2a29bc3aa4");
98 + }
99 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static org.assertj.core.api.Assertions.assertThat;
20 +import static org.assertj.core.api.Assertions.fail;
21 +
22 +import org.junit.Test;
23 +
24 +public class FixedChannelTokenSupplierTest {
25 + @Test(expected = NullPointerException.class)
26 + public void constructedInstanceAlwaysNonNullTest() {
27 + // Do
28 + FixedChannelTokenSupplier.of(null);
29 +
30 + // Verify
31 + fail("NullPointerException is not occurred.");
32 + }
33 +
34 + @Test
35 + public void getTest() {
36 + ChannelTokenSupplier target = FixedChannelTokenSupplier.of("FIXED_TOKEN");
37 +
38 + // DO
39 + String result = target.get();
40 +
41 + // Verify
42 + assertThat(result).isEqualTo("FIXED_TOKEN");
43 + }
44 +}
1 +/*
2 + * Copyright 2016 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static java.util.Collections.singletonList;
20 +import static org.assertj.core.api.Assertions.assertThat;
21 +import static org.mockito.Mockito.when;
22 +
23 +import java.net.URI;
24 +
25 +import org.junit.Rule;
26 +import org.junit.Test;
27 +import org.mockito.Mock;
28 +import org.mockito.junit.MockitoJUnit;
29 +import org.mockito.junit.MockitoRule;
30 +
31 +import okhttp3.mockwebserver.MockWebServer;
32 +import okhttp3.mockwebserver.RecordedRequest;
33 +
34 +public class HeaderInterceptorWireMockTest extends AbstractWiremockTest {
35 + @Rule
36 + public final MockitoRule mockitoRule = MockitoJUnit.rule();
37 +
38 + @Mock
39 + ChannelTokenSupplier channelTokenSupplier;
40 +
41 + @Test(timeout = ASYNC_TEST_TIMEOUT)
42 + public void forChannelTokenSupplier() throws Exception {
43 + // Do
44 + when(channelTokenSupplier.get()).thenReturn("1st");
45 + lineMessagingClient.getProfile("TEST");
46 +
47 + // Verify
48 + final RecordedRequest request1st = mockWebServer.takeRequest();
49 + assertThat(request1st.getHeaders().toMultimap())
50 + .containsEntry("Authorization", singletonList("Bearer 1st"));
51 +
52 + // Do again with another channel token.
53 + when(channelTokenSupplier.get()).thenReturn("2nd");
54 + lineMessagingClient.getProfile("TEST");
55 +
56 + // Verify
57 + final RecordedRequest request2nd = mockWebServer.takeRequest();
58 + assertThat(request2nd.getHeaders().toMultimap())
59 + .containsEntry("Authorization", singletonList("Bearer 2nd"));
60 + }
61 +
62 + @Override
63 + protected LineMessagingClient createLineMessagingClient(final MockWebServer mockWebServer) {
64 + return LineMessagingClient.builder(channelTokenSupplier)
65 + .apiEndPoint(URI.create("http://localhost:" + mockWebServer.getPort()))
66 + .build();
67 + }
68 +}
1 +/*
2 + * Copyright 2019 LINE Corporation
3 + *
4 + * LINE Corporation licenses this file to you under the Apache License,
5 + * version 2.0 (the "License"); you may not use this file except in compliance
6 + * with the License. You may obtain a copy of the License at:
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 + * License for the specific language governing permissions and limitations
14 + * under the License.
15 + */
16 +
17 +package com.linecorp.bot.client;
18 +
19 +import static java.util.Collections.emptyList;
20 +import static java.util.Collections.singletonMap;
21 +import static org.assertj.core.api.Assertions.assertThat;
22 +import static org.mockito.ArgumentMatchers.any;
23 +import static org.mockito.ArgumentMatchers.eq;
24 +import static org.mockito.Mockito.only;
25 +import static org.mockito.Mockito.verify;
26 +import static org.mockito.Mockito.when;
27 +
28 +import java.io.IOException;
29 +
30 +import org.junit.Rule;
31 +import org.junit.Test;
32 +import org.junit.rules.Timeout;
33 +import org.mockito.InjectMocks;
34 +import org.mockito.Mock;
35 +import org.mockito.junit.MockitoJUnit;
36 +import org.mockito.junit.MockitoRule;
37 +import org.mockito.stubbing.OngoingStubbing;
38 +
39 +import com.linecorp.bot.model.response.BotApiResponse;
40 +
41 +import okhttp3.Headers;
42 +import okhttp3.MediaType;
43 +import okhttp3.Request;
44 +import okhttp3.ResponseBody;
45 +import retrofit2.Call;
46 +import retrofit2.Callback;
47 +import retrofit2.Response;
48 +
49 +public class LineBlobClientImplTest {
50 + private static final byte[] ZERO_BYTES = {};
51 + private static final String REQUEST_ID_FIXTURE = "REQUEST_ID_FIXTURE";
52 + private static final BotApiResponseBody BOT_API_SUCCESS_RESPONSE_BODY =
53 + new BotApiResponseBody("", emptyList());
54 + private static final BotApiResponse BOT_API_SUCCESS_RESPONSE =
55 + BOT_API_SUCCESS_RESPONSE_BODY.withRequestId(REQUEST_ID_FIXTURE);
56 +
57 + @Rule
58 + public final MockitoRule mockitoRule = MockitoJUnit.rule();
59 +
60 + @Rule
61 + public final Timeout timeoutRule = Timeout.seconds(5);
62 +
63 + @Mock
64 + private LineBlobService retrofitMock;
65 +
66 + @InjectMocks
67 + private LineBlobClientImpl target;
68 +
69 + @Test
70 + public void getMessageContentTest() throws Exception {
71 + whenCall(retrofitMock.getMessageContent(any()),
72 + ResponseBody.create(MediaType.parse("image/jpeg"), ZERO_BYTES));
73 +
74 + // Do
75 + final MessageContentResponse contentResponse = target.getMessageContent("ID").get();
76 +
77 + // Verify
78 + verify(retrofitMock, only()).getMessageContent("ID");
79 + assertThat(contentResponse.getLength()).isEqualTo(0);
80 + assertThat(contentResponse.getMimeType()).isEqualTo("image/jpeg");
81 + }
82 +
83 + @Test
84 + public void getRichMenuImageTest() throws Exception {
85 + whenCall(retrofitMock.getRichMenuImage(any()),
86 + ResponseBody.create(MediaType.parse("image/jpeg"), ZERO_BYTES));
87 +
88 + // Do
89 + final MessageContentResponse messageContentResponse = target.getRichMenuImage("ID").get();
90 +
91 + // Verify
92 + verify(retrofitMock, only()).getRichMenuImage("ID");
93 + assertThat(messageContentResponse.getLength()).isZero();
94 + }
95 +
96 + @Test
97 + public void uploadRichMenuImageTest() throws Exception {
98 + whenCall(retrofitMock.uploadRichMenuImage(any(), any()),
99 + null);
100 +
101 + // Do
102 + final BotApiResponse botApiResponse =
103 + target.setRichMenuImage("ID", "image/jpeg", ZERO_BYTES).get();
104 +
105 + // Verify
106 + verify(retrofitMock, only())
107 + .uploadRichMenuImage(eq("ID"), any());
108 + assertThat(botApiResponse).isEqualTo(BOT_API_SUCCESS_RESPONSE);
109 +
110 + }
111 +
112 + // Utility methods
113 +
114 + private static <T> void whenCall(Call<T> call, T value) {
115 + final OngoingStubbing<Call<T>> callOngoingStubbing = when(call);
116 + callOngoingStubbing.thenReturn(enqueue(value));
117 + }
118 +
119 + private static <T> Call<T> enqueue(T value) {
120 + return new Call<T>() {
121 + @Override
122 + public Response<T> execute() throws IOException {
123 + throw new UnsupportedOperationException();
124 + }
125 +
126 + @Override
127 + public void enqueue(Callback<T> callback) {
128 + final Headers headers = Headers.of(singletonMap("x-line-request-id", REQUEST_ID_FIXTURE));
129 + callback.onResponse(this, Response.success(value, headers));
130 + }
131 +
132 + @Override
133 + public boolean isExecuted() {
134 + throw new UnsupportedOperationException();
135 + }
136 +
137 + @Override
138 + public void cancel() {
139 + throw new UnsupportedOperationException();
140 + }
141 +
142 + @Override
143 + public boolean isCanceled() {
144 + throw new UnsupportedOperationException();
145 + }
146 +
147 + @Override
148 + public Call<T> clone() {
149 + throw new UnsupportedOperationException();
150 + }
151 +
152 + @Override
153 + public Request request() {
154 + throw new UnsupportedOperationException();
155 + }
156 + };
157 + }
158 +}
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.