최현준

line-bot-sdk-java reset

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