diff --git a/android/.gitignore b/.gitignore similarity index 59% rename from android/.gitignore rename to .gitignore index 648a292a7..287e67fa5 100644 --- a/android/.gitignore +++ b/.gitignore @@ -27,3 +27,20 @@ proguard/ *.ipr *.iws .idea/ + +*.class + +# gwt caches and compiled units # +war/gwt_bree/ +gwt-unitCache/ + +# boilerplate generated classes # +.apt_generated/ + +# more caches and things from deploy # +war/WEB-INF/deploy/ +war/WEB-INF/classes/ + +target +build +.gradle diff --git a/.pubnub.yml b/.pubnub.yml new file mode 100644 index 000000000..cdc3657c3 --- /dev/null +++ b/.pubnub.yml @@ -0,0 +1,93 @@ +name: java +version: 4.0.8 +schema: 1 +scm: github.com/pubnub/java +changelog: + - version: v4.0.8 + date: + changes: + - type: feature + text: added unsubscribeAll, getSubscribedChannels, getSubscribedChannelGroups + - type: feature + text: SDK will establish secure connections by default + - type: feature + text: added support for exponential backoff reconnection policies + - version: v4.0.7 + date: + changes: + - type: improvement + text: reduce overlap on error handling when returning exceptions. + - version: v4.0.6 + changes: + - type: improvement + text: send heartbeat presence value when subscribing + - version: v4.0.5 + changes: + - type: improvement + text: unified retrofit handling to lower amount of instances and sync'd the state methods. + - version: v4.0.4 + changes: + - type: bug + text: setting State for other UUID's is now supported. + - version: v4.0.3 + changes: + - type: feature + text: fire() method and no-replicaton options. + - version: v4.0.2 + changes: + - type: bug + text: fix to the version fetching. + - version: v4.0.1 + changes: + - type: bug + text: adjustment of the subscribe loop to alleviate duplicate dispatches. + - version: v4.0.0 + changes: + - type: bug + text: first GA. + - version: v4.0.0-beta4 + changes: + - type: improvement + text: reconnects and minor adjustments. + - version: v4.0.0-beta3 + changes: + - type: bug + text: fixing state not coming on the subscriber callback. + - type: bug + text: adjustments to URL encoding on publish, subscribe, set-state operations to avoid double encoding with retrofit. + - version: v4.0.0-beta2 + changes: + - type: improvement + text: reworking of message queue. + - type: improvement + text: checkstyle, findbugs. + - type: improvement + text: reworking error notifications. + - version: v4.0.0-beta1 + changes: + - type: improvement + text: initial beta1. +features: + access: + - GRANT + - AUDIT + channel-groups: + - ADD-CHANNELS + - REMOVE-CHANNELS + - LIST-GROUPS + - LIST-CHANNELS-IN-GROUP + push: + - ADD-DEVICE-TO-CHANNELS + - REMOVE-DEVICE-FROM-CHANNELS + - LIST-CHANNELS-FROM-DEVICE + - REMOVE-DEVICE + presence: + - HERE-NOW + - WHERE-NOW + - SET-STATE + - GET-STATE + - HEARTBEAT + publish: + - STORE-FLAG + - FIRE + - REPLICATION-FLAG diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..cee27a448 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: java +sudo: false +jdk: + - oraclejdk7 + - oraclejdk8 +after_success: + - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..f8ba805bc --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,149 @@ + +## [v4.0.8](https://github.com/pubnub/java/tree/v4.0.8) + + + [Full Changelog](https://github.com/pubnub/java/compare/v4.0.7...v4.0.8) + +- 🌟added unsubscribeAll, getSubscribedChannels, getSubscribedChannelGroups + + + +- 🌟SDK will establish secure connections by default + + + +- 🌟added support for exponential backoff reconnection policies + + + + +## [v4.0.7](https://github.com/pubnub/java/tree/v4.0.7) + + + [Full Changelog](https://github.com/pubnub/java/compare/v4.0.6...v4.0.7) + + +- ⭐reduce overlap on error handling when returning exceptions. + + + +## [v4.0.6](https://github.com/pubnub/java/tree/v4.0.6) + + + [Full Changelog](https://github.com/pubnub/java/compare/v4.0.5...v4.0.6) + + +- ⭐send heartbeat presence value when subscribing + + + +## [v4.0.5](https://github.com/pubnub/java/tree/v4.0.5) + + + [Full Changelog](https://github.com/pubnub/java/compare/v4.0.4...v4.0.5) + + +- ⭐unified retrofit handling to lower amount of instances and sync'd the state methods. + + + +## [v4.0.4](https://github.com/pubnub/java/tree/v4.0.4) + + + [Full Changelog](https://github.com/pubnub/java/compare/v4.0.3...v4.0.4) + + + +- 🐛setting State for other UUID's is now supported. + + +## [v4.0.3](https://github.com/pubnub/java/tree/v4.0.3) + + + [Full Changelog](https://github.com/pubnub/java/compare/v4.0.2...v4.0.3) + +- 🌟fire() method and no-replicaton options. + + + + +## [v4.0.2](https://github.com/pubnub/java/tree/v4.0.2) + + + [Full Changelog](https://github.com/pubnub/java/compare/v4.0.1...v4.0.2) + + + +- 🐛fix to the version fetching. + + +## [v4.0.1](https://github.com/pubnub/java/tree/v4.0.1) + + + [Full Changelog](https://github.com/pubnub/java/compare/v4.0.0...v4.0.1) + + + +- 🐛adjustment of the subscribe loop to alleviate duplicate dispatches. + + +## [v4.0.0](https://github.com/pubnub/java/tree/v4.0.0) + + + [Full Changelog](https://github.com/pubnub/java/compare/v4.0.0-beta4...v4.0.0) + + + +- 🐛first GA. + + +## [v4.0.0-beta4](https://github.com/pubnub/java/tree/v4.0.0-beta4) + + + [Full Changelog](https://github.com/pubnub/java/compare/v4.0.0-beta3...v4.0.0-beta4) + + +- ⭐reconnects and minor adjustments. + + + +## [v4.0.0-beta3](https://github.com/pubnub/java/tree/v4.0.0-beta3) + + + [Full Changelog](https://github.com/pubnub/java/compare/v4.0.0-beta2...v4.0.0-beta3) + + + +- 🐛fixing state not coming on the subscriber callback. + + + +- 🐛adjustments to URL encoding on publish, subscribe, set-state operations to avoid double encoding with retrofit. + + +## [v4.0.0-beta2](https://github.com/pubnub/java/tree/v4.0.0-beta2) + + + [Full Changelog](https://github.com/pubnub/java/compare/v4.0.0-beta1...v4.0.0-beta2) + + +- ⭐reworking of message queue. + + + +- ⭐checkstyle, findbugs. + + + +- ⭐reworking error notifications. + + + +## [v4.0.0-beta1](https://github.com/pubnub/java/tree/v4.0.0-beta1) + + + + +- ⭐initial beta1. + + diff --git a/DEVELOPER.md b/DEVELOPER.md new file mode 100644 index 000000000..81e4ea701 --- /dev/null +++ b/DEVELOPER.md @@ -0,0 +1,17 @@ + +### Installing Dependencies + * Gradle [https://docs.gradle.org/current/userguide/installation.html] + * Lombok Plugins: + * [intellij](https://plugins.jetbrains.com/plugin/6317) -- [installation guide](https://github.com/mplushnikov/lombok-intellij-plugin#installation) + * [eclipse](http://stackoverflow.com/questions/22310414/how-to-configure-lombok-in-eclipse-luna) + +### Compiling + `gradle clean compile` + +### Building a shadowJar (Fat Jar) + `gradle clean build shadowJar` + +### deploying to nexus + * enable the javadoc documentation + * `gradle clean build javadoc upload` + * enable the new package on sonatype diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..3efa3922e --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +PubNub Real-time Cloud-Hosted Push API and Push Notification Client Frameworks +Copyright (c) 2013 PubNub Inc. +http://www.pubnub.com/ +http://www.pubnub.com/terms + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +PubNub Real-time Cloud-Hosted Push API and Push Notification Client Frameworks +Copyright (c) 2013 PubNub Inc. +http://www.pubnub.com/ +http://www.pubnub.com/terms diff --git a/README.md b/README.md new file mode 100644 index 000000000..018917129 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ + + +### PubNub Java-based SDKs for Java / Android + + +[![Build Status](https://travis-ci.org/pubnub/java.svg?branch=master)](https://travis-ci.org/pubnub/java) +[![codecov.io](https://codecov.io/github/pubnub/java/coverage.svg?branch=edge)](https://codecov.io/github/pubnub/java?branch=edge) +[![Download](https://api.bintray.com/packages/bintray/jcenter/com.pubnub%3Apubnub/images/download.svg)](https://bintray.com/bintray/jcenter/com.pubnub%3Apubnub/_latestVersion) +[![Maven Central](https://img.shields.io/maven-central/v/com.pubnub/pubnub.svg)]() + +### [Documentation](https://www.pubnub.com/docs/java/pubnub-java-sdk-v4) + +## Communication + +- If you **need help** or have a **general question**, contact diff --git a/VERSION b/VERSION new file mode 100644 index 000000000..a7d0a2eda --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +4.0.8 \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..36324df65 --- /dev/null +++ b/build.gradle @@ -0,0 +1,128 @@ +plugins { + id 'io.franzbecker.gradle-lombok' version '1.6' + id 'com.github.johnrengelman.shadow' version '1.2.3' + id "com.bmuschko.nexus" version "2.3.1" + id 'java' + id 'jacoco' + id 'maven' + id 'checkstyle' + id 'findbugs' +} +group = 'com.pubnub' +version = '4.0.8' + +description = """""" + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 + +configurations.all { +} + +repositories { + + maven { url "https://oss.sonatype.org/content/repositories/snapshots" } + maven { url "http://repo.maven.apache.org/maven2" } + maven { url "https://plugins.gradle.org/m2/" } +} + +dependencies { + compile group: 'com.squareup.retrofit2', name: 'retrofit', version:'2.0.2' + compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version:'2.7.3' + compile group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version:'2.7.3' + compile group: 'com.squareup.retrofit2', name: 'converter-jackson', version:'2.0.2' + compile group: 'com.squareup.okhttp3', name: 'logging-interceptor', version:'3.2.0' + compile group: 'org.slf4j', name: 'slf4j-nop', version:'1.7.21' + + testCompile group: 'ch.qos.logback', name: 'logback-classic', version:'1.1.7' + testCompile group: 'ch.qos.logback', name: 'logback-core', version:'1.1.7' + testCompile group: 'junit', name: 'junit', version:'4.12' + testCompile group: 'com.github.tomakehurst', name: 'wiremock', version:'1.58' + testCompile group: 'org.hamcrest', name: 'hamcrest-all', version:'1.3' + testCompile group: 'com.jayway.awaitility', name: 'awaitility', version:'1.7.0' +} + +jacoco { + toolVersion = "0.7.6.201602180812" +} + +jacocoTestReport { + reports { + xml.enabled = true + html.enabled = true + } +} + +checkstyle { + configFile = new File(rootDir, "src/test/checkstyle.xml") + toolVersion = '6.17' +} + +tasks.withType(Checkstyle) { + + exclude '**/vendor/**', "**/*Test*" + + reports { + xml.enabled = true + html.enabled = true + } +} + +tasks.withType(FindBugs) { + + exclude '**/vendor/**', "**/*Test*" + + reports { + xml.enabled false + html.enabled true + } +} + +check.dependsOn jacocoTestReport + +processResources { + expand projectVersion: project.version +} + +extraArchive { + sources = true + tests = true + javadoc = false // enable when uploading to nexus : ). +} + +nexus { + sign = true + repositoryUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/' + snapshotRepositoryUrl = 'https://oss.sonatype.org/content/repositories/snapshots' +} + +modifyPom { + project { + name 'PubNub Java SDK' + description 'PubNub is a cross-platform client-to-client (1:1 and 1:many) push service in the cloud, capable of\n' + + ' broadcasting real-time messages to millions of web and mobile clients simultaneously, in less than a quarter\n' + + ' second!' + url 'https://github.com/pubnub/java' + inceptionYear '2009' + + scm { + url 'https://github.com/pubnub/java' + } + + licenses { + license { + name 'MIT License' + url 'https://github.com/pubnub/pubnub-api/blob/master/LICENSE' + distribution 'repo' + } + } + + developers { + developer { + id 'PubNub' + name 'PubNub' + email 'support@pubnub.com' + } + } + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..2c6137b87 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..cd6c5dbb2 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun May 01 11:48:10 PDT 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 000000000..9d82f7891 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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 +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# 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 + +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" ] ; 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, switch paths to Windows format before running java +if $cygwin ; 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=$((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 + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..72d362daf --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@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 + +@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= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@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 +if "%@eval[2+2]" == "4" goto 4NT_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=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +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 diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..ea7c5dc87 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'pubnub' diff --git a/src/main/java/com/pubnub/api/PNConfiguration.java b/src/main/java/com/pubnub/api/PNConfiguration.java new file mode 100644 index 000000000..ebc9362c5 --- /dev/null +++ b/src/main/java/com/pubnub/api/PNConfiguration.java @@ -0,0 +1,144 @@ +package com.pubnub.api; + + +import com.pubnub.api.enums.PNHeartbeatNotificationOptions; +import com.pubnub.api.enums.PNLogVerbosity; +import com.pubnub.api.enums.PNReconnectionPolicy; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.util.UUID; + +@Getter +@Setter +@Accessors(chain = true) +public class PNConfiguration { + private static final int PRESENCE_TIMEOUT = 300; + private static final int NON_SUBSCRIBE_REQUEST_TIMEOUT = 10; + private static final int SUBSCRIBE_TIMEOUT = 310; + private static final int CONNECT_TIMEOUT = 5; + + /** + * By default, the origin is pointing directly to PubNub servers. If a proxy origin is needed, set a custom + * origin using this parameter. + */ + private String origin; + private int subscribeTimeout; + + + /** + * In seconds, how long the server will consider this client to be online before issuing a leave event. + */ + @Setter(AccessLevel.NONE) + private int presenceTimeout; + /** + * In seconds, How often the client should announce it's existence via heartbeating. + */ + @Setter(AccessLevel.NONE) + private int heartbeatInterval; + + /** + * set to true to switch the client to HTTPS:// based communications. + */ + private boolean secure; + /** + * Subscribe Key provided by PubNub + */ + private String subscribeKey; + /** + * Publish Key provided by PubNub. + */ + private String publishKey; + private String secretKey; + private String cipherKey; + private String authKey; + private String uuid; + /** + * If proxies are forcefully caching requests, set to true to allow the client to randomize the subdomain. + * This configuration is not supported if custom origin is enabled. + */ + @Deprecated + private boolean cacheBusting; + + /** + * toggle to enable verbose logging. + */ + + private PNLogVerbosity logVerbosity; + + /** + * Stores the maximum number of seconds which the client should wait for connection before timing out. + */ + private int connectTimeout; + + /** + * Reference on number of seconds which is used by client during non-subscription operations to + * check whether response potentially failed with 'timeout' or not. + */ + private int nonSubscribeRequestTimeout; + + /** + * verbosity of heartbeat configuration, by default only alerts on failed heartbeats + */ + private PNHeartbeatNotificationOptions heartbeatNotificationOptions; + + /** + * filterExpression used as part of PSV2 specification. + */ + @Setter + private String filterExpression; + + + /** + * Reconnection policy which will be used if/when networking goes down + */ + @Setter + private PNReconnectionPolicy reconnectionPolicy; + + /** + * Initialize the PNConfiguration with default values + */ + public PNConfiguration() { + setPresenceTimeout(PRESENCE_TIMEOUT); + + uuid = UUID.randomUUID().toString(); + + nonSubscribeRequestTimeout = NON_SUBSCRIBE_REQUEST_TIMEOUT; + subscribeTimeout = SUBSCRIBE_TIMEOUT; + connectTimeout = CONNECT_TIMEOUT; + + logVerbosity = PNLogVerbosity.NONE; + + heartbeatNotificationOptions = PNHeartbeatNotificationOptions.FAILURES; + reconnectionPolicy = PNReconnectionPolicy.NONE; + + secure = true; + } + + /** + * set presence configurations for timeout and announce interval. + * + * @param timeout presence timeout; how long before the server considers this client to be gone. + * @param interval presence announce interval, how often the client should announce itself. + * @return returns itself. + */ + public PNConfiguration setPresenceTimeoutWithCustomInterval(final int timeout, final int interval) { + this.presenceTimeout = timeout; + this.heartbeatInterval = interval; + + return this; + } + + /** + * set presence configurations for timeout and allow the client to pick the best interval + * + * @param timeout presence timeout; how long before the server considers this client to be gone. + * @return returns itself. + */ + public PNConfiguration setPresenceTimeout(final int timeout) { + return setPresenceTimeoutWithCustomInterval(timeout, (timeout / 2) - 1); + } + +} diff --git a/src/main/java/com/pubnub/api/PubNub.java b/src/main/java/com/pubnub/api/PubNub.java new file mode 100644 index 000000000..0114f32ba --- /dev/null +++ b/src/main/java/com/pubnub/api/PubNub.java @@ -0,0 +1,268 @@ +package com.pubnub.api; + +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.builder.SubscribeBuilder; +import com.pubnub.api.builder.UnsubscribeBuilder; +import com.pubnub.api.endpoints.History; +import com.pubnub.api.endpoints.Time; +import com.pubnub.api.endpoints.access.Audit; +import com.pubnub.api.endpoints.access.Grant; +import com.pubnub.api.endpoints.channel_groups.AddChannelChannelGroup; +import com.pubnub.api.endpoints.channel_groups.AllChannelsChannelGroup; +import com.pubnub.api.endpoints.channel_groups.DeleteChannelGroup; +import com.pubnub.api.endpoints.channel_groups.RemoveChannelChannelGroup; +import com.pubnub.api.endpoints.channel_groups.ListAllChannelGroup; +import com.pubnub.api.endpoints.presence.GetState; +import com.pubnub.api.endpoints.presence.HereNow; +import com.pubnub.api.endpoints.presence.SetState; +import com.pubnub.api.endpoints.presence.WhereNow; +import com.pubnub.api.endpoints.pubsub.Publish; +import com.pubnub.api.endpoints.push.AddChannelsToPush; +import com.pubnub.api.endpoints.push.RemoveChannelsFromPush; +import com.pubnub.api.endpoints.push.RemoveAllPushChannelsForDevice; +import com.pubnub.api.endpoints.push.ListPushProvisions; +import com.pubnub.api.managers.BasePathManager; +import com.pubnub.api.managers.PublishSequenceManager; +import com.pubnub.api.managers.RetrofitManager; +import com.pubnub.api.managers.SubscriptionManager; +import com.pubnub.api.vendor.Crypto; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.util.Date; +import java.util.List; + + +@Getter +@Slf4j +public class PubNub { + + private PNConfiguration configuration; + + @Getter(AccessLevel.NONE) + private SubscriptionManager subscriptionManager; + @Getter(AccessLevel.NONE) + private BasePathManager basePathManager; + @Getter(AccessLevel.NONE) + private PublishSequenceManager publishSequenceManager; + @Getter(AccessLevel.NONE) + private RetrofitManager retrofitManager; + + + private static final int TIMESTAMP_DIVIDER = 1000; + private static final int MAX_SEQUENCE = 65535; + + private static final String SDK_VERSION = "4.0.8"; + + public PubNub(final PNConfiguration initialConfig) { + this.configuration = initialConfig; + this.basePathManager = new BasePathManager(initialConfig); + this.retrofitManager = new RetrofitManager(this); + this.subscriptionManager = new SubscriptionManager(this, retrofitManager); + this.publishSequenceManager = new PublishSequenceManager(MAX_SEQUENCE); + } + + public String getBaseUrl() { + return this.basePathManager.getBasePath(); + } + + + // + public final void addListener(SubscribeCallback listener) { + subscriptionManager.addListener(listener); + } + + public final void removeListener(SubscribeCallback listener) { + subscriptionManager.removeListener(listener); + } + + public final SubscribeBuilder subscribe() { + return new SubscribeBuilder(this.subscriptionManager); + } + + public final UnsubscribeBuilder unsubscribe() { + return new UnsubscribeBuilder(this.subscriptionManager); + } + + // start push + + public final AddChannelsToPush addPushNotificationsOnChannels() { + return new AddChannelsToPush(this, this.retrofitManager.getTransactionInstance()); + } + + public final RemoveChannelsFromPush removePushNotificationsFromChannels() { + return new RemoveChannelsFromPush(this, this.retrofitManager.getTransactionInstance()); + } + + public final RemoveAllPushChannelsForDevice removeAllPushNotificationsFromDeviceWithPushToken() { + return new RemoveAllPushChannelsForDevice(this, this.retrofitManager.getTransactionInstance()); + } + + public final ListPushProvisions auditPushChannelProvisions() { + return new ListPushProvisions(this, this.retrofitManager.getTransactionInstance()); + } + + // end push + + public final WhereNow whereNow() { + return new WhereNow(this, this.retrofitManager.getTransactionInstance()); + } + + public final HereNow hereNow() { + return new HereNow(this, this.retrofitManager.getTransactionInstance()); + } + + public final Time time() { + return new Time(this, this.retrofitManager.getTransactionInstance()); + } + + public final History history() { + return new History(this, this.retrofitManager.getTransactionInstance()); + } + + + public final Audit audit() { + return new Audit(this, this.retrofitManager.getTransactionInstance()); + } + + public final Grant grant() { + return new Grant(this, this.retrofitManager.getTransactionInstance()); + } + + public final GetState getPresenceState() { + return new GetState(this, this.retrofitManager.getTransactionInstance()); + } + + public final SetState setPresenceState() { + return new SetState(this, subscriptionManager, this.retrofitManager.getTransactionInstance()); + } + + public final Publish publish() { + return new Publish(this, publishSequenceManager, this.retrofitManager.getTransactionInstance()); + } + + public final ListAllChannelGroup listAllChannelGroups() { + return new ListAllChannelGroup(this, this.retrofitManager.getTransactionInstance()); + } + + public final AllChannelsChannelGroup listChannelsForChannelGroup() { + return new AllChannelsChannelGroup(this, this.retrofitManager.getTransactionInstance()); + } + + public final AddChannelChannelGroup addChannelsToChannelGroup() { + return new AddChannelChannelGroup(this, this.retrofitManager.getTransactionInstance()); + } + + public final RemoveChannelChannelGroup removeChannelsFromChannelGroup() { + return new RemoveChannelChannelGroup(this, this.retrofitManager.getTransactionInstance()); + } + + public final DeleteChannelGroup deleteChannelGroup() { + return new DeleteChannelGroup(this, this.retrofitManager.getTransactionInstance()); + } + + // public methods + + /** + * Perform Cryptographic decryption of an input string using cipher key provided by PNConfiguration + * + * @param inputString String to be encrypted + * @return String containing the encryption of inputString using cipherKey + */ + public final String decrypt(String inputString) throws PubNubException { + if (inputString == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_INVALID_ARGUMENTS).build(); + } + + return decrypt(inputString, this.getConfiguration().getCipherKey()); + } + + /** + * Perform Cryptographic decryption of an input string using the cipher key + * + * @param inputString String to be encrypted + * @param cipherKey cipher key to be used for encryption + * @return String containing the encryption of inputString using cipherKey + * @throws PubNubException throws exception in case of failed encryption + */ + public final String decrypt(final String inputString, final String cipherKey) throws PubNubException { + if (inputString == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_INVALID_ARGUMENTS).build(); + } + + return new Crypto(cipherKey).decrypt(inputString); + } + + /** + * Perform Cryptographic encryption of an input string and the cipher key provided by PNConfiguration + * + * @param inputString String to be encrypted + * @return String containing the encryption of inputString using cipherKey + */ + public final String encrypt(final String inputString) throws PubNubException { + if (inputString == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_INVALID_ARGUMENTS).build(); + } + + return encrypt(inputString, this.getConfiguration().getCipherKey()); + } + + /** + * Perform Cryptographic encryption of an input string and the cipher key. + * + * @param inputString String to be encrypted + * @param cipherKey cipher key to be used for encryption + * @return String containing the encryption of inputString using cipherKey + * @throws PubNubException throws exception in case of failed encryption + */ + public final String encrypt(final String inputString, final String cipherKey) throws PubNubException { + if (inputString == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_INVALID_ARGUMENTS).build(); + } + + return new Crypto(cipherKey).encrypt(inputString); + } + + public int getTimestamp() { + return (int) ((new Date().getTime()) / TIMESTAMP_DIVIDER); + } + + /** + * @return version of the SDK. + */ + public String getVersion() { + return SDK_VERSION; + } + + /** + * Stop the SDK and terminate all listeners. + */ + public final void stop() { + subscriptionManager.stop(); + } + + /** + * Perform a Reconnect to the network + */ + public final void reconnect() { + subscriptionManager.reconnect(); + } + + public final Publish fire() { + return publish().shouldStore(false).replicate(false); + } + + public final List getSubscribedChannels() { + return subscriptionManager.getSubscribedChannels(); + } + + public final List getSubscribedChannelGroups() { + return subscriptionManager.getSubscribedChannelGroups(); + } + + public final void unsubscribeAll() { + subscriptionManager.unsubscribeAll(); + } +} diff --git a/src/main/java/com/pubnub/api/PubNubError.java b/src/main/java/com/pubnub/api/PubNubError.java new file mode 100644 index 000000000..79ecb6f22 --- /dev/null +++ b/src/main/java/com/pubnub/api/PubNubError.java @@ -0,0 +1,32 @@ +package com.pubnub.api; + + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; +import lombok.Getter; + +/** + * PubNubError object is passed to errorCallback. It contains details of error, + * like error code, error string, and optional message + * + * @author PubNub + * + */ +@Getter +@Builder +public class PubNubError { + + private final int errorCode; + private final int errorCodeExtended; + private final JsonNode errorObject; + /** + * includes a message from the thrown exception (if any.) + */ + private final String message; + /** + * PubNub supplied explanation of the error. + */ + private final String errorString; + +} + diff --git a/src/main/java/com/pubnub/api/PubNubException.java b/src/main/java/com/pubnub/api/PubNubException.java new file mode 100644 index 000000000..47b0cfcc8 --- /dev/null +++ b/src/main/java/com/pubnub/api/PubNubException.java @@ -0,0 +1,20 @@ +package com.pubnub.api; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import retrofit2.Call; + +@Builder +@Getter +public class PubNubException extends Exception { + private String errormsg = ""; + private PubNubError pubnubError; + private JsonNode jso; + private String response; + private int statusCode; + + @Getter(AccessLevel.NONE) + private Call affectedCall; +} diff --git a/src/main/java/com/pubnub/api/PubNubUtil.java b/src/main/java/com/pubnub/api/PubNubUtil.java new file mode 100644 index 000000000..56a089d5e --- /dev/null +++ b/src/main/java/com/pubnub/api/PubNubUtil.java @@ -0,0 +1,135 @@ +package com.pubnub.api; + +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.vendor.Base64; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + + +public final class PubNubUtil { + + private PubNubUtil() { + } + + public static String joinString(List val, String delim) { + StringBuilder builder = new StringBuilder(); + for (String l : val) { + builder.append(l); + builder.append(","); + } + + return builder.toString().substring(0, builder.toString().length() - 1); + + } + + /** + * Returns encoded String + * + * @param sUrl , input string + * @return , encoded string + */ + public static String pamEncode(String sUrl) { + /* !'()*~ */ + + String encoded = urlEncode(sUrl); + if (encoded != null) { + encoded = encoded. + replace("*", "%2A") + .replace("!", "%21") + .replace("'", "%27") + .replace("(", "%28") + .replace(")", "%29") + .replace("[", "%5B") + .replace("]", "%5D") + .replace("~", "%7E"); + } + return encoded; + } + + + /** + * Returns encoded String + * + * @param sUrl , input string + * @return , encoded string + */ + public static String urlEncode(String sUrl) { + try { + return URLEncoder.encode(sUrl, "UTF-8").replace("+", "%20"); + } catch (UnsupportedEncodingException e) { + return null; + } + } + + /** + * Returns decoded String + * + * @param sUrl + * , input string + * @return , decoded string + */ + public static String urlDecode(String sUrl) { + try { + return URLDecoder.decode(sUrl, "UTF-8"); + } catch (UnsupportedEncodingException e) { + return null; + } + } + + public static String preparePamArguments(Map pamArgs) { + Set pamKeys = new TreeSet(pamArgs.keySet()); + String stringifiedArguments = ""; + int i = 0; + + for (String pamKey : pamKeys) { + if (i != 0) { + stringifiedArguments = stringifiedArguments.concat("&"); + } + + + stringifiedArguments = stringifiedArguments.concat(pamKey).concat("=").concat(pamEncode(pamArgs.get(pamKey))); + + i += 1; + } + + return stringifiedArguments; + } + + public static String signSHA256(final String key, final String data) throws PubNubException { + Mac sha256HMAC; + byte[] hmacData; + SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(Charset.forName("UTF-8")), "HmacSHA256"); + + try { + sha256HMAC = Mac.getInstance("HmacSHA256"); + } catch (NoSuchAlgorithmException e) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CRYPTO_ERROR).errormsg(e.getMessage()).build(); + } + + try { + sha256HMAC.init(secretKey); + } catch (InvalidKeyException e) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CRYPTO_ERROR).errormsg(e.getMessage()).build(); + } + + try { + hmacData = sha256HMAC.doFinal(data.getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CRYPTO_ERROR).errormsg(e.getMessage()).build(); + } + + return new String(Base64.encode(hmacData, 0), Charset.forName("UTF-8")).replace('+', '-').replace('/', '_'); + } + +} diff --git a/src/main/java/com/pubnub/api/builder/PubNubErrorBuilder.java b/src/main/java/com/pubnub/api/builder/PubNubErrorBuilder.java new file mode 100644 index 000000000..91d090e17 --- /dev/null +++ b/src/main/java/com/pubnub/api/builder/PubNubErrorBuilder.java @@ -0,0 +1,488 @@ +package com.pubnub.api.builder; + +import com.pubnub.api.PubNubError; + + +public final class PubNubErrorBuilder { + + + private PubNubErrorBuilder() { + + } + + // Error Codes + + /** + * Timeout Error . + */ + public static final int PNERR_TIMEOUT = 100; + + /** + * + */ + public static final int PNERR_PUBNUB_ERROR = 101; + + /** + * Connect Exception . Network Unreachable. + */ + public static final int PNERR_CONNECT_EXCEPTION = 102; + + /** + * Please check network connectivity. Please contact support with error + * details if issue persists. + */ + public static final int PNERR_HTTP_ERROR = 103; + + /** + * Client Timeout . + */ + public static final int PNERR_CLIENT_TIMEOUT = 104; + + /** + * An ULS singature error occurred . Please contact support with error + * details. + */ + public static final int PNERR_ULSSIGN_ERROR = 105; + + /** + * Please verify if network is reachable + */ + public static final int PNERR_NETWORK_ERROR = 106; + + /** + * PubNub Exception . + */ + public static final int PNERR_PUBNUB_EXCEPTION = 108; + + /** + * Disconnect . + */ + public static final int PNERR_DISCONNECT = 109; + + /** + * Disconnect and Resubscribe Received . + */ + public static final int PNERR_DISCONN_AND_RESUB = 110; + + /** + * Gateway Timeout + */ + public static final int PNERR_GATEWAY_TIMEOUT = 111; + + /** + * PubNub server returned HTTP 403 forbidden status code. Happens when wrong + * authentication key is used . + */ + public static final int PNERR_FORBIDDEN = 112; + /** + * PubNub server returned HTTP 401 unauthorized status code Happens when + * authentication key is missing . + */ + public static final int PNERR_UNAUTHORIZED = 113; + + /** + * Secret key not configured + */ + public static final int PNERR_SECRET_KEY_MISSING = 114; + + // internal error codes + + /** + * Error while encrypting message to be published to PubNub Cloud . Please + * contact support with error details. + */ + public static final int PNERR_ENCRYPTION_ERROR = 115; + + /** + * Decryption Error . Please contact support with error details. + */ + public static final int PNERR_DECRYPTION_ERROR = 116; + + /** + * Invalid Json . Please contact support with error details. + */ + public static final int PNERR_INVALID_JSON = 117; + + /** + * Unable to open input stream . Please contact support with error details. + */ + public static final int PNERR_GETINPUTSTREAM = 118; + + /** + * Malformed URL . Please contact support with error details . + */ + public static final int PNERR_MALFORMED_URL = 119; + + /** + * Error in opening URL . Please contact support with error details. + */ + public static final int PNERR_URL_OPEN = 120; + + /** + * JSON Error while processing API response. Please contact support with + * error details. + */ + public static final int PNERR_JSON_ERROR = 121; + + /** + * Protocol Exception . Please contact support with error details. + */ + public static final int PNERR_PROTOCOL_EXCEPTION = 122; + + /** + * Unable to read input stream . Please contact support with error details. + */ + public static final int PNERR_READINPUT = 123; + + /** + * Bad gateway . Please contact support with error details. + */ + public static final int PNERR_BAD_GATEWAY = 124; + + /** + * PubNub server returned HTTP 502 internal server error status code. Please + * contact support with error details. + */ + public static final int PNERR_INTERNAL_ERROR = 125; + + /** + * Parsing Error . + */ + public static final int PNERR_PARSING_ERROR = 126; + + /** + * Bad Request . Please contact support with error details. + */ + public static final int PNERR_BAD_REQUEST = 127; + + public static final int PNERR_HTTP_RC_ERROR = 128; + /** + * PubNub server or intermediate server returned HTTP 404 unauthorized + * status code + * + */ + public static final int PNERR_NOT_FOUND = 129; + + /** + * Subscribe Timeout . + */ + public static final int PNERR_HTTP_SUBSCRIBE_TIMEOUT = 130; + + /** + * Invalid arguments provided to API + * + */ + public static final int PNERR_INVALID_ARGUMENTS = 131; + + /** + * Channel missing + * + */ + public static final int PNERR_CHANNEL_MISSING = 132; + + /** + * PubNub connection not set on sender + * + */ + public static final int PNERR_CONNECTION_NOT_SET = 133; + + /** + * Error while parsing group name + */ + public static final int PNERR_CHANNEL_GROUP_PARSING_ERROR = 134; + + /** + * Crypto Error + */ + public static final int PNERR_CRYPTO_ERROR = 135; + + /** + * Group missing + * + */ + public static final int PNERR_GROUP_MISSING = 136; + + /** + * Auth Keys missing + * + */ + public static final int PNERR_AUTH_KEYS_MISSING = 137; + + /** + * Subscribe Key missing + * + */ + public static final int PNERR_SUBSCRIBE_KEY_MISSING = 138; + + /** + * Publish Key missing + * + */ + public static final int PNERR_PUBLISH_KEY_MISSING = 139; + + /** + * State missing + * + */ + public static final int PNERR_STATE_MISSING = 140; + + /** + * Channel and Group missing + * + */ + public static final int PNERR_CHANNEL_AND_GROUP_MISSING = 141; + + /** + * Message missing + * + */ + public static final int PNERR_MESSAGE_MISSING = 142; + + /** + * Push TYpe missing + * + */ + public static final int PNERR_PUSH_TYPE_MISSING = 143; + + /** + * Device ID missing + * + */ + public static final int PNERR_DEVICE_ID_MISSING = 144; + + // Error Objects + + public static final PubNubError PNERROBJ_TIMEOUT = PubNubError.builder() + .errorCode(PNERR_TIMEOUT) + .message("Timeout Occurred") + .build(); + + public static final PubNubError PNERROBJ_INTERNAL_ERROR = PubNubError.builder() + .errorCode(PNERR_INTERNAL_ERROR) + .message("Internal Error") + .build(); + + public static final PubNubError PNERROBJ_ENCRYPTION_ERROR = PubNubError.builder() + .errorCode(PNERR_ENCRYPTION_ERROR) + .message("Error while encrypting message to be published to PubNub Cloud. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_DECRYPTION_ERROR = PubNubError.builder() + .errorCode(PNERR_DECRYPTION_ERROR) + .message("Decryption Error. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_INVALID_JSON = PubNubError.builder() + .errorCode(PNERR_INVALID_JSON) + .message("Invalid Json. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_JSON_ERROR = PubNubError.builder() + .errorCode(PNERR_JSON_ERROR) + .message("JSON Error while processing API response. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_MALFORMED_URL = PubNubError.builder() + .errorCode(PNERR_MALFORMED_URL) + .message("Malformed URL. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_PUBNUB_ERROR = PubNubError.builder() + .errorCode(PNERR_PUBNUB_ERROR) + .message("PubNub Error") + .build(); + + public static final PubNubError PNERROBJ_URL_OPEN = PubNubError.builder() + .errorCode(PNERR_URL_OPEN) + .message("Error opening url. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_PROTOCOL_EXCEPTION = PubNubError.builder() + .errorCode(PNERR_PROTOCOL_EXCEPTION) + .message("Protocol Exception. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_CONNECT_EXCEPTION = PubNubError.builder() + .errorCode(PNERR_CONNECT_EXCEPTION) + .message("Connect Exception. Please verify if network is reachable.") + .build(); + + public static final PubNubError PNERROBJ_HTTP_RC_ERROR = PubNubError.builder() + .errorCode(PNERR_HTTP_RC_ERROR) + .message("Unable to get PnResponse Code. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_GETINPUTSTREAM = PubNubError.builder() + .errorCode(PNERR_GETINPUTSTREAM) + .message("Unable to get Input Stream Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_READINPUT = PubNubError.builder() + .errorCode(PNERR_READINPUT) + .message("Unable to read Input Stream. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_BAD_REQUEST = PubNubError.builder() + .errorCode(PNERR_BAD_REQUEST) + .message("Bad request. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_HTTP_ERROR = PubNubError.builder() + .errorCode(PNERR_HTTP_ERROR) + .message("HTTP Error. Please check network connectivity. Please contact support with error details if issue persists.") + .build(); + + public static final PubNubError PNERROBJ_BAD_GATEWAY = PubNubError.builder() + .errorCode(PNERR_BAD_GATEWAY) + .message("Bad Gateway. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_CLIENT_TIMEOUT = PubNubError.builder() + .errorCode(PNERR_CLIENT_TIMEOUT) + .message("Client Timeout") + .build(); + + public static final PubNubError PNERROBJ_GATEWAY_TIMEOUT = PubNubError.builder() + .errorCode(PNERR_GATEWAY_TIMEOUT) + .message("Gateway Timeout") + .build(); + + public static final PubNubError PNERROBJ_5023_INTERNAL_ERROR = PubNubError.builder() + .errorCode(PNERR_INTERNAL_ERROR) + .message("Internal Server Error. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_PARSING_ERROR = PubNubError.builder() + .errorCode(PNERR_PARSING_ERROR) + .message("Parsing Error") + .build(); + + public static final PubNubError PNERROBJ_PUBNUB_EXCEPTION = PubNubError.builder() + .errorCode(PNERR_PUBNUB_EXCEPTION) + .message("PubNub Exception") + .build(); + + public static final PubNubError PNERROBJ_DISCONNECT = PubNubError.builder() + .errorCode(PNERR_DISCONNECT) + .message("Disconnect") + .build(); + + public static final PubNubError PNERROBJ_DISCONN_AND_RESUB = PubNubError.builder() + .errorCode(PNERR_DISCONN_AND_RESUB) + .message("Disconnect and Resubscribe") + .build(); + + public static final PubNubError PNERROBJ_FORBIDDEN = PubNubError.builder() + .errorCode(PNERR_FORBIDDEN) + .message("Authentication Failure. Incorrect Authentication Key") + .build(); + + public static final PubNubError PNERROBJ_UNAUTHORIZED = PubNubError.builder() + .errorCode(PNERR_UNAUTHORIZED) + .message("Authentication Failure. Authentication Key is missing") + .build(); + + public static final PubNubError PNERROBJ_SECRET_KEY_MISSING = PubNubError.builder() + .errorCode(PNERR_SECRET_KEY_MISSING) + .message("ULS configuration failed. Secret Key not configured.") + .build(); + + public static final PubNubError PNERROBJ_SUBSCRIBE_KEY_MISSING = PubNubError.builder() + .errorCode(PNERR_SUBSCRIBE_KEY_MISSING) + .message("ULS configuration failed. Subscribe Key not configured.") + .build(); + + public static final PubNubError PNERROBJ_PUBLISH_KEY_MISSING = PubNubError.builder() + .errorCode(PNERR_PUBLISH_KEY_MISSING) + .message("ULS configuration failed. Publish Key not configured.") + .build(); + + public static final PubNubError PNERROBJ_ULSSIGN_ERROR = PubNubError.builder() + .errorCode(PNERR_ULSSIGN_ERROR) + .message("Invalid Signature. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_5075_NETWORK_ERROR = PubNubError.builder() + .errorCode(PNERR_NETWORK_ERROR) + .message("Network Error. Please verify if network is reachable.") + .build(); + + public static final PubNubError PNERROBJ_NOT_FOUND_ERROR = PubNubError.builder() + .errorCode(PNERR_NOT_FOUND) + .message("Page Not Found Please verify if network is reachable. Please contact support with error details.") + .build(); + + public static final PubNubError PNERROBJ_SUBSCRIBE_TIMEOUT = PubNubError.builder() + .errorCode(PNERR_HTTP_SUBSCRIBE_TIMEOUT) + .message("Subscribe Timeout.") + .build(); + + public static final PubNubError PNERROBJ_INVALID_ARGUMENTS = PubNubError.builder() + .errorCode(PNERR_INVALID_ARGUMENTS) + .message("INVALID ARGUMENTS.") + .build(); + + public static final PubNubError PNERROBJ_CHANNEL_MISSING = PubNubError.builder() + .errorCode(PNERR_CHANNEL_MISSING) + .message("Channel Missing.") + .build(); + + public static final PubNubError PNERROBJ_STATE_MISSING = PubNubError.builder() + .errorCode(PNERR_STATE_MISSING) + .message("State Missing.") + .build(); + + public static final PubNubError PNERROBJ_MESSAGE_MISSING = PubNubError.builder() + .errorCode(PNERR_MESSAGE_MISSING) + .message("Message Missing.") + .build(); + + public static final PubNubError PNERROBJ_PUSH_TYPE_MISSING = PubNubError.builder() + .errorCode(PNERR_PUSH_TYPE_MISSING) + .message("Push Type Missing.") + .build(); + + public static final PubNubError PNERROBJ_DEVICE_ID_MISSING = PubNubError.builder() + .errorCode(PNERR_DEVICE_ID_MISSING) + .message("Device Id Missing.") + .build(); + + public static final PubNubError PNERROBJ_CONNECTION_NOT_SET = PubNubError.builder() + .errorCode(PNERR_CONNECTION_NOT_SET) + .message("PubNub Connection not set") + .build(); + + public static final PubNubError PNERROBJ_GROUP_MISSING = PubNubError.builder() + .errorCode(PNERR_GROUP_MISSING) + .message("Group Missing.") + .build(); + + public static final PubNubError PNERROBJ_CHANNEL_AND_GROUP_MISSING = PubNubError.builder() + .errorCode(PNERR_CHANNEL_AND_GROUP_MISSING) + .message("Channel and Group Missing.") + .build(); + + public static final PubNubError PNERROBJ_AUTH_KEYS_MISSING = PubNubError.builder() + .errorCode(PNERR_AUTH_KEYS_MISSING) + .message("Auth Keys Missing.") + .build(); + + public static final PubNubError PNERROBJ_CHANNEL_GROUP_PARSING_ERROR = PubNubError.builder() + .errorCode(PNERR_CHANNEL_GROUP_PARSING_ERROR) + .message("Channel group name is invalid") + .build(); + + public static final PubNubError PNERROBJ_CRYPTO_ERROR = PubNubError.builder() + .errorCode(PNERR_CRYPTO_ERROR) + .message("Error while encrypting/decrypting message. Please contact support with error details.") + .build(); + + public static PubNubError createCryptoError(final int code, final String message) { + return PubNubError.builder() + .errorCode(PNERR_CRYPTO_ERROR) + .errorCodeExtended(code) + .message("Error while encrypting/decrypting message. Please contact support with error details. - ".concat(message)) + .build(); + } + +} diff --git a/src/main/java/com/pubnub/api/builder/PubSubBuilder.java b/src/main/java/com/pubnub/api/builder/PubSubBuilder.java new file mode 100644 index 000000000..65e8ae8df --- /dev/null +++ b/src/main/java/com/pubnub/api/builder/PubSubBuilder.java @@ -0,0 +1,45 @@ +package com.pubnub.api.builder; + + +import com.pubnub.api.managers.SubscriptionManager; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +public abstract class PubSubBuilder { + + @Getter(AccessLevel.PROTECTED) + @Setter(AccessLevel.PROTECTED) + private List channelSubscriptions; + + @Getter(AccessLevel.PROTECTED) + @Setter(AccessLevel.PROTECTED) + private List channelGroupSubscriptions; + + @Getter(AccessLevel.PROTECTED) + @Setter(AccessLevel.PROTECTED) + private SubscriptionManager subscriptionManager; + + public PubSubBuilder(SubscriptionManager subscriptionManagerInstance) { + this.subscriptionManager = subscriptionManagerInstance; + this.channelSubscriptions = new ArrayList<>(); + this.channelGroupSubscriptions = new ArrayList<>(); + } + + + public PubSubBuilder channels(List channel) { + channelSubscriptions.addAll(channel); + return this; + } + + public PubSubBuilder channelGroups(List channelGroup) { + channelGroupSubscriptions.addAll(channelGroup); + return this; + } + + public abstract void execute(); + +} diff --git a/src/main/java/com/pubnub/api/builder/SubscribeBuilder.java b/src/main/java/com/pubnub/api/builder/SubscribeBuilder.java new file mode 100644 index 000000000..fbe685e0f --- /dev/null +++ b/src/main/java/com/pubnub/api/builder/SubscribeBuilder.java @@ -0,0 +1,60 @@ +package com.pubnub.api.builder; + +import com.pubnub.api.builder.dto.SubscribeOperation; +import com.pubnub.api.managers.SubscriptionManager; +import lombok.AccessLevel; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.util.List; + +@Setter +@Accessors(chain = true, fluent = true) +public class SubscribeBuilder extends PubSubBuilder { + + /** + * Allow users to specify if they would also like to include the presence channels for those subscriptions. + */ + @Setter(AccessLevel.NONE) + private boolean presenceEnabled; + + /** + * Allow users to subscribe with a custom timetoken. + */ + @Setter(AccessLevel.NONE) + private Long timetoken; + + public SubscribeBuilder(final SubscriptionManager subscriptionManager) { + super(subscriptionManager); + } + + public SubscribeBuilder withPresence() { + this.presenceEnabled = true; + return this; + } + + public SubscribeBuilder withTimetoken(Long timetokenInstance) { + this.timetoken = timetokenInstance; + return this; + } + + public void execute() { + SubscribeOperation subscribeOperation = SubscribeOperation.builder() + .channels(this.getChannelSubscriptions()) + .channelGroups(this.getChannelGroupSubscriptions()) + .timetoken(timetoken) + .presenceEnabled(presenceEnabled) + .build(); + + this.getSubscriptionManager().adaptSubscribeBuilder(subscribeOperation); + } + + public SubscribeBuilder channels(final List channels) { + return (SubscribeBuilder) super.channels(channels); + } + + public SubscribeBuilder channelGroups(final List channelGroups) { + return (SubscribeBuilder) super.channelGroups(channelGroups); + } + +} diff --git a/src/main/java/com/pubnub/api/builder/UnsubscribeBuilder.java b/src/main/java/com/pubnub/api/builder/UnsubscribeBuilder.java new file mode 100644 index 000000000..03c4c7063 --- /dev/null +++ b/src/main/java/com/pubnub/api/builder/UnsubscribeBuilder.java @@ -0,0 +1,26 @@ +package com.pubnub.api.builder; + +import com.pubnub.api.builder.dto.UnsubscribeOperation; +import com.pubnub.api.managers.SubscriptionManager; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class UnsubscribeBuilder extends PubSubBuilder { + + public UnsubscribeBuilder(final SubscriptionManager subscriptionManager) { + super(subscriptionManager); + } + + public void execute() { + + UnsubscribeOperation unsubscribeOperation = UnsubscribeOperation.builder() + .channels(this.getChannelSubscriptions()) + .channelGroups(this.getChannelGroupSubscriptions()) + .build(); + + this.getSubscriptionManager().adaptUnsubscribeBuilder(unsubscribeOperation); + } + +} diff --git a/src/main/java/com/pubnub/api/builder/dto/StateOperation.java b/src/main/java/com/pubnub/api/builder/dto/StateOperation.java new file mode 100644 index 000000000..cd512fb56 --- /dev/null +++ b/src/main/java/com/pubnub/api/builder/dto/StateOperation.java @@ -0,0 +1,16 @@ +package com.pubnub.api.builder.dto; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Builder +@Getter +public class StateOperation { + + private List channels; + private List channelGroups; + private Object state; + +} diff --git a/src/main/java/com/pubnub/api/builder/dto/SubscribeOperation.java b/src/main/java/com/pubnub/api/builder/dto/SubscribeOperation.java new file mode 100644 index 000000000..915bec92c --- /dev/null +++ b/src/main/java/com/pubnub/api/builder/dto/SubscribeOperation.java @@ -0,0 +1,17 @@ +package com.pubnub.api.builder.dto; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Builder +@Getter +public class SubscribeOperation { + + private List channels; + private List channelGroups; + private boolean presenceEnabled; + private Long timetoken; + +} diff --git a/src/main/java/com/pubnub/api/builder/dto/UnsubscribeOperation.java b/src/main/java/com/pubnub/api/builder/dto/UnsubscribeOperation.java new file mode 100644 index 000000000..509ceb51b --- /dev/null +++ b/src/main/java/com/pubnub/api/builder/dto/UnsubscribeOperation.java @@ -0,0 +1,14 @@ +package com.pubnub.api.builder.dto; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class UnsubscribeOperation { + + private List channels; + private List channelGroups; +} diff --git a/src/main/java/com/pubnub/api/callbacks/PNCallback.java b/src/main/java/com/pubnub/api/callbacks/PNCallback.java new file mode 100644 index 000000000..6be898bec --- /dev/null +++ b/src/main/java/com/pubnub/api/callbacks/PNCallback.java @@ -0,0 +1,9 @@ +package com.pubnub.api.callbacks; + + +import com.pubnub.api.models.consumer.PNStatus; + +public abstract class PNCallback { + public abstract void onResponse(X result, PNStatus status); +} + diff --git a/src/main/java/com/pubnub/api/callbacks/ReconnectionCallback.java b/src/main/java/com/pubnub/api/callbacks/ReconnectionCallback.java new file mode 100644 index 000000000..cf1dc734d --- /dev/null +++ b/src/main/java/com/pubnub/api/callbacks/ReconnectionCallback.java @@ -0,0 +1,8 @@ +package com.pubnub.api.callbacks; + + +public abstract class ReconnectionCallback { + + public abstract void onReconnection(); + +} diff --git a/src/main/java/com/pubnub/api/callbacks/SubscribeCallback.java b/src/main/java/com/pubnub/api/callbacks/SubscribeCallback.java new file mode 100644 index 000000000..2add4759b --- /dev/null +++ b/src/main/java/com/pubnub/api/callbacks/SubscribeCallback.java @@ -0,0 +1,12 @@ +package com.pubnub.api.callbacks; + +import com.pubnub.api.PubNub; +import com.pubnub.api.models.consumer.pubsub.PNMessageResult; +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; +import com.pubnub.api.models.consumer.PNStatus; + +public abstract class SubscribeCallback { + public abstract void status(PubNub pubnub, PNStatus status); + public abstract void message(PubNub pubnub, PNMessageResult message); + public abstract void presence(PubNub pubnub, PNPresenceEventResult presence); +} diff --git a/src/main/java/com/pubnub/api/callbacks/TimeCallback.java b/src/main/java/com/pubnub/api/callbacks/TimeCallback.java new file mode 100644 index 000000000..9012fe0c2 --- /dev/null +++ b/src/main/java/com/pubnub/api/callbacks/TimeCallback.java @@ -0,0 +1,6 @@ +package com.pubnub.api.callbacks; + +import com.pubnub.api.models.consumer.PNTimeResult; + +public abstract class TimeCallback extends PNCallback { +} diff --git a/src/main/java/com/pubnub/api/callbacks/WhereNowCallback.java b/src/main/java/com/pubnub/api/callbacks/WhereNowCallback.java new file mode 100644 index 000000000..d286a3fb7 --- /dev/null +++ b/src/main/java/com/pubnub/api/callbacks/WhereNowCallback.java @@ -0,0 +1,7 @@ +package com.pubnub.api.callbacks; + +import com.pubnub.api.models.consumer.presence.PNWhereNowResult; + + +public abstract class WhereNowCallback extends PNCallback { +} diff --git a/src/main/java/com/pubnub/api/endpoints/Endpoint.java b/src/main/java/com/pubnub/api/endpoints/Endpoint.java new file mode 100644 index 000000000..3767c4e2d --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/Endpoint.java @@ -0,0 +1,259 @@ +package com.pubnub.api.endpoints; + + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.enums.PNStatusCategory; +import com.pubnub.api.models.consumer.PNErrorData; +import com.pubnub.api.models.consumer.PNStatus; +import lombok.AccessLevel; +import lombok.Getter; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class Endpoint { + + @Getter(AccessLevel.PROTECTED) + private PubNub pubnub; + @Getter(AccessLevel.PROTECTED) + private Retrofit retrofit; + + @Getter(AccessLevel.NONE) + private PNCallback cachedCallback; + + @Getter(AccessLevel.NONE) + private Call call; + + /** + * If the endpoint failed to execute and we do not want to alert the user, flip this to true + * This operation is handy if we internally cancelled the endpoint. + */ + @Getter(AccessLevel.NONE) + private boolean silenceFailures; + + private static final int SERVER_RESPONSE_SUCCESS = 200; + private static final int SERVER_RESPONSE_FORBIDDEN = 403; + private static final int SERVER_RESPONSE_BAD_REQUEST = 400; + + public Endpoint(final PubNub pubnubInstance, Retrofit retrofitInstance) { + this.pubnub = pubnubInstance; + this.retrofit = retrofitInstance; + } + + + public final Output sync() throws PubNubException { + this.validateParams(); + + call = doWork(createBaseParams()); + Response serverResponse; + Output response; + + try { + serverResponse = call.execute(); + } catch (IOException e) { + throw PubNubException.builder() + .pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR) + .errormsg(e.toString()) + .affectedCall(call) + .build(); + } + + if (!serverResponse.isSuccessful() || serverResponse.code() != SERVER_RESPONSE_SUCCESS) { + String responseBodyText; + + try { + responseBodyText = serverResponse.errorBody().string(); + } catch (IOException e) { + responseBodyText = "N/A"; + } + + throw PubNubException.builder() + .pubnubError(PubNubErrorBuilder.PNERROBJ_HTTP_ERROR) + .errormsg(responseBodyText) + .statusCode(serverResponse.code()) + .affectedCall(call) + .build(); + } + + response = createResponse(serverResponse); + + return response; + } + + public final void async(final PNCallback callback) { + cachedCallback = callback; + + try { + call = doWork(createBaseParams()); + } catch (PubNubException pubnubException) { + callback.onResponse(null, createStatusResponse(PNStatusCategory.PNBadRequestCategory, null, pubnubException)); + return; + } + + call.enqueue(new retrofit2.Callback() { + + @Override + public void onResponse(final Call performedCall, final Response response) { + Output callbackResponse; + + if (!response.isSuccessful() || response.code() != SERVER_RESPONSE_SUCCESS) { + + String responseBodyText; + + try { + responseBodyText = response.errorBody().string(); + } catch (IOException e) { + responseBodyText = "N/A"; + } + + PNStatusCategory pnStatusCategory = PNStatusCategory.PNUnknownCategory; + PubNubException ex = PubNubException.builder() + .pubnubError(PubNubErrorBuilder.PNERROBJ_HTTP_ERROR) + .errormsg(responseBodyText) + .statusCode(response.code()) + .build(); + + if (response.code() == SERVER_RESPONSE_FORBIDDEN) { + pnStatusCategory = PNStatusCategory.PNAccessDeniedCategory; + } + + if (response.code() == SERVER_RESPONSE_BAD_REQUEST) { + pnStatusCategory = PNStatusCategory.PNBadRequestCategory; + } + + callback.onResponse(null, createStatusResponse(pnStatusCategory, response, ex)); + return; + } + + try { + callbackResponse = createResponse(response); + } catch (PubNubException pubnubException) { + callback.onResponse(null, createStatusResponse(PNStatusCategory.PNMalformedResponseCategory, response, pubnubException)); + return; + } + + callback.onResponse(callbackResponse, createStatusResponse(PNStatusCategory.PNAcknowledgmentCategory, response, null)); + } + + @Override + public void onFailure(final Call performedCall, final Throwable throwable) { + if (silenceFailures) { + return; + } + + PNStatusCategory pnStatusCategory = PNStatusCategory.PNBadRequestCategory; + PubNubException.PubNubExceptionBuilder pubnubException = PubNubException.builder() + .errormsg(throwable.getMessage()); + + try { + throw throwable; + } catch (UnknownHostException networkException) { + pubnubException.pubnubError(PubNubErrorBuilder.PNERROBJ_CONNECTION_NOT_SET); + pnStatusCategory = PNStatusCategory.PNUnexpectedDisconnectCategory; + } catch (ConnectException connectException) { + pubnubException.pubnubError(PubNubErrorBuilder.PNERROBJ_CONNECT_EXCEPTION); + pnStatusCategory = PNStatusCategory.PNUnexpectedDisconnectCategory; + } catch (SocketTimeoutException socketTimeoutException) { + pubnubException.pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_TIMEOUT); + pnStatusCategory = PNStatusCategory.PNTimeoutCategory; + } catch (Throwable throwable1) { + pubnubException.pubnubError(PubNubErrorBuilder.PNERROBJ_HTTP_ERROR); + } + + callback.onResponse(null, createStatusResponse(pnStatusCategory, null, pubnubException.build())); + + } + }); + } + + public void retry() { + silenceFailures = false; + async(cachedCallback); + }; + + /** + * cancel the operation but do not alert anybody, useful for restarting the heartbeats and subscribe loops. + */ + public void silentCancel() { + if (call != null && !call.isCanceled()) { + this.silenceFailures = true; + call.cancel(); + } + } + + private PNStatus createStatusResponse(PNStatusCategory category, Response response, Exception throwable) { + PNStatus.PNStatusBuilder pnStatus = PNStatus.builder(); + + pnStatus.executedEndpoint(this); + + if (response == null || throwable != null) { + pnStatus.error(true); + } + + if (throwable != null) { + PNErrorData pnErrorData = new PNErrorData(throwable.getMessage(), throwable); + pnStatus.errorData(pnErrorData); + } + + if (response != null) { + pnStatus.statusCode(response.code()); + pnStatus.tlsEnabled(response.raw().request().url().isHttps()); + pnStatus.origin(response.raw().request().url().host()); + pnStatus.uuid(response.raw().request().url().queryParameter("uuid")); + pnStatus.authKey(response.raw().request().url().queryParameter("auth")); + pnStatus.clientRequest(response.raw().request()); + } + + pnStatus.operation(getOperationType()); + pnStatus.category(category); + pnStatus.affectedChannels(getAffectedChannels()); + pnStatus.affectedChannelGroups(getAffectedChannelGroups()); + + return pnStatus.build(); + } + + protected final Map createBaseParams() { + Map params = new HashMap<>(); + + params.put("pnsdk", "PubNub-Java-Unified/".concat(this.pubnub.getVersion())); + params.put("uuid", this.pubnub.getConfiguration().getUuid()); + + // add the auth key for publish and subscribe. + if (this.pubnub.getConfiguration().getAuthKey() != null && isAuthRequired()) { + params.put("auth", pubnub.getConfiguration().getAuthKey()); + } + + return params; + } + + protected List getAffectedChannels() { + return null; + } + + protected List getAffectedChannelGroups() { + return null; + } + + protected abstract void validateParams() throws PubNubException; + + protected abstract Call doWork(Map baseParams) throws PubNubException; + + protected abstract Output createResponse(Response input) throws PubNubException; + + protected abstract PNOperationType getOperationType(); + + protected abstract boolean isAuthRequired(); + +} diff --git a/src/main/java/com/pubnub/api/endpoints/History.java b/src/main/java/com/pubnub/api/endpoints/History.java new file mode 100644 index 000000000..aee10e607 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/History.java @@ -0,0 +1,151 @@ +package com.pubnub.api.endpoints; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.pubnub.api.vendor.Crypto; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.history.PNHistoryItemResult; +import com.pubnub.api.models.consumer.history.PNHistoryResult; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.http.GET; +import retrofit2.http.Path; +import retrofit2.http.QueryMap; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class History extends Endpoint { + private static final int MAX_COUNT = 100; + @Setter + private String channel; + @Setter + private Long start; + @Setter + private Long end; + @Setter + private Boolean reverse; + @Setter + private Integer count; + @Setter + private Boolean includeTimetoken; + + public History(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + } + + private interface HistoryService { + @GET("v2/history/sub-key/{subKey}/channel/{channel}") + Call fetchHistory(@Path("subKey") String subKey, + @Path("channel") String channel, + @QueryMap Map options); + } + + @Override + protected void validateParams() throws PubNubException { + if (channel == null || channel.isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_MISSING).build(); + } + } + + @Override + protected Call doWork(Map params) { + + HistoryService service = this.getRetrofit().create(HistoryService.class); + + if (reverse != null) { + params.put("reverse", String.valueOf(reverse)); + } + + if (includeTimetoken != null) { + params.put("include_token", String.valueOf(includeTimetoken)); + } + + if (count != null && count > 0 && count <= MAX_COUNT) { + params.put("count", String.valueOf(count)); + } else { + params.put("count", "100"); + } + + if (start != null) { + params.put("start", Long.toString(start).toLowerCase()); + } + if (end != null) { + params.put("end", Long.toString(end).toLowerCase()); + } + + return service.fetchHistory(this.getPubnub().getConfiguration().getSubscribeKey(), channel, params); + } + + @Override + protected PNHistoryResult createResponse(Response input) throws PubNubException { + PNHistoryResult.PNHistoryResultBuilder historyData = PNHistoryResult.builder(); + List messages = new ArrayList<>(); + + if (input.body() != null) { + historyData.startTimetoken(input.body().get(1).asLong()); + historyData.endTimetoken(input.body().get(2).asLong()); + + ArrayNode historyItems = (ArrayNode) input.body().get(0); + + for (final JsonNode historyEntry : historyItems) { + PNHistoryItemResult.PNHistoryItemResultBuilder historyItem = PNHistoryItemResult.builder(); + JsonNode message; + + if (includeTimetoken != null && includeTimetoken) { + historyItem.timetoken(historyEntry.get("timetoken").asLong()); + message = processMessage(historyEntry.get("message")); + } else { + message = processMessage(historyEntry); + } + + historyItem.entry(message); + messages.add(historyItem.build()); + } + + historyData.messages(messages); + } + + return historyData.build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNHistoryOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + + private JsonNode processMessage(JsonNode message) throws PubNubException { + if (this.getPubnub().getConfiguration().getCipherKey() == null) { + return message; + } + + Crypto crypto = new Crypto(this.getPubnub().getConfiguration().getCipherKey()); + String outputText = crypto.decrypt(message.asText()); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode outputObject; + try { + outputObject = mapper.readValue(outputText, JsonNode.class); + } catch (IOException e) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).errormsg(e.getMessage()).build(); + } + + return outputObject; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/Time.java b/src/main/java/com/pubnub/api/endpoints/Time.java new file mode 100644 index 000000000..a09e12408 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/Time.java @@ -0,0 +1,61 @@ +package com.pubnub.api.endpoints; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.PNTimeResult; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.http.GET; +import retrofit2.http.QueryMap; + +import java.util.List; +import java.util.Map; + +public class Time extends Endpoint, PNTimeResult> { + + public Time(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + } + + private interface TimeService { + @GET("/time/0") + Call> fetchTime(@QueryMap Map options); + } + + @Override + protected final void validateParams() throws PubNubException { + + } + + @Override + protected final Call> doWork(Map params) { + TimeService service = this.getRetrofit().create(TimeService.class); + return service.fetchTime(params); + } + + @Override + protected final PNTimeResult createResponse(final Response> input) throws PubNubException { + PNTimeResult.PNTimeResultBuilder timeData = PNTimeResult.builder(); + + if (input.body() == null || input.body().size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + timeData.timetoken(input.body().get(0)); + return timeData.build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNTimeOperation; + } + + @Override + protected boolean isAuthRequired() { + return false; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/access/AccessManagerService.java b/src/main/java/com/pubnub/api/endpoints/access/AccessManagerService.java new file mode 100644 index 000000000..16c8584fd --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/access/AccessManagerService.java @@ -0,0 +1,21 @@ +package com.pubnub.api.endpoints.access; + +import com.pubnub.api.models.server.access_manager.AccessManagerGrantPayload; +import com.pubnub.api.models.server.Envelope; +import com.pubnub.api.models.server.access_manager.AccessManagerAuditPayload; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Path; +import retrofit2.http.QueryMap; + +import java.util.Map; + +public interface AccessManagerService { + + @GET("/v1/auth/grant/sub-key/{subKey}") + Call> grant(@Path("subKey") String subKey, @QueryMap Map options); + + @GET("/v1/auth/audit/sub-key/{subKey}") + Call> audit(@Path("subKey") String subKey, @QueryMap Map options); + +} diff --git a/src/main/java/com/pubnub/api/endpoints/access/Audit.java b/src/main/java/com/pubnub/api/endpoints/access/Audit.java new file mode 100644 index 000000000..d8fccc8a3 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/access/Audit.java @@ -0,0 +1,120 @@ +package com.pubnub.api.endpoints.access; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerAuditResult; +import com.pubnub.api.models.server.Envelope; +import com.pubnub.api.models.server.access_manager.AccessManagerAuditPayload; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class Audit extends Endpoint, PNAccessManagerAuditResult> { + + @Setter + private List authKeys; + @Setter + private String channel; + @Setter + private String channelGroup; + + public Audit(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + authKeys = new ArrayList<>(); + } + + @Override + protected void validateParams() throws PubNubException { + if (authKeys.size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_AUTH_KEYS_MISSING).build(); + } + if (this.getPubnub().getConfiguration().getSecretKey() == null || this.getPubnub().getConfiguration().getSecretKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SECRET_KEY_MISSING).build(); + } + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + if (this.getPubnub().getConfiguration().getPublishKey() == null || this.getPubnub().getConfiguration().getPublishKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PUBLISH_KEY_MISSING).build(); + } + if (channel == null && channelGroup == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_AND_GROUP_MISSING).build(); + } + } + + @Override + protected Call> doWork(Map queryParams) throws PubNubException { + String signature; + + int timestamp = this.getPubnub().getTimestamp(); + + String signInput = this.getPubnub().getConfiguration().getSubscribeKey() + "\n" + + this.getPubnub().getConfiguration().getPublishKey() + "\n" + + "audit" + "\n"; + + queryParams.put("timestamp", String.valueOf(timestamp)); + + if (channel != null) { + queryParams.put("channel", channel); + } + + if (channelGroup != null) { + queryParams.put("channel-group", channelGroup); + } + + if (authKeys.size() > 0) { + queryParams.put("auth", PubNubUtil.joinString(authKeys, ",")); + } + + signInput += PubNubUtil.preparePamArguments(queryParams); + + signature = PubNubUtil.signSHA256(this.getPubnub().getConfiguration().getSecretKey(), signInput); + + queryParams.put("signature", signature); + + AccessManagerService service = this.getRetrofit().create(AccessManagerService.class); + return service.audit(this.getPubnub().getConfiguration().getSubscribeKey(), queryParams); + } + + @Override + protected PNAccessManagerAuditResult createResponse(final Response> input) throws PubNubException { + PNAccessManagerAuditResult.PNAccessManagerAuditResultBuilder pnAccessManagerAuditResult = PNAccessManagerAuditResult.builder(); + + if (input.body() == null || input.body().getPayload() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + AccessManagerAuditPayload auditPayload = input.body().getPayload(); + pnAccessManagerAuditResult + .authKeys(auditPayload.getAuthKeys()) + .channel(auditPayload.getChannel()) + .channelGroup(auditPayload.getChannelGroup()) + .level(auditPayload.getLevel()) + .subscribeKey(auditPayload.getSubscribeKey()); + + + return pnAccessManagerAuditResult.build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNAccessManagerAudit; + } + + @Override + protected boolean isAuthRequired() { + return false; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/access/Grant.java b/src/main/java/com/pubnub/api/endpoints/access/Grant.java new file mode 100644 index 000000000..e7497c862 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/access/Grant.java @@ -0,0 +1,180 @@ +package com.pubnub.api.endpoints.access; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerGrantResult; +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeyData; +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeysData; +import com.pubnub.api.models.server.Envelope; +import com.pubnub.api.models.server.access_manager.AccessManagerGrantPayload; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +@Accessors(chain = true, fluent = true) +public class Grant extends Endpoint, PNAccessManagerGrantResult> { + + @Setter + private boolean read; + @Setter + private boolean write; + @Setter + private boolean manage; + @Setter + private Integer ttl; + + + @Setter + private List authKeys; + @Setter + private List channels; + @Setter + private List channelGroups; + + public Grant(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + authKeys = new ArrayList<>(); + channels = new ArrayList<>(); + channelGroups = new ArrayList<>(); + } + + @Override + protected void validateParams() throws PubNubException { + if (authKeys.size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_AUTH_KEYS_MISSING).build(); + } + if (this.getPubnub().getConfiguration().getSecretKey() == null || this.getPubnub().getConfiguration().getSecretKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SECRET_KEY_MISSING).build(); + } + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + if (this.getPubnub().getConfiguration().getPublishKey() == null || this.getPubnub().getConfiguration().getPublishKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PUBLISH_KEY_MISSING).build(); + } + if (channels.size() == 0 && channelGroups.size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_AND_GROUP_MISSING).build(); + } + } + + @Override + protected Call> doWork(Map queryParams) throws PubNubException { + String signature; + + String signInput = this.getPubnub().getConfiguration().getSubscribeKey() + "\n" + + this.getPubnub().getConfiguration().getPublishKey() + "\n" + + "grant" + "\n"; + + queryParams.put("timestamp", String.valueOf(this.getPubnub().getTimestamp())); + + if (channels.size() > 0) { + queryParams.put("channel", PubNubUtil.joinString(channels, ",")); + } + + if (channelGroups.size() > 0) { + queryParams.put("channel-group", PubNubUtil.joinString(channelGroups, ",")); + } + + if (authKeys.size() > 0) { + queryParams.put("auth", PubNubUtil.joinString(authKeys, ",")); + } + + if (ttl != null && ttl >= -1) { + queryParams.put("ttl", String.valueOf(ttl)); + } + + queryParams.put("r", (read) ? "1" : "0"); + queryParams.put("w", (write) ? "1" : "0"); + queryParams.put("m", (manage) ? "1" : "0"); + + signInput += PubNubUtil.preparePamArguments(queryParams); + + signature = PubNubUtil.signSHA256(this.getPubnub().getConfiguration().getSecretKey(), signInput); + + queryParams.put("signature", signature); + + AccessManagerService service = this.getRetrofit().create(AccessManagerService.class); + return service.grant(this.getPubnub().getConfiguration().getSubscribeKey(), queryParams); + } + + @Override + protected PNAccessManagerGrantResult createResponse(Response> input) throws PubNubException { + ObjectMapper mapper = new ObjectMapper(); + PNAccessManagerGrantResult.PNAccessManagerGrantResultBuilder pnAccessManagerGrantResult = PNAccessManagerGrantResult.builder(); + + if (input.body() == null || input.body().getPayload() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + AccessManagerGrantPayload data = input.body().getPayload(); + Map> constructedChannels = new HashMap<>(); + Map> constructedGroups = new HashMap<>(); + + // we have a case of a singular channel. + if (data.getChannel() != null) { + constructedChannels.put(data.getChannel(), data.getAuthKeys()); + } + + if (channelGroups != null) { + if (channelGroups.size() == 1) { + constructedGroups.put(data.getChannelGroups().asText(), data.getAuthKeys()); + } else if (channelGroups.size() > 1) { + try { + HashMap channelGroupKeySet = mapper.readValue(data.getChannelGroups().toString(), + new TypeReference>() { + }); +// for (String fetchedChannelGroup : channelGroupKeySet.keySet()) { +// constructedGroups.put(fetchedChannelGroup, channelGroupKeySet.get(fetchedChannelGroup).getAuthKeys()); +// } + for (Map.Entry entry : channelGroupKeySet.entrySet()) { + constructedGroups.put(entry.getKey(), entry.getValue().getAuthKeys()); + } + } catch (IOException e) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).errormsg(e.getMessage()).build(); + } + } + } + + + if (data.getChannels() != null) { + for (String fetchedChannel : data.getChannels().keySet()) { + constructedChannels.put(fetchedChannel, data.getChannels().get(fetchedChannel).getAuthKeys()); + } + } + + + return pnAccessManagerGrantResult + .subscribeKey(data.getSubscribeKey()) + .level(data.getLevel()) + .ttl(data.getTtl()) + .channels(constructedChannels) + .channelGroups(constructedGroups) + .build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNAccessManagerGrant; + } + + @Override + protected boolean isAuthRequired() { + return false; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/channel_groups/AddChannelChannelGroup.java b/src/main/java/com/pubnub/api/endpoints/channel_groups/AddChannelChannelGroup.java new file mode 100644 index 000000000..1156ca50d --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/channel_groups/AddChannelChannelGroup.java @@ -0,0 +1,74 @@ +package com.pubnub.api.endpoints.channel_groups; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAddChannelResult; +import com.pubnub.api.models.server.Envelope; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class AddChannelChannelGroup extends Endpoint { + @Setter + private String channelGroup; + @Setter + private List channels; + + + public AddChannelChannelGroup(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + channels = new ArrayList<>(); + } + + @Override + protected void validateParams() throws PubNubException { + if (channelGroup == null || channelGroup.isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_GROUP_MISSING).build(); + } + if (channels.size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_MISSING).build(); + } + } + + @Override + protected Call doWork(final Map params) { + ChannelGroupService service = this.getRetrofit().create(ChannelGroupService.class); + + if (channels.size() > 0) { + params.put("add", PubNubUtil.joinString(channels, ",")); + } + + return service.addChannelChannelGroup(this.getPubnub().getConfiguration().getSubscribeKey(), channelGroup, params); + } + + @Override + protected PNChannelGroupsAddChannelResult createResponse(Response input) throws PubNubException { + if (input.body() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + return PNChannelGroupsAddChannelResult.builder().build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNAddChannelsToGroupOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/channel_groups/AllChannelsChannelGroup.java b/src/main/java/com/pubnub/api/endpoints/channel_groups/AllChannelsChannelGroup.java new file mode 100644 index 000000000..fdc543128 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/channel_groups/AllChannelsChannelGroup.java @@ -0,0 +1,69 @@ +package com.pubnub.api.endpoints.channel_groups; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAllChannelsResult; +import com.pubnub.api.models.server.Envelope; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class AllChannelsChannelGroup extends Endpoint, PNChannelGroupsAllChannelsResult> { + @Setter + private String channelGroup; + + public AllChannelsChannelGroup(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + } + + @Override + protected void validateParams() throws PubNubException { + if (channelGroup == null || channelGroup.isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_GROUP_MISSING).build(); + } + } + + @Override + protected Call> doWork(Map params) { + ChannelGroupService service = this.getRetrofit().create(ChannelGroupService.class); + + return service.allChannelsChannelGroup(this.getPubnub().getConfiguration().getSubscribeKey(), channelGroup, params); + } + + @Override + protected PNChannelGroupsAllChannelsResult createResponse(Response> input) throws PubNubException { + Map stateMappings; + + if (input.body() == null || input.body().getPayload() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + stateMappings = (Map) input.body().getPayload(); + List channels = (ArrayList) stateMappings.get("channels"); + + return PNChannelGroupsAllChannelsResult.builder() + .channels(channels) + .build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNChannelsForGroupOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/channel_groups/ChannelGroupService.java b/src/main/java/com/pubnub/api/endpoints/channel_groups/ChannelGroupService.java new file mode 100644 index 000000000..be35edbc2 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/channel_groups/ChannelGroupService.java @@ -0,0 +1,37 @@ +package com.pubnub.api.endpoints.channel_groups; + +import com.pubnub.api.models.server.Envelope; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Path; +import retrofit2.http.QueryMap; + +import java.util.Map; + + +public interface ChannelGroupService { + @GET("v1/channel-registration/sub-key/{subKey}/channel-group") + Call> listAllChannelGroup(@Path("subKey") String subKey, + @QueryMap Map options); + + @GET("v1/channel-registration/sub-key/{subKey}/channel-group/{group}") + Call> allChannelsChannelGroup(@Path("subKey") String subKey, + @Path("group") String group, + @QueryMap Map options); + + @GET("v1/channel-registration/sub-key/{subKey}/channel-group/{group}") + Call addChannelChannelGroup(@Path("subKey") String subKey, + @Path("group") String group, + @QueryMap Map options); + + @GET("v1/channel-registration/sub-key/{subKey}/channel-group/{group}") + Call removeChannel(@Path("subKey") String subKey, + @Path("group") String group, + @QueryMap Map options); + + @GET("v1/channel-registration/sub-key/{subKey}/channel-group/{group}/remove") + Call deleteChannelGroup(@Path("subKey") String subKey, + @Path("group") String group, + @QueryMap Map options); +} + diff --git a/src/main/java/com/pubnub/api/endpoints/channel_groups/DeleteChannelGroup.java b/src/main/java/com/pubnub/api/endpoints/channel_groups/DeleteChannelGroup.java new file mode 100644 index 000000000..c191f71f2 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/channel_groups/DeleteChannelGroup.java @@ -0,0 +1,59 @@ +package com.pubnub.api.endpoints.channel_groups; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsDeleteGroupResult; +import com.pubnub.api.models.server.Envelope; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class DeleteChannelGroup extends Endpoint { + @Setter + private String channelGroup; + + public DeleteChannelGroup(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + } + + @Override + protected void validateParams() throws PubNubException { + if (channelGroup == null || channelGroup.isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_GROUP_MISSING).build(); + } + } + + @Override + protected Call doWork(Map params) { + ChannelGroupService service = this.getRetrofit().create(ChannelGroupService.class); + + return service.deleteChannelGroup(this.getPubnub().getConfiguration().getSubscribeKey(), channelGroup, params); + } + + @Override + protected PNChannelGroupsDeleteGroupResult createResponse(Response input) throws PubNubException { + if (input.body() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + return PNChannelGroupsDeleteGroupResult.builder().build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNRemoveGroupOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/channel_groups/ListAllChannelGroup.java b/src/main/java/com/pubnub/api/endpoints/channel_groups/ListAllChannelGroup.java new file mode 100644 index 000000000..ee888a602 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/channel_groups/ListAllChannelGroup.java @@ -0,0 +1,64 @@ +package com.pubnub.api.endpoints.channel_groups; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsListAllResult; +import com.pubnub.api.models.server.Envelope; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class ListAllChannelGroup extends Endpoint, PNChannelGroupsListAllResult> { + + public ListAllChannelGroup(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + } + + @Override + protected void validateParams() throws PubNubException { + } + + @Override + protected Call> doWork(Map params) { + + ChannelGroupService service = this.getRetrofit().create(ChannelGroupService.class); + + return service.listAllChannelGroup(this.getPubnub().getConfiguration().getSubscribeKey(), params); + } + + @Override + protected PNChannelGroupsListAllResult createResponse(Response> input) throws PubNubException { + Map stateMappings; + + if (input.body() == null || input.body().getPayload() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + stateMappings = (Map) input.body().getPayload(); + List groups = (ArrayList) stateMappings.get("groups"); + + return PNChannelGroupsListAllResult.builder() + .groups(groups) + .build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNChannelGroupsOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/channel_groups/RemoveChannelChannelGroup.java b/src/main/java/com/pubnub/api/endpoints/channel_groups/RemoveChannelChannelGroup.java new file mode 100644 index 000000000..af2981459 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/channel_groups/RemoveChannelChannelGroup.java @@ -0,0 +1,73 @@ +package com.pubnub.api.endpoints.channel_groups; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsRemoveChannelResult; +import com.pubnub.api.models.server.Envelope; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class RemoveChannelChannelGroup extends Endpoint { + @Setter + private String channelGroup; + @Setter + private List channels; + + + public RemoveChannelChannelGroup(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + channels = new ArrayList<>(); + } + + @Override + protected void validateParams() throws PubNubException { + if (channelGroup == null || channelGroup.isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_GROUP_MISSING).build(); + } + if (channels.size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_MISSING).build(); + } + } + + @Override + protected Call doWork(Map params) { + ChannelGroupService service = this.getRetrofit().create(ChannelGroupService.class); + + if (channels.size() > 0) { + params.put("remove", PubNubUtil.joinString(channels, ",")); + } + + return service.removeChannel(this.getPubnub().getConfiguration().getSubscribeKey(), channelGroup, params); + } + + @Override + protected PNChannelGroupsRemoveChannelResult createResponse(Response input) throws PubNubException { + if (input.body() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + return PNChannelGroupsRemoveChannelResult.builder().build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNRemoveChannelsFromGroupOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/presence/GetState.java b/src/main/java/com/pubnub/api/endpoints/presence/GetState.java new file mode 100644 index 000000000..13df639db --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/presence/GetState.java @@ -0,0 +1,87 @@ +package com.pubnub.api.endpoints.presence; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.presence.PNGetStateResult; +import com.pubnub.api.models.server.Envelope; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class GetState extends Endpoint, PNGetStateResult> { + + @Setter + private List channels; + @Setter + private List channelGroups; + @Setter + private String uuid; + + public GetState(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + channels = new ArrayList<>(); + channelGroups = new ArrayList<>(); + } + + @Override + protected void validateParams() throws PubNubException { + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + if (channels.size() == 0 && channelGroups.size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_AND_GROUP_MISSING).build(); + } + } + + @Override + protected Call> doWork(Map params) { + PresenceService service = this.getRetrofit().create(PresenceService.class); + + if (channelGroups.size() > 0) { + params.put("channel-group", PubNubUtil.joinString(channelGroups, ",")); + } + + String channelCSV = channels.size() > 0 ? PubNubUtil.joinString(channels, ",") : ","; + + String selectedUUID = uuid != null ? uuid : this.getPubnub().getConfiguration().getUuid(); + + return service.getState(this.getPubnub().getConfiguration().getSubscribeKey(), channelCSV, selectedUUID, params); + } + + @Override + protected PNGetStateResult createResponse(final Response> input) throws PubNubException { + Map stateMappings; + + if (channels.size() == 1 && channelGroups.size() == 0) { + stateMappings = new HashMap<>(); + stateMappings.put(channels.get(0), input.body().getPayload()); + } else { + stateMappings = (Map) input.body().getPayload(); + } + + return PNGetStateResult.builder().stateByUUID(stateMappings).build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNGetState; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/presence/Heartbeat.java b/src/main/java/com/pubnub/api/endpoints/presence/Heartbeat.java new file mode 100644 index 000000000..502ea5cf8 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/presence/Heartbeat.java @@ -0,0 +1,99 @@ +package com.pubnub.api.endpoints.presence; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.server.Envelope; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class Heartbeat extends Endpoint { + + @Setter + private Object state; + @Setter + private List channels; + @Setter + private List channelGroups; + + public Heartbeat(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + channels = new ArrayList<>(); + channelGroups = new ArrayList<>(); + } + + @Override + protected void validateParams() throws PubNubException { + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + if (channels.size() == 0 && channelGroups.size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_AND_GROUP_MISSING).build(); + } + } + + @Override + protected Call doWork(Map params) throws PubNubException { + ObjectWriter ow = new ObjectMapper().writer(); + + params.put("heartbeat", String.valueOf(this.getPubnub().getConfiguration().getPresenceTimeout())); + + if (channelGroups.size() > 0) { + params.put("channel-group", PubNubUtil.joinString(channelGroups, ",")); + } + + String channelsCSV; + + if (channels.size() > 0) { + channelsCSV = PubNubUtil.joinString(channels, ","); + } else { + channelsCSV = ","; + } + + if (state != null) { + String stringifiedState; + + try { + stringifiedState = ow.writeValueAsString(state); + } catch (JsonProcessingException e) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_INVALID_ARGUMENTS).errormsg(e.getMessage()).build(); + } + + stringifiedState = PubNubUtil.urlEncode(stringifiedState); + params.put("state", stringifiedState); + } + + PresenceService service = this.getRetrofit().create(PresenceService.class); + return service.heartbeat(this.getPubnub().getConfiguration().getSubscribeKey(), channelsCSV, params); + } + + @Override + protected Boolean createResponse(Response input) throws PubNubException { + return true; + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNHeartbeatOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/presence/HereNow.java b/src/main/java/com/pubnub/api/endpoints/presence/HereNow.java new file mode 100644 index 000000000..6da52559b --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/presence/HereNow.java @@ -0,0 +1,199 @@ +package com.pubnub.api.endpoints.presence; + + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.presence.PNHereNowChannelData; +import com.pubnub.api.models.consumer.presence.PNHereNowOccupantData; +import com.pubnub.api.models.consumer.presence.PNHereNowResult; +import com.pubnub.api.models.server.Envelope; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class HereNow extends Endpoint, PNHereNowResult> { + @Setter + private List channels; + @Setter + private List channelGroups; + @Setter + private Boolean includeState; + @Setter + private Boolean includeUUIDs; + + public HereNow(PubNub pubnubInstance, Retrofit retrofit) { + super(pubnubInstance, retrofit); + channels = new ArrayList<>(); + channelGroups = new ArrayList<>(); + } + + + @Override + protected void validateParams() throws PubNubException { + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + } + + @Override + protected Call> doWork(Map params) { + + if (includeState == null) { + includeState = false; + } + + if (includeUUIDs == null) { + includeUUIDs = true; + } + + String channelCSV; + + PresenceService service = this.getRetrofit().create(PresenceService.class); + + if (includeState) { + params.put("state", "1"); + } + if (!includeUUIDs) { + params.put("disable_uuids", "1"); + } + if (channelGroups.size() > 0) { + params.put("channel-group", PubNubUtil.joinString(channelGroups, ",")); + } + + if (channels.size() > 0) { + channelCSV = PubNubUtil.joinString(channels, ","); + } else { + channelCSV = ","; + } + + if (channels.size() > 0 || channelGroups.size() > 0) { + return service.hereNow(this.getPubnub().getConfiguration().getSubscribeKey(), channelCSV, params); + } else { + return service.globalHereNow(this.getPubnub().getConfiguration().getSubscribeKey(), params); + } + } + + @Override + protected PNHereNowResult createResponse(Response> input) { + PNHereNowResult herenowData; + + if (channels.size() > 1 || channelGroups.size() > 0) { + herenowData = parseMultipleChannelResponse(input.body().getPayload()); + } else { + herenowData = parseSingleChannelResponse(input.body()); + } + + return herenowData; + } + + private PNHereNowResult parseSingleChannelResponse(Envelope input) { + PNHereNowResult hereNowData = PNHereNowResult.builder() + .totalChannels(1) + .channels(new HashMap()) + .totalOccupancy(input.getOccupancy()) + .build(); + + PNHereNowChannelData.PNHereNowChannelDataBuilder hereNowChannelData = PNHereNowChannelData.builder() + .channelName(channels.get(0)) + .occupancy(input.getOccupancy()); + + if (includeUUIDs) { + hereNowChannelData.occupants(prepareOccupantData(input.getUuids())); + hereNowData.getChannels().put(channels.get(0), hereNowChannelData.build()); + } + + return hereNowData; + } + + private PNHereNowResult parseMultipleChannelResponse(Object input) { + Map parsedInput = (Map) input; + + PNHereNowResult hereNowData = PNHereNowResult.builder() + .channels(new HashMap()) + .totalChannels((Integer) parsedInput.get("total_channels")) + .totalOccupancy((Integer) parsedInput.get("total_occupancy")) + .build(); + + Map channelsParam = (HashMap) parsedInput.get("channels"); + + for (Map.Entry entry : channelsParam.entrySet()) { + Map channel = (Map) channelsParam.get(entry.getKey()); + + PNHereNowChannelData.PNHereNowChannelDataBuilder hereNowChannelData = PNHereNowChannelData.builder() + .channelName(entry.getKey()) + .occupancy((Integer) channel.get("occupancy")); + + if (includeUUIDs) { + hereNowChannelData.occupants(prepareOccupantData(channel.get("uuids"))); + } else { + hereNowChannelData.occupants(null); + } + + hereNowData.getChannels().put(entry.getKey(), hereNowChannelData.build()); + } + +// for (String channelName : channelsParam.keySet()) { +// Map channel = (Map) channelsParam.get(channelName); +// +// PNHereNowChannelData.PNHereNowChannelDataBuilder hereNowChannelData = PNHereNowChannelData.builder() +// .channelName(channelName) +// .occupancy((Integer) channel.get("occupancy")); +// +// if (includeUUIDs) { +// hereNowChannelData.occupants(prepareOccupantData(channel.get("uuids"))); +// } else { +// hereNowChannelData.occupants(null); +// } +// +// hereNowData.getChannels().put(channelName, hereNowChannelData.build()); +// } + + return hereNowData; + } + + private List prepareOccupantData(Object input) { + List occupantsResults = new ArrayList(); + + if (includeState != null && includeState) { + for (Map occupant : (List>) input) { + PNHereNowOccupantData.PNHereNowOccupantDataBuilder hereNowOccupantData = PNHereNowOccupantData.builder(); + hereNowOccupantData.uuid((String) occupant.get("uuid")); + hereNowOccupantData.state(occupant.get("state")); + + occupantsResults.add(hereNowOccupantData.build()); + } + } else { + for (String occupant : (List) input) { + PNHereNowOccupantData.PNHereNowOccupantDataBuilder hereNowOccupantData = PNHereNowOccupantData.builder(); + hereNowOccupantData.uuid(occupant); + hereNowOccupantData.state(null); + + occupantsResults.add(hereNowOccupantData.build()); + } + } + return occupantsResults; + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNHereNowOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/presence/Leave.java b/src/main/java/com/pubnub/api/endpoints/presence/Leave.java new file mode 100644 index 000000000..390a6f237 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/presence/Leave.java @@ -0,0 +1,86 @@ +package com.pubnub.api.endpoints.presence; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.server.Envelope; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class Leave extends Endpoint { + @Setter + private List channels; + @Setter + private List channelGroups; + + public Leave(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + channels = new ArrayList<>(); + channelGroups = new ArrayList<>(); + } + + @Override + protected void validateParams() throws PubNubException { + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + if (channels.size() == 0 && channelGroups.size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_AND_GROUP_MISSING).build(); + } + } + + @Override + protected Call doWork(Map params) { + String channelCSV; + PresenceService service = this.getRetrofit().create(PresenceService.class); + + if (channelGroups.size() > 0) { + params.put("channel-group", PubNubUtil.joinString(channelGroups, ",")); + } + + if (channels.size() > 0) { + channelCSV = PubNubUtil.joinString(channels, ","); + } else { + channelCSV = ","; + } + + return service.leave(this.getPubnub().getConfiguration().getSubscribeKey(), channelCSV, params); + } + + @Override + protected Boolean createResponse(Response input) throws PubNubException { + return true; + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNUnsubscribeOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + + @Override + protected List getAffectedChannels() { + return channels; + } + + @Override + protected List getAffectedChannelGroups() { + return channelGroups; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/presence/PresenceService.java b/src/main/java/com/pubnub/api/endpoints/presence/PresenceService.java new file mode 100644 index 000000000..18373a8c2 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/presence/PresenceService.java @@ -0,0 +1,50 @@ +package com.pubnub.api.endpoints.presence; + +import com.pubnub.api.models.server.Envelope; +import com.pubnub.api.models.server.presence.WhereNowPayload; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Path; +import retrofit2.http.QueryMap; + +import java.util.Map; + +public interface PresenceService { + + @GET("v2/presence/sub-key/{subKey}/channel/{channel}/leave") + Call leave(@Path("subKey") String subKey, + @Path("channel") String channel, + @QueryMap Map options); + + @GET("v2/presence/sub-key/{subKey}/channel/{channel}/heartbeat") + Call heartbeat(@Path("subKey") String subKey, + @Path("channel") String channel, + @QueryMap Map options); + + @GET("v2/presence/sub-key/{subKey}/uuid/{uuid}") + Call> whereNow(@Path("subKey") String subKey, + @Path("uuid") String uuid, + @QueryMap Map options); + + @GET("v2/presence/sub_key/{subKey}") + Call> globalHereNow(@Path("subKey") String subKey, + @QueryMap Map options); + + @GET("v2/presence/sub_key/{subKey}/channel/{channel}") + Call> hereNow(@Path("subKey") String subKey, + @Path("channel") String channel, + @QueryMap Map options); + + @GET("v2/presence/sub-key/{subKey}/channel/{channel}/uuid/{uuid}") + Call> getState(@Path("subKey") String subKey, + @Path("channel") String channel, + @Path("uuid") String uuid, + @QueryMap Map options); + + @GET("v2/presence/sub-key/{subKey}/channel/{channel}/uuid/{uuid}/data") + Call>> setState(@Path("subKey") String subKey, + @Path("channel") String channel, + @Path("uuid") String uuid, + @QueryMap(encoded = true) Map options); + +} diff --git a/src/main/java/com/pubnub/api/endpoints/presence/SetState.java b/src/main/java/com/pubnub/api/endpoints/presence/SetState.java new file mode 100644 index 000000000..96f0f5137 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/presence/SetState.java @@ -0,0 +1,123 @@ +package com.pubnub.api.endpoints.presence; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.builder.dto.StateOperation; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.managers.SubscriptionManager; +import com.pubnub.api.models.consumer.presence.PNSetStateResult; +import com.pubnub.api.models.server.Envelope; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + + +@Accessors(chain = true, fluent = true) +public class SetState extends Endpoint>, PNSetStateResult> { + + @Getter(AccessLevel.NONE) + private SubscriptionManager subscriptionManager; + + @Setter + private List channels; + @Setter + private List channelGroups; + @Setter + private Object state; + @Setter + private String uuid; + + public SetState(PubNub pubnub, SubscriptionManager subscriptionManagerInstance, Retrofit retrofit) { + super(pubnub, retrofit); + this.subscriptionManager = subscriptionManagerInstance; + channels = new ArrayList<>(); + channelGroups = new ArrayList<>(); + } + + @Override + protected void validateParams() throws PubNubException { + if (state == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_STATE_MISSING).build(); + } + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + if (channels.size() == 0 && channelGroups.size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_AND_GROUP_MISSING).build(); + } + } + + @Override + protected Call>> doWork(Map params) throws PubNubException { + ObjectWriter ow = new ObjectMapper().writer(); + String selectedUUID = uuid != null ? uuid : this.getPubnub().getConfiguration().getUuid(); + String stringifiedState; + + // only store the state change if we are modifying it for ourselves. + if (selectedUUID.equals(this.getPubnub().getConfiguration().getUuid())) { + StateOperation stateOperation = StateOperation.builder() + .state(state) + .channels(channels) + .channelGroups(channelGroups) + .build(); + subscriptionManager.adaptStateBuilder(stateOperation); + } + + PresenceService service = this.getRetrofit().create(PresenceService.class); + + if (channelGroups.size() > 0) { + params.put("channel-group", PubNubUtil.joinString(channelGroups, ",")); + } + + try { + stringifiedState = ow.writeValueAsString(state); + } catch (JsonProcessingException e) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_INVALID_ARGUMENTS).errormsg(e.getMessage()).build(); + } + + stringifiedState = PubNubUtil.urlEncode(stringifiedState); + params.put("state", stringifiedState); + + String channelCSV = channels.size() > 0 ? PubNubUtil.joinString(channels, ",") : ","; + + return service.setState(this.getPubnub().getConfiguration().getSubscribeKey(), channelCSV, selectedUUID, params); + } + + @Override + protected PNSetStateResult createResponse(Response>> input) throws PubNubException { + + if (input.body() == null || input.body().getPayload() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + PNSetStateResult.PNSetStateResultBuilder pnSetStateResult = PNSetStateResult.builder() + .state(input.body().getPayload()); + + return pnSetStateResult.build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNSetStateOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/presence/WhereNow.java b/src/main/java/com/pubnub/api/endpoints/presence/WhereNow.java new file mode 100644 index 000000000..1ee729990 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/presence/WhereNow.java @@ -0,0 +1,66 @@ +package com.pubnub.api.endpoints.presence; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.presence.PNWhereNowResult; +import com.pubnub.api.models.server.Envelope; +import com.pubnub.api.models.server.presence.WhereNowPayload; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class WhereNow extends Endpoint, PNWhereNowResult> { + + @Setter + private String uuid; + + public WhereNow(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + } + + @Override + protected void validateParams() throws PubNubException { + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + } + + @Override + protected Call> doWork(Map params) { + PresenceService service = this.getRetrofit().create(PresenceService.class); + return service.whereNow(this.getPubnub().getConfiguration().getSubscribeKey(), + this.uuid != null ? this.uuid : this.getPubnub().getConfiguration().getUuid(), params); + } + + @Override + protected PNWhereNowResult createResponse(Response> input) throws PubNubException { + if (input.body() == null || input.body().getPayload() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + PNWhereNowResult pnPresenceWhereNowResult = PNWhereNowResult.builder() + .channels(input.body().getPayload().getChannels()) + .build(); + + return pnPresenceWhereNowResult; + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNWhereNowOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/pubsub/PubSubService.java b/src/main/java/com/pubnub/api/endpoints/pubsub/PubSubService.java new file mode 100644 index 000000000..ff5f35b19 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/pubsub/PubSubService.java @@ -0,0 +1,37 @@ +package com.pubnub.api.endpoints.pubsub; + +import com.pubnub.api.models.server.SubscribeEnvelope; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.Path; +import retrofit2.http.QueryMap; +import retrofit2.http.Headers; +import retrofit2.http.Body; +import java.util.List; +import java.util.Map; + +public interface PubSubService { + + @GET("v2/subscribe/{subKey}/{channel}/0") + Call subscribe(@Path("subKey") String subKey, + @Path("channel") String channel, + @QueryMap(encoded = true) Map options); + + @GET("publish/{pubKey}/{subKey}/0/{channel}/0/{message}") + Call> publish(@Path("pubKey") String pubKey, + @Path("subKey") String subKey, + @Path("channel") String channel, + @Path(value = "message", encoded = true) String message, + @QueryMap(encoded = true) Map options); + + @POST("publish/{pubKey}/{subKey}/0/{channel}/0") + @Headers("Content-Type: application/json; charset=UTF-8") + Call> publishWithPost(@Path("pubKey") String pubKey, + @Path("subKey") String subKey, + @Path("channel") String channel, + @Body Object body, + @QueryMap(encoded = true) Map options); + + +} diff --git a/src/main/java/com/pubnub/api/endpoints/pubsub/Publish.java b/src/main/java/com/pubnub/api/endpoints/pubsub/Publish.java new file mode 100644 index 000000000..d6c355002 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/pubsub/Publish.java @@ -0,0 +1,151 @@ +package com.pubnub.api.endpoints.pubsub; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.managers.PublishSequenceManager; +import com.pubnub.api.models.consumer.PNPublishResult; +import com.pubnub.api.vendor.Crypto; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class Publish extends Endpoint, PNPublishResult> { + + @Setter + private Object message; + @Setter + private String channel; + @Setter + private Boolean shouldStore; + @Setter + private Boolean usePOST; + @Setter + private Object meta; + @Setter + private Boolean replicate; + + private PublishSequenceManager publishSequenceManager; + + public Publish(PubNub pubnub, PublishSequenceManager providedPublishSequenceManager, Retrofit retrofit) { + super(pubnub, retrofit); + + this.publishSequenceManager = providedPublishSequenceManager; + this.replicate = true; + } + + @Override + protected final void validateParams() throws PubNubException { + if (message == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_MESSAGE_MISSING).build(); + } + if (channel == null || channel.isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_MISSING).build(); + } + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + if (this.getPubnub().getConfiguration().getPublishKey() == null || this.getPubnub().getConfiguration().getPublishKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PUBLISH_KEY_MISSING).build(); + } + } + + @Override + protected final Call> doWork(Map params) throws PubNubException { + String stringifiedMessage; + String stringifiedMeta; + ObjectMapper mapper = new ObjectMapper(); + + try { + stringifiedMessage = mapper.writeValueAsString(message); + } catch (JsonProcessingException e) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_INVALID_ARGUMENTS).errormsg(e.getMessage()).build(); + } + + if (meta != null) { + try { + stringifiedMeta = mapper.writeValueAsString(meta); + stringifiedMeta = PubNubUtil.urlEncode(stringifiedMeta); + params.put("meta", stringifiedMeta); + } catch (JsonProcessingException e) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_INVALID_ARGUMENTS).errormsg(e.getMessage()).build(); + } + } + + if (shouldStore != null) { + if (shouldStore) { + params.put("store", "1"); + } else { + params.put("store", "0"); + } + } + + params.put("seqn", String.valueOf(publishSequenceManager.getNextSequence())); + + if (!replicate) { + params.put("norep", "true"); + } + + if (this.getPubnub().getConfiguration().getCipherKey() != null) { + Crypto crypto = new Crypto(this.getPubnub().getConfiguration().getCipherKey()); + stringifiedMessage = crypto.encrypt(stringifiedMessage).replace("\n", ""); + } + + PubSubService service = this.getRetrofit().create(PubSubService.class); + + if (usePOST != null && usePOST) { + Object payloadToSend; + + if (this.getPubnub().getConfiguration().getCipherKey() != null) { + payloadToSend = stringifiedMessage; + } else { + payloadToSend = message; + } + + return service.publishWithPost(this.getPubnub().getConfiguration().getPublishKey(), + this.getPubnub().getConfiguration().getSubscribeKey(), + channel, payloadToSend, params); + } else { + + if (this.getPubnub().getConfiguration().getCipherKey() != null) { + stringifiedMessage = "\"".concat(stringifiedMessage).concat("\""); + } + + stringifiedMessage = PubNubUtil.urlEncode(stringifiedMessage); + + return service.publish(this.getPubnub().getConfiguration().getPublishKey(), + this.getPubnub().getConfiguration().getSubscribeKey(), + channel, stringifiedMessage, params); + } + } + + @Override + protected final PNPublishResult createResponse(final Response> input) throws PubNubException { + PNPublishResult.PNPublishResultBuilder pnPublishResult = PNPublishResult.builder(); + pnPublishResult.timetoken(Long.valueOf(input.body().get(2).toString())); + + return pnPublishResult.build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNPublishOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/pubsub/Subscribe.java b/src/main/java/com/pubnub/api/endpoints/pubsub/Subscribe.java new file mode 100644 index 000000000..b30065eff --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/pubsub/Subscribe.java @@ -0,0 +1,130 @@ +package com.pubnub.api.endpoints.pubsub; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.server.SubscribeEnvelope; +import lombok.Setter; +import lombok.experimental.Accessors; +import lombok.extern.slf4j.Slf4j; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Supports calling of the subscribe endpoints and deconstructs the response to POJO's. + */ +@Slf4j +@Accessors(chain = true, fluent = true) +public class Subscribe extends Endpoint { + + /** + * List of channels that will be called to subscribe. + */ + @Setter + private List channels; + /** + * List of channel groups that will be called with subscribe. + */ + @Setter + private List channelGroups; + + /** + * timeToken to subscribe with 0 for initial subscribe. + */ + @Setter + private Long timetoken; + + /** + * filterExpression used as part of PubSub V2 specification to filter on message. + */ + @Setter + private String filterExpression; + + /** + * region is used as part of PubSub V2 to help the server route traffic to best data center. + */ + @Setter + private String region; + + /** + * CreFte a new Subscribe instance endpoint. + * + * @param pubnub supplied pubnub instance. + */ + public Subscribe(final PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + channels = new ArrayList<>(); + channelGroups = new ArrayList<>(); + } + + @Override + protected final void validateParams() throws PubNubException { + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + if (channels.size() == 0 && channelGroups.size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_AND_GROUP_MISSING).build(); + } + } + + @Override + protected final Call doWork(final Map params) throws PubNubException { + PubSubService pubSubService = this.getRetrofit().create(PubSubService.class); + String channelCSV; + + if (channelGroups.size() > 0) { + params.put("channel-group", PubNubUtil.joinString(channelGroups, ",")); + } + + if (filterExpression != null && filterExpression.length() > 0) { + params.put("filter-expr", PubNubUtil.urlEncode(filterExpression)); + } + + if (timetoken != null) { + params.put("tt", timetoken.toString()); + } + + if (region != null) { + params.put("tr", region); + } + + if (channels.size() > 0) { + channelCSV = PubNubUtil.joinString(channels, ","); + } else { + channelCSV = ","; + } + + params.put("heartbeat", String.valueOf(this.getPubnub().getConfiguration().getPresenceTimeout())); + + return pubSubService.subscribe(this.getPubnub().getConfiguration().getSubscribeKey(), channelCSV, params); + } + + @Override + protected final SubscribeEnvelope createResponse(final Response input) throws PubNubException { + + if (input.body() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + return input.body(); + } + + @Override + protected final PNOperationType getOperationType() { + return PNOperationType.PNSubscribeOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/push/AddChannelsToPush.java b/src/main/java/com/pubnub/api/endpoints/push/AddChannelsToPush.java new file mode 100644 index 000000000..3041337a3 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/push/AddChannelsToPush.java @@ -0,0 +1,83 @@ +package com.pubnub.api.endpoints.push; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.enums.PNPushType; +import com.pubnub.api.models.consumer.push.PNPushAddChannelResult; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class AddChannelsToPush extends Endpoint, PNPushAddChannelResult> { + + @Setter + private PNPushType pushType; + @Setter + private List channels; + @Setter + private String deviceId; + + public AddChannelsToPush(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + channels = new ArrayList<>(); + } + + @Override + protected void validateParams() throws PubNubException { + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + if (pushType == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PUSH_TYPE_MISSING).build(); + } + if (deviceId == null || deviceId.isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_DEVICE_ID_MISSING).build(); + } + if (channels.size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_MISSING).build(); + } + } + + @Override + protected Call> doWork(Map baseParams) throws PubNubException { + baseParams.put("type", pushType.name().toLowerCase()); + + if (channels.size() != 0) { + baseParams.put("add", PubNubUtil.joinString(channels, ",")); + } + + PushService service = this.getRetrofit().create(PushService.class); + return service.modifyChannelsForDevice(this.getPubnub().getConfiguration().getSubscribeKey(), deviceId, baseParams); + + } + + @Override + protected PNPushAddChannelResult createResponse(Response> input) throws PubNubException { + if (input.body() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + return PNPushAddChannelResult.builder().build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNPushNotificationEnabledChannelsOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } +} diff --git a/src/main/java/com/pubnub/api/endpoints/push/ListPushProvisions.java b/src/main/java/com/pubnub/api/endpoints/push/ListPushProvisions.java new file mode 100644 index 000000000..d14cdac56 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/push/ListPushProvisions.java @@ -0,0 +1,70 @@ +package com.pubnub.api.endpoints.push; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.enums.PNPushType; +import com.pubnub.api.models.consumer.push.PNPushListProvisionsResult; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class ListPushProvisions extends Endpoint, PNPushListProvisionsResult> { + + @Setter + private PNPushType pushType; + @Setter + private String deviceId; + + public ListPushProvisions(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + } + + @Override + protected void validateParams() throws PubNubException { + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + if (pushType == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PUSH_TYPE_MISSING).build(); + } + if (deviceId == null || deviceId.isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_DEVICE_ID_MISSING).build(); + } + } + + @Override + protected Call> doWork(Map params) throws PubNubException { + params.put("type", pushType.name().toLowerCase()); + PushService service = this.getRetrofit().create(PushService.class); + return service.listChannelsForDevice(this.getPubnub().getConfiguration().getSubscribeKey(), deviceId, params); + } + + @Override + protected PNPushListProvisionsResult createResponse(Response> input) throws PubNubException { + if (input.body() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + return PNPushListProvisionsResult.builder().channels(input.body()).build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNPushNotificationEnabledChannelsOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/push/PushService.java b/src/main/java/com/pubnub/api/endpoints/push/PushService.java new file mode 100644 index 000000000..4f4da91f5 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/push/PushService.java @@ -0,0 +1,28 @@ +package com.pubnub.api.endpoints.push; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Path; +import retrofit2.http.QueryMap; + +import java.util.List; +import java.util.Map; + +public interface PushService { + + @GET("v1/push/sub-key/{subKey}/devices/{pushToken}") + Call> modifyChannelsForDevice(@Path("subKey") String subKey, + @Path("pushToken") String pushToken, + @QueryMap Map options); + + @GET("v1/push/sub-key/{subKey}/devices/{pushToken}/remove") + Call> removeAllChannelsForDevice(@Path("subKey") String subKey, + @Path("pushToken") String pushToken, + @QueryMap Map options); + + @GET("v1/push/sub-key/{subKey}/devices/{pushToken}") + Call> listChannelsForDevice(@Path("subKey") String subKey, + @Path("pushToken") String pushToken, + @QueryMap Map options); + +} diff --git a/src/main/java/com/pubnub/api/endpoints/push/RemoveAllPushChannelsForDevice.java b/src/main/java/com/pubnub/api/endpoints/push/RemoveAllPushChannelsForDevice.java new file mode 100644 index 000000000..2b91b3fb3 --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/push/RemoveAllPushChannelsForDevice.java @@ -0,0 +1,74 @@ +package com.pubnub.api.endpoints.push; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.enums.PNPushType; +import com.pubnub.api.models.consumer.push.PNPushRemoveAllChannelsResult; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class RemoveAllPushChannelsForDevice extends Endpoint, PNPushRemoveAllChannelsResult> { + + @Setter + private PNPushType pushType; + @Setter + private String deviceId; + + public RemoveAllPushChannelsForDevice(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + } + + @Override + protected void validateParams() throws PubNubException { + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + if (pushType == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PUSH_TYPE_MISSING).build(); + } + if (deviceId == null || deviceId.isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_DEVICE_ID_MISSING).build(); + } + } + + + @Override + protected Call> doWork(Map params) throws PubNubException { + params.put("type", pushType.name().toLowerCase()); + + PushService service = this.getRetrofit().create(PushService.class); + + return service.removeAllChannelsForDevice(this.getPubnub().getConfiguration().getSubscribeKey(), deviceId, params); + + } + + @Override + protected PNPushRemoveAllChannelsResult createResponse(Response> input) throws PubNubException { + if (input.body() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + return PNPushRemoveAllChannelsResult.builder().build(); + } + + @Override + protected PNOperationType getOperationType() { + return null; // PNOperationType.PNPushNotificationModifiedChannelsOperations; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + +} diff --git a/src/main/java/com/pubnub/api/endpoints/push/RemoveChannelsFromPush.java b/src/main/java/com/pubnub/api/endpoints/push/RemoveChannelsFromPush.java new file mode 100644 index 000000000..c0c325a0b --- /dev/null +++ b/src/main/java/com/pubnub/api/endpoints/push/RemoveChannelsFromPush.java @@ -0,0 +1,84 @@ +package com.pubnub.api.endpoints.push; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.enums.PNPushType; +import com.pubnub.api.models.consumer.push.PNPushRemoveChannelResult; +import lombok.Setter; +import lombok.experimental.Accessors; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Accessors(chain = true, fluent = true) +public class RemoveChannelsFromPush extends Endpoint, PNPushRemoveChannelResult> { + + @Setter + private PNPushType pushType; + @Setter + private List channels; + @Setter + private String deviceId; + + public RemoveChannelsFromPush(PubNub pubnub, Retrofit retrofit) { + super(pubnub, retrofit); + + channels = new ArrayList<>(); + } + + @Override + protected void validateParams() throws PubNubException { + if (this.getPubnub().getConfiguration().getSubscribeKey() == null || this.getPubnub().getConfiguration().getSubscribeKey().isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_SUBSCRIBE_KEY_MISSING).build(); + } + if (pushType == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PUSH_TYPE_MISSING).build(); + } + if (deviceId == null || deviceId.isEmpty()) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_DEVICE_ID_MISSING).build(); + } + if (channels.size() == 0) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_CHANNEL_MISSING).build(); + } + } + + @Override + protected Call> doWork(Map baseParams) throws PubNubException { + baseParams.put("type", pushType.name().toLowerCase()); + + if (channels.size() != 0) { + baseParams.put("remove", PubNubUtil.joinString(channels, ",")); + } + + PushService service = this.getRetrofit().create(PushService.class); + return service.modifyChannelsForDevice(this.getPubnub().getConfiguration().getSubscribeKey(), deviceId, baseParams); + + } + + @Override + protected PNPushRemoveChannelResult createResponse(Response> input) throws PubNubException { + if (input.body() == null) { + throw PubNubException.builder().pubnubError(PubNubErrorBuilder.PNERROBJ_PARSING_ERROR).build(); + } + + return PNPushRemoveChannelResult.builder().build(); + } + + @Override + protected PNOperationType getOperationType() { + return PNOperationType.PNRemovePushNotificationsFromChannelsOperation; + } + + @Override + protected boolean isAuthRequired() { + return true; + } +} diff --git a/src/main/java/com/pubnub/api/enums/PNHeartbeatNotificationOptions.java b/src/main/java/com/pubnub/api/enums/PNHeartbeatNotificationOptions.java new file mode 100644 index 000000000..abf74d8b9 --- /dev/null +++ b/src/main/java/com/pubnub/api/enums/PNHeartbeatNotificationOptions.java @@ -0,0 +1,10 @@ +package com.pubnub.api.enums; + + +public enum PNHeartbeatNotificationOptions { + + NONE, + FAILURES, + ALL + +} diff --git a/src/main/java/com/pubnub/api/enums/PNLogVerbosity.java b/src/main/java/com/pubnub/api/enums/PNLogVerbosity.java new file mode 100644 index 000000000..0c42cbf81 --- /dev/null +++ b/src/main/java/com/pubnub/api/enums/PNLogVerbosity.java @@ -0,0 +1,8 @@ +package com.pubnub.api.enums; + +public enum PNLogVerbosity { + + NONE, + BODY, + +} diff --git a/src/main/java/com/pubnub/api/enums/PNOperationType.java b/src/main/java/com/pubnub/api/enums/PNOperationType.java new file mode 100644 index 000000000..3400f918a --- /dev/null +++ b/src/main/java/com/pubnub/api/enums/PNOperationType.java @@ -0,0 +1,31 @@ +package com.pubnub.api.enums; + +/** + * Created by Max on 4/7/16. + */ +public enum PNOperationType { + PNSubscribeOperation, + PNUnsubscribeOperation, + PNPublishOperation, + PNHistoryOperation, + PNWhereNowOperation, + + PNHeartbeatOperation, + PNSetStateOperation, + PNAddChannelsToGroupOperation, + PNRemoveChannelsFromGroupOperation, + PNChannelGroupsOperation, + PNRemoveGroupOperation, + PNChannelsForGroupOperation, + PNPushNotificationEnabledChannelsOperation, + PNAddPushNotificationsOnChannelsOperation, + PNRemovePushNotificationsFromChannelsOperation, + PNRemoveAllPushNotificationsOperation, + PNTimeOperation, + + // CREATED + PNHereNowOperation, + PNGetState, + PNAccessManagerAudit, + PNAccessManagerGrant +} diff --git a/src/main/java/com/pubnub/api/enums/PNPushType.java b/src/main/java/com/pubnub/api/enums/PNPushType.java new file mode 100644 index 000000000..0799fc8b7 --- /dev/null +++ b/src/main/java/com/pubnub/api/enums/PNPushType.java @@ -0,0 +1,9 @@ +package com.pubnub.api.enums; + +public enum PNPushType { + + APNS, + MPNS, + GCM + +} diff --git a/src/main/java/com/pubnub/api/enums/PNReconnectionPolicy.java b/src/main/java/com/pubnub/api/enums/PNReconnectionPolicy.java new file mode 100644 index 000000000..55e6a9924 --- /dev/null +++ b/src/main/java/com/pubnub/api/enums/PNReconnectionPolicy.java @@ -0,0 +1,8 @@ +package com.pubnub.api.enums; + +public enum PNReconnectionPolicy { + + NONE, + LINEAR, + EXPONENTIAL +} diff --git a/src/main/java/com/pubnub/api/enums/PNStatusCategory.java b/src/main/java/com/pubnub/api/enums/PNStatusCategory.java new file mode 100644 index 000000000..5a6e80a8b --- /dev/null +++ b/src/main/java/com/pubnub/api/enums/PNStatusCategory.java @@ -0,0 +1,22 @@ +package com.pubnub.api.enums; + +public enum PNStatusCategory { + + PNUnknownCategory, + PNAcknowledgmentCategory, + PNAccessDeniedCategory, + PNTimeoutCategory, + PNNetworkIssuesCategory, + PNConnectedCategory, + PNReconnectedCategory, + PNDisconnectedCategory, + PNUnexpectedDisconnectCategory, + PNCancelledCategory, + PNBadRequestCategory, + PNMalformedFilterExpressionCategory, + PNMalformedResponseCategory, + PNDecryptionErrorCategory, + PNTLSConnectionFailedCategory, + PNTLSUntrustedCertificateCategory, + +} diff --git a/src/main/java/com/pubnub/api/managers/BasePathManager.java b/src/main/java/com/pubnub/api/managers/BasePathManager.java new file mode 100644 index 000000000..36b9c9f6c --- /dev/null +++ b/src/main/java/com/pubnub/api/managers/BasePathManager.java @@ -0,0 +1,73 @@ +package com.pubnub.api.managers; + +import com.pubnub.api.PNConfiguration; + +/** + * A stateful manager to support base path construction, proxying and cache busting. + */ +public class BasePathManager { + + /** + * PubNub configuration storage. + */ + private PNConfiguration config; + /** + * for cache busting, the current subdomain number used. + */ + private int currentSubdomain; + + /** + * if using cache busting, this is the max number of subdomains that are supported. + */ + private static final int MAX_SUBDOMAIN = 20; + /** + * default subdomain used if cache busting is disabled. + */ + private static final String DEFAULT_SUBDOMAIN = "pubsub"; + /** + * default base path if a custom one is not provided. + */ + private static final String DEFAULT_BASE_PATH = "pubnub.com"; + + /** + * Initialize the path management. + * @param initialConfig configuration object + */ + public BasePathManager(final PNConfiguration initialConfig) { + this.config = initialConfig; + currentSubdomain = 1; + } + + + /** + * Prepares a next usable base url. + * @return usable base url. + */ + public final String getBasePath() { + StringBuilder constructedUrl = new StringBuilder("http"); + + if (config.isSecure()) { + constructedUrl.append("s"); + } + + constructedUrl.append("://"); + + if (config.getOrigin() != null) { + constructedUrl.append(config.getOrigin()); + } else if (config.isCacheBusting()) { + constructedUrl.append("ps").append(currentSubdomain).append(".").append(DEFAULT_BASE_PATH); + + if (currentSubdomain == MAX_SUBDOMAIN) { + currentSubdomain = 1; + } else { + currentSubdomain += 1; + } + + } else { + constructedUrl.append(DEFAULT_SUBDOMAIN).append(".").append(DEFAULT_BASE_PATH); + } + + return constructedUrl.toString(); + } + +} diff --git a/src/main/java/com/pubnub/api/managers/ListenerManager.java b/src/main/java/com/pubnub/api/managers/ListenerManager.java new file mode 100644 index 000000000..26e43706e --- /dev/null +++ b/src/main/java/com/pubnub/api/managers/ListenerManager.java @@ -0,0 +1,53 @@ +package com.pubnub.api.managers; + +import com.pubnub.api.PubNub; +import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.pubsub.PNMessageResult; +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; + +import java.util.ArrayList; +import java.util.List; + +public class ListenerManager { + + private List listeners; + private PubNub pubnub; + + public ListenerManager(PubNub pubnubInstance) { + this.listeners = new ArrayList<>(); + this.pubnub = pubnubInstance; + } + + public final void addListener(SubscribeCallback listener) { + listeners.add(listener); + } + + public final void removeListener(SubscribeCallback listener) { + listeners.remove(listener); + } + + /** + * announce a PNStatus to listeners. + * + * @param status PNStatus which will be broadcast to listeners. + */ + public void announce(final PNStatus status) { + for (SubscribeCallback subscribeCallback : listeners) { + subscribeCallback.status(this.pubnub, status); + } + } + + public void announce(final PNMessageResult message) { + for (SubscribeCallback subscribeCallback : listeners) { + subscribeCallback.message(this.pubnub, message); + } + } + + public void announce(final PNPresenceEventResult presence) { + for (SubscribeCallback subscribeCallback : listeners) { + subscribeCallback.presence(this.pubnub, presence); + } + } + +} diff --git a/src/main/java/com/pubnub/api/managers/PublishSequenceManager.java b/src/main/java/com/pubnub/api/managers/PublishSequenceManager.java new file mode 100644 index 000000000..e81f36be5 --- /dev/null +++ b/src/main/java/com/pubnub/api/managers/PublishSequenceManager.java @@ -0,0 +1,23 @@ +package com.pubnub.api.managers; + +public class PublishSequenceManager { + + + private int maxSequence; + private int nextSequence; + + public PublishSequenceManager(final int providedMaxSequence) { + this.maxSequence = providedMaxSequence; + } + + public final synchronized int getNextSequence() { + if (maxSequence == nextSequence) { + nextSequence = 1; + } else { + nextSequence += 1; + } + + return nextSequence; + } + +} diff --git a/src/main/java/com/pubnub/api/managers/ReconnectionManager.java b/src/main/java/com/pubnub/api/managers/ReconnectionManager.java new file mode 100644 index 000000000..6c2e87e6b --- /dev/null +++ b/src/main/java/com/pubnub/api/managers/ReconnectionManager.java @@ -0,0 +1,105 @@ +package com.pubnub.api.managers; + +import com.pubnub.api.PubNub; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.callbacks.ReconnectionCallback; +import com.pubnub.api.enums.PNReconnectionPolicy; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.PNTimeResult; +import lombok.extern.slf4j.Slf4j; + +import java.util.Calendar; +import java.util.Timer; +import java.util.TimerTask; + + +@Slf4j +public class ReconnectionManager { + + private static final int INTERVAL = 3; + + private ReconnectionCallback callback; + private PubNub pubnub; + private static final int MINEXPONENTIALBACKOFF = 1; + private static final int MAXEXPONENTIALBACKOFF = 32; + private int timerInterval; + private int connectionErrors = 1; + private static final int MILLISECONDS = 1000; + + /** + * Timer for heartbeat operations. + */ + private Timer timer; + + public ReconnectionManager(PubNub pubnubInstance) { + this.pubnub = pubnubInstance; + } + + public ReconnectionManager setReconnectionListener(ReconnectionCallback reconnectionCallback) { + this.callback = reconnectionCallback; + return this; + } + + public void startPolling() { + if (this.pubnub.getConfiguration().getReconnectionPolicy() == PNReconnectionPolicy.NONE) { + log.warn("reconnection policy is disabled, please handle reconnection manually."); + return; + } + + registerHeartbeatTimer(); + } + + + private void registerHeartbeatTimer() { + // make sure only one timer is running at a time. + stopHeartbeatTimer(); + + timer = new Timer(); + + if (pubnub.getConfiguration().getReconnectionPolicy() == PNReconnectionPolicy.EXPONENTIAL) { + timerInterval = (int) (Math.pow(2, connectionErrors) - 1); + if (timerInterval > MAXEXPONENTIALBACKOFF) { + timerInterval = MINEXPONENTIALBACKOFF; + connectionErrors = 1; + log.debug("timerInterval > MAXEXPONENTIALBACKOFF at: " + Calendar.getInstance().getTime().toString()); + } else if (timerInterval < 1) { + timerInterval = MINEXPONENTIALBACKOFF; + } + log.debug("timerInterval = " + String.valueOf(timerInterval) + " at: " + Calendar.getInstance().getTime().toString()); + } else { + timerInterval = INTERVAL; + } + + timer.schedule(new TimerTask() { + @Override + public void run() { + callTime(); + } + }, timerInterval * MILLISECONDS, timerInterval * MILLISECONDS); + } + + private void stopHeartbeatTimer() { + if (timer != null) { + timer.cancel(); + timer = null; + } + } + + private void callTime() { + pubnub.time().async(new PNCallback() { + @Override + public void onResponse(PNTimeResult result, PNStatus status) { + if (!status.isError()) { + connectionErrors = 1; + stopHeartbeatTimer(); + callback.onReconnection(); + } else if (pubnub.getConfiguration().getReconnectionPolicy() == PNReconnectionPolicy.EXPONENTIAL) { + log.debug("callTime() at: " + Calendar.getInstance().getTime().toString()); + stopHeartbeatTimer(); + connectionErrors++; + registerHeartbeatTimer(); + } + } + }); + } +} diff --git a/src/main/java/com/pubnub/api/managers/RetrofitManager.java b/src/main/java/com/pubnub/api/managers/RetrofitManager.java new file mode 100644 index 000000000..cf8979ac0 --- /dev/null +++ b/src/main/java/com/pubnub/api/managers/RetrofitManager.java @@ -0,0 +1,56 @@ +package com.pubnub.api.managers; + + +import com.pubnub.api.PubNub; +import com.pubnub.api.enums.PNLogVerbosity; +import lombok.Getter; +import okhttp3.OkHttpClient; +import okhttp3.logging.HttpLoggingInterceptor; +import retrofit2.Retrofit; +import retrofit2.converter.jackson.JacksonConverterFactory; + +import java.util.concurrent.TimeUnit; + +public class RetrofitManager { + + private PubNub pubnub; + + @Getter + private Retrofit transactionInstance; + @Getter + private Retrofit subscriptionInstance; + + public RetrofitManager(PubNub pubNubInstance) { + this.pubnub = pubNubInstance; + + this.transactionInstance = createRetrofit( + this.pubnub.getConfiguration().getNonSubscribeRequestTimeout(), + this.pubnub.getConfiguration().getConnectTimeout() + ); + + this.subscriptionInstance = createRetrofit( + this.pubnub.getConfiguration().getSubscribeTimeout(), + this.pubnub.getConfiguration().getConnectTimeout() + ); + + } + + protected final Retrofit createRetrofit(int requestTimeout, int connectTimeOut) { + OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); + httpClient.readTimeout(requestTimeout, TimeUnit.SECONDS); + httpClient.connectTimeout(connectTimeOut, TimeUnit.SECONDS); + + if (pubnub.getConfiguration().getLogVerbosity() == PNLogVerbosity.BODY) { + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); + logging.setLevel(HttpLoggingInterceptor.Level.BODY); + httpClient.addInterceptor(logging); + } + + return new Retrofit.Builder() + .baseUrl(pubnub.getBaseUrl()) + .addConverterFactory(JacksonConverterFactory.create()) + .client(httpClient.build()) + .build(); + } + +} diff --git a/src/main/java/com/pubnub/api/managers/StateManager.java b/src/main/java/com/pubnub/api/managers/StateManager.java new file mode 100644 index 000000000..ac8235242 --- /dev/null +++ b/src/main/java/com/pubnub/api/managers/StateManager.java @@ -0,0 +1,149 @@ +package com.pubnub.api.managers; + + +import com.pubnub.api.builder.dto.StateOperation; +import com.pubnub.api.builder.dto.SubscribeOperation; +import com.pubnub.api.builder.dto.UnsubscribeOperation; +import com.pubnub.api.models.SubscriptionItem; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class StateManager { + + /** + * Contains a list of subscribed channels + */ + private Map channels; + /** + * Contains a list of subscribed presence channels. + */ + private Map presenceChannels; + + /** + * Contains a list of subscribed channel groups. + */ + private Map groups; + + /** + * Contains a list of subscribed presence channel groups. + */ + private Map presenceGroups; + + public StateManager() { + this.channels = new HashMap<>(); + this.presenceChannels = new HashMap<>(); + + this.groups = new HashMap<>(); + this.presenceGroups = new HashMap<>(); + } + + + public final synchronized void adaptSubscribeBuilder(final SubscribeOperation subscribeOperation) { + for (String channel : subscribeOperation.getChannels()) { + SubscriptionItem subscriptionItem = new SubscriptionItem().setName(channel); + channels.put(channel, subscriptionItem); + + if (subscribeOperation.isPresenceEnabled()) { + SubscriptionItem presenceSubscriptionItem = new SubscriptionItem().setName(channel); + presenceChannels.put(channel, presenceSubscriptionItem); + } + + } + + for (String channelGroup : subscribeOperation.getChannelGroups()) { + SubscriptionItem subscriptionItem = new SubscriptionItem().setName(channelGroup); + groups.put(channelGroup, subscriptionItem); + + if (subscribeOperation.isPresenceEnabled()) { + SubscriptionItem presenceSubscriptionItem = new SubscriptionItem().setName(channelGroup); + presenceGroups.put(channelGroup, presenceSubscriptionItem); + } + + } + } + + public final synchronized void adaptStateBuilder(final StateOperation stateOperation) { + for (String channel: stateOperation.getChannels()) { + SubscriptionItem subscribedChannel = channels.get(channel); + + if (subscribedChannel != null) { + subscribedChannel.setState(stateOperation.getState()); + } + } + + for (String channelGroup: stateOperation.getChannelGroups()) { + SubscriptionItem subscribedChannelGroup = groups.get(channelGroup); + + if (subscribedChannelGroup != null) { + subscribedChannelGroup.setState(stateOperation.getState()); + } + } + } + + + public final synchronized void adaptUnsubscribeBuilder(final UnsubscribeOperation unsubscribeOperation) { + + for (String channel: unsubscribeOperation.getChannels()) { + this.channels.remove(channel); + this.presenceChannels.remove(channel); + } + + for (String channelGroup: unsubscribeOperation.getChannelGroups()) { + this.groups.remove(channelGroup); + this.presenceGroups.remove(channelGroup); + } + } + + public final synchronized Map createStatePayload() { + Map stateResponse = new HashMap<>(); + + for (SubscriptionItem channel: channels.values()) { + if (channel.getState() != null) { + stateResponse.put(channel.getName(), channel.getState()); + } + } + + for (SubscriptionItem channelGroup: groups.values()) { + if (channelGroup.getState() != null) { + stateResponse.put(channelGroup.getName(), channelGroup.getState()); + } + } + + return stateResponse; + } + + public synchronized List prepareChannelList(final boolean includePresence) { + return prepareMembershipList(channels, presenceChannels, includePresence); + } + + public synchronized List prepareChannelGroupList(final boolean includePresence) { + return prepareMembershipList(groups, presenceGroups, includePresence); + } + + public synchronized boolean isEmpty() { + return (channels.isEmpty() && presenceChannels.isEmpty() && groups.isEmpty() && presenceGroups.isEmpty()); + } + + private synchronized List prepareMembershipList(final Map dataStorage, + final Map presenceStorage, + final boolean includePresence) { + List response = new ArrayList<>(); + + for (SubscriptionItem channelGroupItem: dataStorage.values()) { + response.add(channelGroupItem.getName()); + } + + if (includePresence) { + for (SubscriptionItem presenceChannelGroupItem: presenceStorage.values()) { + response.add(presenceChannelGroupItem.getName().concat("-pnpres")); + } + } + + + return response; + } + +} diff --git a/src/main/java/com/pubnub/api/managers/SubscriptionManager.java b/src/main/java/com/pubnub/api/managers/SubscriptionManager.java new file mode 100644 index 000000000..4aa5ae1b4 --- /dev/null +++ b/src/main/java/com/pubnub/api/managers/SubscriptionManager.java @@ -0,0 +1,303 @@ +package com.pubnub.api.managers; + +import com.pubnub.api.PubNub; +import com.pubnub.api.builder.dto.StateOperation; +import com.pubnub.api.builder.dto.SubscribeOperation; +import com.pubnub.api.builder.dto.UnsubscribeOperation; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.callbacks.ReconnectionCallback; +import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.endpoints.presence.Heartbeat; +import com.pubnub.api.endpoints.presence.Leave; +import com.pubnub.api.endpoints.pubsub.Subscribe; +import com.pubnub.api.enums.PNHeartbeatNotificationOptions; +import com.pubnub.api.enums.PNStatusCategory; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.server.SubscribeEnvelope; +import com.pubnub.api.models.server.SubscribeMessage; +import com.pubnub.api.workers.SubscribeMessageWorker; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.LinkedBlockingQueue; + +@Slf4j +public class SubscriptionManager { + + private static final int HEARTBEAT_INTERVAL_MULTIPLIER = 1000; + + private PubNub pubnub; + private Subscribe subscribeCall; + private Heartbeat heartbeatCall; + + private LinkedBlockingQueue messageQueue; + + /** + * Store the latest timetoken to subscribe with, null by default to get the latest timetoken. + */ + private Long timetoken; + + /** + * Keep track of Region to support PSV2 specification. + */ + private String region; + + /** + * Timer for heartbeat operations. + */ + private Timer timer; + + private StateManager subscriptionState; + private ListenerManager listenerManager; + private ReconnectionManager reconnectionManager; + private RetrofitManager retrofitManager; + + private Thread consumerThread; + + /** + * lever to indicate if an announcement to the user about the subscription should be made. + * the announcement happens only after the channel mix has been changed. + */ + private boolean subscriptionStatusAnnounced; + + public SubscriptionManager(PubNub pubnubInstance, RetrofitManager retrofitManagerInstance) { + this.pubnub = pubnubInstance; + + this.subscriptionStatusAnnounced = false; + this.messageQueue = new LinkedBlockingQueue<>(); + this.subscriptionState = new StateManager(); + + this.listenerManager = new ListenerManager(this.pubnub); + this.reconnectionManager = new ReconnectionManager(this.pubnub); + this.retrofitManager = retrofitManagerInstance; + + this.timetoken = 0L; + + this.reconnectionManager.setReconnectionListener(new ReconnectionCallback() { + @Override + public void onReconnection() { + reconnect(); + PNStatus pnStatus = PNStatus.builder() + .error(false) + .category(PNStatusCategory.PNReconnectedCategory) + .build(); + + subscriptionStatusAnnounced = true; + listenerManager.announce(pnStatus); + } + }); + + consumerThread = new Thread(new SubscribeMessageWorker(this.pubnub, listenerManager, messageQueue)); + consumerThread.start(); + } + + public final void addListener(SubscribeCallback listener) { + listenerManager.addListener(listener); + } + + public final void removeListener(SubscribeCallback listener) { + listenerManager.removeListener(listener); + } + + + public final synchronized void reconnect() { + this.startSubscribeLoop(); + this.registerHeartbeatTimer(); + } + + public final synchronized void disconnect() { + stopHeartbeatTimer(); + stopSubscribeLoop(); + } + + + public synchronized void stop() { + disconnect(); + consumerThread.interrupt(); + } + + public final synchronized void adaptStateBuilder(final StateOperation stateOperation) { + this.subscriptionState.adaptStateBuilder(stateOperation); + reconnect(); + } + + public final synchronized void adaptSubscribeBuilder(final SubscribeOperation subscribeOperation) { + this.subscriptionState.adaptSubscribeBuilder(subscribeOperation); + // the channel mix changed, on the successful subscribe, there is going to be announcement. + this.subscriptionStatusAnnounced = false; + + if (subscribeOperation.getTimetoken() != null) { + this.timetoken = subscribeOperation.getTimetoken(); + } + + reconnect(); + } + + public final synchronized void adaptUnsubscribeBuilder(final UnsubscribeOperation unsubscribeOperation) { + this.subscriptionState.adaptUnsubscribeBuilder(unsubscribeOperation); + + new Leave(pubnub, this.retrofitManager.getTransactionInstance()) + .channels(unsubscribeOperation.getChannels()).channelGroups(unsubscribeOperation.getChannelGroups()) + .async(new PNCallback() { + @Override + public void onResponse(final Boolean result, final PNStatus status) { + listenerManager.announce(status); + } + }); + + // if we unsubscribed from all the channels, reset the timetoken back to zero and remove the region. + if (this.subscriptionState.isEmpty()) { + region = null; + timetoken = 0L; + } + + reconnect(); + } + + private void registerHeartbeatTimer() { + // make sure only one timer is running at a time. + stopHeartbeatTimer(); + + timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + performHeartbeatLoop(); + } + }, 0, pubnub.getConfiguration().getHeartbeatInterval() * HEARTBEAT_INTERVAL_MULTIPLIER); + + } + + private void stopHeartbeatTimer() { + if (timer != null) { + timer.cancel(); + timer = null; + } + } + + private void startSubscribeLoop() { + // this function can be called from different points, make sure any old loop is closed + stopSubscribeLoop(); + + List combinedChannels = this.subscriptionState.prepareChannelList(true); + List combinedChannelGroups = this.subscriptionState.prepareChannelGroupList(true); + + // do not start the subscribe loop if we have no channels to subscribe to. + if (combinedChannels.isEmpty() && combinedChannelGroups.isEmpty()) { + return; + } + + subscribeCall = new Subscribe(pubnub, this.retrofitManager.getSubscriptionInstance()) + .channels(combinedChannels).channelGroups(combinedChannelGroups) + .timetoken(timetoken).region(region) + .filterExpression(pubnub.getConfiguration().getFilterExpression()); + + subscribeCall.async(new PNCallback() { + @Override + public void onResponse(final SubscribeEnvelope result, final PNStatus status) { + if (status.isError()) { + + if (status.getCategory() == PNStatusCategory.PNTimeoutCategory) { + startSubscribeLoop(); + } else { + disconnect(); + listenerManager.announce(status); + + // stop all announcements and ask the reconnection manager to start polling for connection restoration.. + reconnectionManager.startPolling(); + } + + return; + } + + if (!subscriptionStatusAnnounced) { + PNStatus pnStatus = PNStatus.builder() + .error(false) + .category(PNStatusCategory.PNConnectedCategory) + .statusCode(status.getStatusCode()) + .authKey(status.getAuthKey()) + .operation(status.getOperation()) + .clientRequest(status.getClientRequest()) + .origin(status.getOrigin()) + .tlsEnabled(status.isTlsEnabled()) + .build(); + + subscriptionStatusAnnounced = true; + listenerManager.announce(pnStatus); + } + + if (result.getMessages().size() != 0) { + messageQueue.addAll(result.getMessages()); + } + + timetoken = result.getMetadata().getTimetoken(); + region = result.getMetadata().getRegion(); + startSubscribeLoop(); + } + }); + + } + + private void stopSubscribeLoop() { + if (subscribeCall != null) { + subscribeCall.silentCancel(); + } + } + + private void performHeartbeatLoop() { + if (heartbeatCall != null) { + heartbeatCall.silentCancel(); + } + + List presenceChannels = this.subscriptionState.prepareChannelList(false); + List presenceChannelGroups = this.subscriptionState.prepareChannelGroupList(false); + Map stateStorage = this.subscriptionState.createStatePayload(); + + // do not start the loop if we do not have any presence channels or channel groups enabled. + if (presenceChannels.isEmpty() && presenceChannelGroups.isEmpty()) { + return; + } + + heartbeatCall = new Heartbeat(pubnub, this.retrofitManager.getTransactionInstance()) + .channels(presenceChannels).channelGroups(presenceChannelGroups).state(stateStorage); + + heartbeatCall.async(new PNCallback() { + @Override + public void onResponse(Boolean result, PNStatus status) { + PNHeartbeatNotificationOptions heartbeatVerbosity = pubnub + .getConfiguration().getHeartbeatNotificationOptions(); + + if (status.isError()) { + if (heartbeatVerbosity == PNHeartbeatNotificationOptions.ALL + || heartbeatVerbosity == PNHeartbeatNotificationOptions.FAILURES) { + listenerManager.announce(status); + } + + } else { + if (heartbeatVerbosity == PNHeartbeatNotificationOptions.ALL) { + listenerManager.announce(status); + } + } + } + }); + + } + + public final synchronized List getSubscribedChannels() { + return subscriptionState.prepareChannelList(false); + } + + public final synchronized List getSubscribedChannelGroups() { + return subscriptionState.prepareChannelGroupList(false); + } + + public final synchronized void unsubscribeAll() { + adaptUnsubscribeBuilder(UnsubscribeOperation.builder() + .channelGroups(subscriptionState.prepareChannelGroupList(false)) + .channels(subscriptionState.prepareChannelList(false)) + .build()); + } +} diff --git a/src/main/java/com/pubnub/api/models/SubscriptionItem.java b/src/main/java/com/pubnub/api/models/SubscriptionItem.java new file mode 100644 index 000000000..2e4b0f25e --- /dev/null +++ b/src/main/java/com/pubnub/api/models/SubscriptionItem.java @@ -0,0 +1,15 @@ +package com.pubnub.api.models; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +@Setter() +@Getter +@Accessors(chain = true) +public class SubscriptionItem { + + private String name; + private Object state; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/PNErrorData.java b/src/main/java/com/pubnub/api/models/consumer/PNErrorData.java new file mode 100644 index 000000000..ceab6918c --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/PNErrorData.java @@ -0,0 +1,13 @@ +package com.pubnub.api.models.consumer; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class PNErrorData { + + private String information; + private Exception throwable; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/PNPublishResult.java b/src/main/java/com/pubnub/api/models/consumer/PNPublishResult.java new file mode 100644 index 000000000..e99fca305 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/PNPublishResult.java @@ -0,0 +1,10 @@ +package com.pubnub.api.models.consumer; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class PNPublishResult { + private Long timetoken; +} diff --git a/src/main/java/com/pubnub/api/models/consumer/PNStatus.java b/src/main/java/com/pubnub/api/models/consumer/PNStatus.java new file mode 100644 index 000000000..bd32a32d3 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/PNStatus.java @@ -0,0 +1,50 @@ +package com.pubnub.api.models.consumer; + +import com.pubnub.api.endpoints.Endpoint; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.enums.PNStatusCategory; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Builder +@Getter +public class PNStatus { + + private PNStatusCategory category; + private PNErrorData errorData; + private boolean error; + + // boolean automaticallyRetry; + + private int statusCode; + private PNOperationType operation; + + private boolean tlsEnabled; + + private String uuid; + private String authKey; + private String origin; + private Object clientRequest; + + // send back channel, channel groups that were affected by this operation + private List affectedChannels; + private List affectedChannelGroups; + + @Getter(AccessLevel.NONE) + private Endpoint executedEndpoint; + + + public void retry() { + executedEndpoint.retry(); + } + + /* + public void cancelAutomaticRetry() { + // TODO + } + */ + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/PNTimeResult.java b/src/main/java/com/pubnub/api/models/consumer/PNTimeResult.java new file mode 100644 index 000000000..2cfb3e26d --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/PNTimeResult.java @@ -0,0 +1,10 @@ +package com.pubnub.api.models.consumer; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class PNTimeResult { + private Long timetoken; +} diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerAuditResult.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerAuditResult.java new file mode 100644 index 000000000..cc8120b61 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerAuditResult.java @@ -0,0 +1,20 @@ +package com.pubnub.api.models.consumer.access_manager; + +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Builder +@Getter +public class PNAccessManagerAuditResult { + + private String level; + private String subscribeKey; + + private String channel; + + private String channelGroup; + + private Map authKeys; +} diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerGrantResult.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerGrantResult.java new file mode 100644 index 000000000..b4e3c3910 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerGrantResult.java @@ -0,0 +1,20 @@ +package com.pubnub.api.models.consumer.access_manager; + +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Builder +@Getter +public class PNAccessManagerGrantResult { + + private String level; + private int ttl; + private String subscribeKey; + + private Map> channels; + + private Map> channelGroups; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerKeyData.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerKeyData.java new file mode 100644 index 000000000..5c9dc6e10 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerKeyData.java @@ -0,0 +1,20 @@ +package com.pubnub.api.models.consumer.access_manager; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +public class PNAccessManagerKeyData { + + @JsonProperty("r") + private boolean readEnabled; + + @JsonProperty("w") + private boolean writeEnabled; + + @JsonProperty("m") + private boolean manageEnabled; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerKeysData.java b/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerKeysData.java new file mode 100644 index 000000000..3217d74d3 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/access_manager/PNAccessManagerKeysData.java @@ -0,0 +1,16 @@ +package com.pubnub.api.models.consumer.access_manager; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +import java.util.Map; + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +public class PNAccessManagerKeysData { + + @JsonProperty("auths") + private Map authKeys; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsAddChannelResult.java b/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsAddChannelResult.java new file mode 100644 index 000000000..3f7d1a8b7 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsAddChannelResult.java @@ -0,0 +1,9 @@ +package com.pubnub.api.models.consumer.channel_group; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class PNChannelGroupsAddChannelResult { +} diff --git a/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsAllChannelsResult.java b/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsAllChannelsResult.java new file mode 100644 index 000000000..0c659ae35 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsAllChannelsResult.java @@ -0,0 +1,12 @@ +package com.pubnub.api.models.consumer.channel_group; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class PNChannelGroupsAllChannelsResult { + private List channels; +} diff --git a/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsDeleteGroupResult.java b/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsDeleteGroupResult.java new file mode 100644 index 000000000..5b3ad22da --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsDeleteGroupResult.java @@ -0,0 +1,9 @@ +package com.pubnub.api.models.consumer.channel_group; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class PNChannelGroupsDeleteGroupResult { +} diff --git a/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsListAllResult.java b/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsListAllResult.java new file mode 100644 index 000000000..56db38529 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsListAllResult.java @@ -0,0 +1,12 @@ +package com.pubnub.api.models.consumer.channel_group; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Builder +@Getter +public class PNChannelGroupsListAllResult { + private List groups; +} diff --git a/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsRemoveChannelResult.java b/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsRemoveChannelResult.java new file mode 100644 index 000000000..522d731ca --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/channel_group/PNChannelGroupsRemoveChannelResult.java @@ -0,0 +1,9 @@ +package com.pubnub.api.models.consumer.channel_group; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class PNChannelGroupsRemoveChannelResult { +} diff --git a/src/main/java/com/pubnub/api/models/consumer/history/PNHistoryItemResult.java b/src/main/java/com/pubnub/api/models/consumer/history/PNHistoryItemResult.java new file mode 100644 index 000000000..8e8f4db95 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/history/PNHistoryItemResult.java @@ -0,0 +1,14 @@ +package com.pubnub.api.models.consumer.history; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class PNHistoryItemResult { + + @Getter private Long timetoken; + @Getter private JsonNode entry; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/history/PNHistoryResult.java b/src/main/java/com/pubnub/api/models/consumer/history/PNHistoryResult.java new file mode 100644 index 000000000..c207ea620 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/history/PNHistoryResult.java @@ -0,0 +1,16 @@ +package com.pubnub.api.models.consumer.history; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class PNHistoryResult { + + private List messages; + private Long startTimetoken; + private Long endTimetoken; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/presence/PNGetStateResult.java b/src/main/java/com/pubnub/api/models/consumer/presence/PNGetStateResult.java new file mode 100644 index 000000000..92430bef5 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/presence/PNGetStateResult.java @@ -0,0 +1,14 @@ +package com.pubnub.api.models.consumer.presence; + +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Builder +@Getter +public class PNGetStateResult { + + private Map stateByUUID; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/presence/PNHereNowChannelData.java b/src/main/java/com/pubnub/api/models/consumer/presence/PNHereNowChannelData.java new file mode 100644 index 000000000..8065d91cb --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/presence/PNHereNowChannelData.java @@ -0,0 +1,16 @@ +package com.pubnub.api.models.consumer.presence; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class PNHereNowChannelData { + + private String channelName; + private int occupancy; + private List occupants; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/presence/PNHereNowOccupantData.java b/src/main/java/com/pubnub/api/models/consumer/presence/PNHereNowOccupantData.java new file mode 100644 index 000000000..23375775e --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/presence/PNHereNowOccupantData.java @@ -0,0 +1,11 @@ +package com.pubnub.api.models.consumer.presence; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class PNHereNowOccupantData { + private String uuid; + private Object state; +} diff --git a/src/main/java/com/pubnub/api/models/consumer/presence/PNHereNowResult.java b/src/main/java/com/pubnub/api/models/consumer/presence/PNHereNowResult.java new file mode 100644 index 000000000..48668952e --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/presence/PNHereNowResult.java @@ -0,0 +1,15 @@ +package com.pubnub.api.models.consumer.presence; + +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Getter +@Builder +public class PNHereNowResult { + private int totalChannels; + private int totalOccupancy; + private Map channels; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/presence/PNSetStateResult.java b/src/main/java/com/pubnub/api/models/consumer/presence/PNSetStateResult.java new file mode 100644 index 000000000..04aa6aadf --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/presence/PNSetStateResult.java @@ -0,0 +1,14 @@ +package com.pubnub.api.models.consumer.presence; + +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; + +@Builder +@Getter +public class PNSetStateResult { + + private Map state; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/presence/PNWhereNowResult.java b/src/main/java/com/pubnub/api/models/consumer/presence/PNWhereNowResult.java new file mode 100644 index 000000000..8fb746e5d --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/presence/PNWhereNowResult.java @@ -0,0 +1,12 @@ +package com.pubnub.api.models.consumer.presence; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class PNWhereNowResult { + private List channels; +} diff --git a/src/main/java/com/pubnub/api/models/consumer/pubsub/PNMessageResult.java b/src/main/java/com/pubnub/api/models/consumer/pubsub/PNMessageResult.java new file mode 100644 index 000000000..99ab46218 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/pubsub/PNMessageResult.java @@ -0,0 +1,18 @@ +package com.pubnub.api.models.consumer.pubsub; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class PNMessageResult { + + private JsonNode message; + + private String subscribedChannel; + private String actualChannel; + private Long timetoken; + private Object userMetadata; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/pubsub/PNPresenceEventResult.java b/src/main/java/com/pubnub/api/models/consumer/pubsub/PNPresenceEventResult.java new file mode 100644 index 000000000..82eae4283 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/pubsub/PNPresenceEventResult.java @@ -0,0 +1,23 @@ +package com.pubnub.api.models.consumer.pubsub; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class PNPresenceEventResult { + + private String event; + + private String uuid; + private Long timestamp; + private Integer occupancy; + private JsonNode state; + + private String subscribedChannel; + private String actualChannel; + private Long timetoken; + private Object userMetadata; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/push/PNPushAddChannelResult.java b/src/main/java/com/pubnub/api/models/consumer/push/PNPushAddChannelResult.java new file mode 100644 index 000000000..9c99af19e --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/push/PNPushAddChannelResult.java @@ -0,0 +1,9 @@ +package com.pubnub.api.models.consumer.push; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class PNPushAddChannelResult { +} diff --git a/src/main/java/com/pubnub/api/models/consumer/push/PNPushListProvisionsResult.java b/src/main/java/com/pubnub/api/models/consumer/push/PNPushListProvisionsResult.java new file mode 100644 index 000000000..453fc798e --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/push/PNPushListProvisionsResult.java @@ -0,0 +1,14 @@ +package com.pubnub.api.models.consumer.push; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Builder +@Getter +public class PNPushListProvisionsResult { + + private List channels; + +} diff --git a/src/main/java/com/pubnub/api/models/consumer/push/PNPushRemoveAllChannelsResult.java b/src/main/java/com/pubnub/api/models/consumer/push/PNPushRemoveAllChannelsResult.java new file mode 100644 index 000000000..db3e43851 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/push/PNPushRemoveAllChannelsResult.java @@ -0,0 +1,9 @@ +package com.pubnub.api.models.consumer.push; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class PNPushRemoveAllChannelsResult { +} diff --git a/src/main/java/com/pubnub/api/models/consumer/push/PNPushRemoveChannelResult.java b/src/main/java/com/pubnub/api/models/consumer/push/PNPushRemoveChannelResult.java new file mode 100644 index 000000000..6e2813b4a --- /dev/null +++ b/src/main/java/com/pubnub/api/models/consumer/push/PNPushRemoveChannelResult.java @@ -0,0 +1,9 @@ +package com.pubnub.api.models.consumer.push; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class PNPushRemoveChannelResult { +} diff --git a/src/main/java/com/pubnub/api/models/server/Envelope.java b/src/main/java/com/pubnub/api/models/server/Envelope.java new file mode 100644 index 000000000..74a246225 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/server/Envelope.java @@ -0,0 +1,19 @@ +package com.pubnub.api.models.server; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@JsonIgnoreProperties(ignoreUnknown = true) +public class Envelope { + private int status; + private String message; + private String service; + private T payload; + private int occupancy; + private Object uuids; + private String action; + private boolean error; +} diff --git a/src/main/java/com/pubnub/api/models/server/PresenceEnvelope.java b/src/main/java/com/pubnub/api/models/server/PresenceEnvelope.java new file mode 100644 index 000000000..bf90b240a --- /dev/null +++ b/src/main/java/com/pubnub/api/models/server/PresenceEnvelope.java @@ -0,0 +1,17 @@ +package com.pubnub.api.models.server; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Getter; + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +public class PresenceEnvelope { + + private String action; + private String uuid; + private Integer occupancy; + private Long timestamp; + private JsonNode data; + +} diff --git a/src/main/java/com/pubnub/api/models/server/PresenceMessage.java b/src/main/java/com/pubnub/api/models/server/PresenceMessage.java new file mode 100644 index 000000000..81514e760 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/server/PresenceMessage.java @@ -0,0 +1,10 @@ +package com.pubnub.api.core.models.server; + +import lombok.Getter; + +@Getter +public class PresenceMessage { + + private long timestamp; + +} diff --git a/src/main/java/com/pubnub/api/models/server/PublishMetaData.java b/src/main/java/com/pubnub/api/models/server/PublishMetaData.java new file mode 100644 index 000000000..b9b0725ff --- /dev/null +++ b/src/main/java/com/pubnub/api/models/server/PublishMetaData.java @@ -0,0 +1,17 @@ +package com.pubnub.api.models.server; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +public class PublishMetaData { + + @JsonProperty("t") + private Long publishTimetoken; + + @JsonProperty("r") + private Integer region; + +} diff --git a/src/main/java/com/pubnub/api/models/server/SubscribeEnvelope.java b/src/main/java/com/pubnub/api/models/server/SubscribeEnvelope.java new file mode 100644 index 000000000..256df4bda --- /dev/null +++ b/src/main/java/com/pubnub/api/models/server/SubscribeEnvelope.java @@ -0,0 +1,19 @@ +package com.pubnub.api.models.server; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +public class SubscribeEnvelope { + + @JsonProperty("m") + private List messages; + + @JsonProperty("t") + private SubscribeMetadata metadata; + +} diff --git a/src/main/java/com/pubnub/api/models/server/SubscribeMessage.java b/src/main/java/com/pubnub/api/models/server/SubscribeMessage.java new file mode 100644 index 000000000..437f032f1 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/server/SubscribeMessage.java @@ -0,0 +1,54 @@ +package com.pubnub.api.models.server; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Getter; + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +public class SubscribeMessage { + + @JsonProperty("a") + private String shard; + + @JsonProperty("b") + private String subscriptionMatch; // will contain channel group; or channel if user subscribed to channel + + @JsonProperty("c") + private String channel; + + @JsonProperty("d") + private JsonNode payload; + + // TODO: figure me out + //@JsonProperty("ear") + //private String payload; + + @JsonProperty("f") + private String flags; + + @JsonProperty("i") + private String issuingClientId; + + @JsonProperty("k") + private String subscribeKey; + + //@JsonProperty("s") + //private String sequenceNumber; + + @JsonProperty("o") + private String originationTimetoken; + + @JsonProperty("p") + private PublishMetaData publishMetaData; + + //@JsonProperty("r") + //private Object replicationMap; + + //@JsonProperty("u") + //private String userMetadata; + + //@JsonProperty("w") + //private String waypointList; +} diff --git a/src/main/java/com/pubnub/api/models/server/SubscribeMetadata.java b/src/main/java/com/pubnub/api/models/server/SubscribeMetadata.java new file mode 100644 index 000000000..e094369e3 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/server/SubscribeMetadata.java @@ -0,0 +1,17 @@ +package com.pubnub.api.models.server; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class SubscribeMetadata { + + @JsonProperty("t") + private Long timetoken; + + @JsonProperty("r") + private String region; + +} diff --git a/src/main/java/com/pubnub/api/models/server/access_manager/AccessManagerAuditPayload.java b/src/main/java/com/pubnub/api/models/server/access_manager/AccessManagerAuditPayload.java new file mode 100644 index 000000000..0232d0b89 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/server/access_manager/AccessManagerAuditPayload.java @@ -0,0 +1,30 @@ +package com.pubnub.api.models.server.access_manager; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeyData; +import lombok.Getter; + +import java.util.Map; + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +public class AccessManagerAuditPayload { + + @JsonProperty("level") + private String level; + + @JsonProperty("subscribe_key") + private String subscribeKey; + + @JsonProperty("channel") + private String channel; + + @JsonProperty("channel-group") + private String channelGroup; + + @JsonProperty("auths") + private Map authKeys; + + +} diff --git a/src/main/java/com/pubnub/api/models/server/access_manager/AccessManagerGrantPayload.java b/src/main/java/com/pubnub/api/models/server/access_manager/AccessManagerGrantPayload.java new file mode 100644 index 000000000..d9c9ecd1c --- /dev/null +++ b/src/main/java/com/pubnub/api/models/server/access_manager/AccessManagerGrantPayload.java @@ -0,0 +1,36 @@ +package com.pubnub.api.models.server.access_manager; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeyData; +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeysData; +import lombok.Getter; + +import java.util.Map; + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +public class AccessManagerGrantPayload { + + @JsonProperty("level") + private String level; + + private int ttl; + + @JsonProperty("subscribe_key") + private String subscribeKey; + + @JsonProperty("channels") + private Map channels; + + @JsonProperty("channel-groups") + private JsonNode channelGroups; + + @JsonProperty("auths") + private Map authKeys; + + @JsonProperty("channel") + private String channel; + +} diff --git a/src/main/java/com/pubnub/api/models/server/presence/WhereNowPayload.java b/src/main/java/com/pubnub/api/models/server/presence/WhereNowPayload.java new file mode 100644 index 000000000..64490aa01 --- /dev/null +++ b/src/main/java/com/pubnub/api/models/server/presence/WhereNowPayload.java @@ -0,0 +1,10 @@ +package com.pubnub.api.models.server.presence; + +import lombok.Data; + +import java.util.List; + +@Data +public class WhereNowPayload { + private List channels; +} diff --git a/src/main/java/com/pubnub/api/vendor/Base64.java b/src/main/java/com/pubnub/api/vendor/Base64.java new file mode 100644 index 000000000..551e7bc5b --- /dev/null +++ b/src/main/java/com/pubnub/api/vendor/Base64.java @@ -0,0 +1,743 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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. + */ + +package com.pubnub.api.vendor; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +/** + * Utilities for encoding and decoding the Base64 representation of + * binary data. See RFCs 2045 and 3548. + */ +public class Base64 { + /** + * Default values for encoder/decoder flags. + */ + public static final int DEFAULT = 0; + + /** + * Encoder flag bit to omit the padding '=' characters at the end + * of the output (if any). + */ + public static final int NO_PADDING = 1; + + /** + * Encoder flag bit to omit all line terminators (i.e., the output + * will be on one long line). + */ + public static final int NO_WRAP = 2; + + /** + * Encoder flag bit to indicate lines should be terminated with a + * CRLF pair instead of just an LF. Has no effect if {@code + * NO_WRAP} is specified as well. + */ + public static final int CRLF = 4; + + /** + * Encoder/decoder flag bit to indicate using the "URL and + * filename safe" variant of Base64 (see RFC 3548 section 4) where + * {@code -} and {@code _} are used in place of {@code +} and + * {@code /}. + */ + public static final int URL_SAFE = 8; + + /** + * Flag to pass to indicate that it + * should not close the output stream it is wrapping when it + * itself is closed. + */ + public static final int NO_CLOSE = 16; + + // -------------------------------------------------------- + // shared code + // -------------------------------------------------------- + + /* package */ static abstract class Coder { + public byte[] output; + public int op; + + /** + * Encode/decode another block of input data. this.output is + * provided by the caller, and must be big enough to hold all + * the coded data. On exit, this.opwill be set to the length + * of the coded data. + * + * @param finish true if this is the final call to process for + * this object. Will finalize the coder state and + * include any final bytes in the output. + * + * @return true if the input so far is good; false if some + * error has been detected in the input stream.. + */ + public abstract boolean process(byte[] input, int offset, int len, boolean finish); + + /** + * @return the maximum number of bytes a call to process() + * could produce for the given number of input bytes. This may + * be an overestimate. + */ + public abstract int maxOutputSize(int len); + } + + // -------------------------------------------------------- + // decoding + // -------------------------------------------------------- + + /** + * Decode the Base64-encoded data in input and return the data in + * a new byte array. + * + *

The padding '=' characters at the end are considered optional, but + * if any are present, there must be the correct number of them. + * + * @param str the input String to decode, which is converted to + * bytes using the default charset + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. + * + * @throws IllegalArgumentException if the input contains + * incorrect padding + */ + public static byte[] decode(String str, int flags) { + return decode(str.getBytes(Charset.forName("UTF-8")), flags); + } + + /** + * Decode the Base64-encoded data in input and return the data in + * a new byte array. + * + *

The padding '=' characters at the end are considered optional, but + * if any are present, there must be the correct number of them. + * + * @param input the input array to decode + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. + * + * @throws IllegalArgumentException if the input contains + * incorrect padding + */ + public static byte[] decode(byte[] input, int flags) { + return decode(input, 0, input.length, flags); + } + + /** + * Decode the Base64-encoded data in input and return the data in + * a new byte array. + * + *

The padding '=' characters at the end are considered optional, but + * if any are present, there must be the correct number of them. + * + * @param input the data to decode + * @param offset the position within the input array at which to start + * @param len the number of bytes of input to decode + * @param flags controls certain features of the decoded output. + * Pass {@code DEFAULT} to decode standard Base64. + * + * @throws IllegalArgumentException if the input contains + * incorrect padding + */ + public static byte[] decode(byte[] input, int offset, int len, int flags) { + // Allocate space for the most data the input could represent. + // (It could contain less if it contains whitespace, etc.) + Decoder decoder = new Decoder(flags, new byte[len*3/4]); + + if (!decoder.process(input, offset, len, true)) { + throw new IllegalArgumentException("bad base-64"); + } + + // Maybe we got lucky and allocated exactly enough output space. + if (decoder.op == decoder.output.length) { + return decoder.output; + } + + // Need to shorten the array, so allocate a new one of the + // right size and copy. + byte[] temp = new byte[decoder.op]; + System.arraycopy(decoder.output, 0, temp, 0, decoder.op); + return temp; + } + + /* package */ static class Decoder extends Coder { + /** + * Lookup table for turning bytes into their position in the + * Base64 alphabet. + */ + private static final int DECODE[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + /** + * Decode lookup table for the "web safe" variant (RFC 3548 + * sec. 4) where - and _ replace + and /. + */ + private static final int DECODE_WEBSAFE[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + /** Non-data values in the DECODE arrays. */ + private static final int SKIP = -1; + private static final int EQUALS = -2; + + /** + * States 0-3 are reading through the next input tuple. + * State 4 is having read one '=' and expecting exactly + * one more. + * State 5 is expecting no more data or padding characters + * in the input. + * State 6 is the error state; an error has been detected + * in the input and no future input can "fix" it. + */ + private int state; // state number (0 to 6) + private int value; + + final private int[] alphabet; + + public Decoder(int flags, byte[] output) { + this.output = output; + + alphabet = ((flags & URL_SAFE) == 0) ? DECODE : DECODE_WEBSAFE; + state = 0; + value = 0; + } + + /** + * @return an overestimate for the number of bytes {@code + * len} bytes could decode to. + */ + public int maxOutputSize(int len) { + return len * 3/4 + 10; + } + + /** + * Decode another block of input data. + * + * @return true if the state machine is still healthy. false if + * bad base-64 data has been detected in the input stream. + */ + public boolean process(byte[] input, int offset, int len, boolean finish) { + if (this.state == 6) return false; + + int p = offset; + len += offset; + + // Using local variables makes the decoder about 12% + // faster than if we manipulate the member variables in + // the loop. (Even alphabet makes a measurable + // difference, which is somewhat surprising to me since + // the member variable is final.) + int state = this.state; + int value = this.value; + int op = 0; + final byte[] output = this.output; + final int[] alphabet = this.alphabet; + + while (p < len) { + // Try the fast path: we're starting a new tuple and the + // next four bytes of the input stream are all data + // bytes. This corresponds to going through states + // 0-1-2-3-0. We expect to use this method for most of + // the data. + // + // If any of the next four bytes of input are non-data + // (whitespace, etc.), value will end up negative. (All + // the non-data values in decode are small negative + // numbers, so shifting any of them up and or'ing them + // together will result in a value with its top bit set.) + // + // You can remove this whole block and the output should + // be the same, just slower. + if (state == 0) { + while (p+4 <= len && + (value = ((alphabet[input[p] & 0xff] << 18) | + (alphabet[input[p+1] & 0xff] << 12) | + (alphabet[input[p+2] & 0xff] << 6) | + (alphabet[input[p+3] & 0xff]))) >= 0) { + output[op+2] = (byte) value; + output[op+1] = (byte) (value >> 8); + output[op] = (byte) (value >> 16); + op += 3; + p += 4; + } + if (p >= len) break; + } + + // The fast path isn't available -- either we've read a + // partial tuple, or the next four input bytes aren't all + // data, or whatever. Fall back to the slower state + // machine implementation. + + int d = alphabet[input[p++] & 0xff]; + + switch (state) { + case 0: + if (d >= 0) { + value = d; + ++state; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 1: + if (d >= 0) { + value = (value << 6) | d; + ++state; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 2: + if (d >= 0) { + value = (value << 6) | d; + ++state; + } else if (d == EQUALS) { + // Emit the last (partial) output tuple; + // expect exactly one more padding character. + output[op++] = (byte) (value >> 4); + state = 4; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 3: + if (d >= 0) { + // Emit the output triple and return to state 0. + value = (value << 6) | d; + output[op+2] = (byte) value; + output[op+1] = (byte) (value >> 8); + output[op] = (byte) (value >> 16); + op += 3; + state = 0; + } else if (d == EQUALS) { + // Emit the last (partial) output tuple; + // expect no further data or padding characters. + output[op+1] = (byte) (value >> 2); + output[op] = (byte) (value >> 10); + op += 2; + state = 5; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 4: + if (d == EQUALS) { + ++state; + } else if (d != SKIP) { + this.state = 6; + return false; + } + break; + + case 5: + if (d != SKIP) { + this.state = 6; + return false; + } + break; + } + } + + if (!finish) { + // We're out of input, but a future call could provide + // more. + this.state = state; + this.value = value; + this.op = op; + return true; + } + + // Done reading input. Now figure out where we are left in + // the state machine and finish up. + + switch (state) { + case 0: + // Output length is a multiple of three. Fine. + break; + case 1: + // Read one extra input byte, which isn't enough to + // make another output byte. Illegal. + this.state = 6; + return false; + case 2: + // Read two extra input bytes, enough to emit 1 more + // output byte. Fine. + output[op++] = (byte) (value >> 4); + break; + case 3: + // Read three extra input bytes, enough to emit 2 more + // output bytes. Fine. + output[op++] = (byte) (value >> 10); + output[op++] = (byte) (value >> 2); + break; + case 4: + // Read one padding '=' when we expected 2. Illegal. + this.state = 6; + return false; + case 5: + // Read all the padding '='s we expected and no more. + // Fine. + break; + } + + this.state = state; + this.op = op; + return true; + } + } + + // -------------------------------------------------------- + // encoding + // -------------------------------------------------------- + + /** + * Base64-encode the given data and return a newly allocated + * String with the result. + * + * @param input the data to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static String encodeToString(byte[] input, int flags) { + try { + return new String(encode(input, flags), "US-ASCII"); + } catch (UnsupportedEncodingException e) { + // US-ASCII is guaranteed to be available. + throw new AssertionError(e); + } + } + + /** + * Base64-encode the given data and return a newly allocated + * String with the result. + * + * @param input the data to encode + * @param offset the position within the input array at which to + * start + * @param len the number of bytes of input to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static String encodeToString(byte[] input, int offset, int len, int flags) { + try { + return new String(encode(input, offset, len, flags), "US-ASCII"); + } catch (UnsupportedEncodingException e) { + // US-ASCII is guaranteed to be available. + throw new AssertionError(e); + } + } + + /** + * Base64-encode the given data and return a newly allocated + * byte[] with the result. + * + * @param input the data to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static byte[] encode(byte[] input, int flags) { + return encode(input, 0, input.length, flags); + } + + /** + * Base64-encode the given data and return a newly allocated + * byte[] with the result. + * + * @param input the data to encode + * @param offset the position within the input array at which to + * start + * @param len the number of bytes of input to encode + * @param flags controls certain features of the encoded output. + * Passing {@code DEFAULT} results in output that + * adheres to RFC 2045. + */ + public static byte[] encode(byte[] input, int offset, int len, int flags) { + Encoder encoder = new Encoder(flags, null); + + // Compute the exact length of the array we will produce. + int output_len = len / 3 * 4; + + // Account for the tail of the data and the padding bytes, if any. + if (encoder.do_padding) { + if (len % 3 > 0) { + output_len += 4; + } + } else { + switch (len % 3) { + case 0: break; + case 1: output_len += 2; break; + case 2: output_len += 3; break; + default: break; + } + } + + // Account for the newlines, if any. + if (encoder.do_newline && len > 0) { + output_len += (((len-1) / (3 * Encoder.LINE_GROUPS)) + 1) * + (encoder.do_cr ? 2 : 1); + } + + encoder.output = new byte[output_len]; + encoder.process(input, offset, len, true); + + assert encoder.op == output_len; + + return encoder.output; + } + + /* package */ static class Encoder extends Coder { + /** + * Emit a new line every this many output tuples. Corresponds to + * a 76-character line length (the maximum allowable according to + * RFC 2045). + */ + public static final int LINE_GROUPS = 19; + + /** + * Lookup table for turning Base64 alphabet positions (6 bits) + * into output bytes. + */ + private static final byte ENCODE[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', + }; + + /** + * Lookup table for turning Base64 alphabet positions (6 bits) + * into output bytes. + */ + private static final byte ENCODE_WEBSAFE[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', + }; + + final private byte[] tail; + /* package */ int tailLen; + private int count; + + final public boolean do_padding; + final public boolean do_newline; + final public boolean do_cr; + final private byte[] alphabet; + + public Encoder(int flags, byte[] output) { + this.output = output; + + do_padding = (flags & NO_PADDING) == 0; + do_newline = (flags & NO_WRAP) == 0; + do_cr = (flags & CRLF) != 0; + alphabet = ((flags & URL_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE; + + tail = new byte[2]; + tailLen = 0; + + count = do_newline ? LINE_GROUPS : -1; + } + + /** + * @return an overestimate for the number of bytes {@code + * len} bytes could encode to. + */ + public int maxOutputSize(int len) { + return len * 8/5 + 10; + } + + public boolean process(byte[] input, int offset, int len, boolean finish) { + // Using local variables makes the encoder about 9% faster. + final byte[] alphabet = this.alphabet; + final byte[] output = this.output; + int op = 0; + int count = this.count; + + int p = offset; + len += offset; + int v = -1; + + // First we need to concatenate the tail of the previous call + // with any input bytes available now and see if we can empty + // the tail. + + switch (tailLen) { + case 0: + // There was no tail. + break; + + case 1: + if (p+2 <= len) { + // A 1-byte tail with at least 2 bytes of + // input available now. + v = ((tail[0] & 0xff) << 16) | + ((input[p++] & 0xff) << 8) | + (input[p++] & 0xff); + tailLen = 0; + }; + break; + + case 2: + if (p+1 <= len) { + // A 2-byte tail with at least 1 byte of input. + v = ((tail[0] & 0xff) << 16) | + ((tail[1] & 0xff) << 8) | + (input[p++] & 0xff); + tailLen = 0; + } + break; + } + + if (v != -1) { + output[op++] = alphabet[(v >> 18) & 0x3f]; + output[op++] = alphabet[(v >> 12) & 0x3f]; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (--count == 0) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + count = LINE_GROUPS; + } + } + + // At this point either there is no tail, or there are fewer + // than 3 bytes of input available. + + // The main loop, turning 3 input bytes into 4 output bytes on + // each iteration. + while (p+3 <= len) { + v = ((input[p] & 0xff) << 16) | + ((input[p+1] & 0xff) << 8) | + (input[p+2] & 0xff); + output[op] = alphabet[(v >> 18) & 0x3f]; + output[op+1] = alphabet[(v >> 12) & 0x3f]; + output[op+2] = alphabet[(v >> 6) & 0x3f]; + output[op+3] = alphabet[v & 0x3f]; + p += 3; + op += 4; + if (--count == 0) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + count = LINE_GROUPS; + } + } + + if (finish) { + // Finish up the tail of the input. Note that we need to + // consume any bytes in tail before any bytes + // remaining in input; there should be at most two bytes + // total. + + if (p-tailLen == len-1) { + int t = 0; + v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4; + tailLen -= t; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (do_padding) { + output[op++] = '='; + output[op++] = '='; + } + if (do_newline) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + } else if (p-tailLen == len-2) { + int t = 0; + v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) | + (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2); + tailLen -= t; + output[op++] = alphabet[(v >> 12) & 0x3f]; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (do_padding) { + output[op++] = '='; + } + if (do_newline) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + } else if (do_newline && op > 0 && count != LINE_GROUPS) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + + assert tailLen == 0; + assert p == len; + } else { + // Save the leftovers in tail to be consumed on the next + // call to encodeInternal. + + if (p == len-1) { + tail[tailLen++] = input[p]; + } else if (p == len-2) { + tail[tailLen++] = input[p]; + tail[tailLen++] = input[p+1]; + } + } + + this.op = op; + this.count = count; + + return true; + } + } + + private Base64() { } // don't instantiate +} diff --git a/src/main/java/com/pubnub/api/vendor/Crypto.java b/src/main/java/com/pubnub/api/vendor/Crypto.java new file mode 100644 index 000000000..31c86a904 --- /dev/null +++ b/src/main/java/com/pubnub/api/vendor/Crypto.java @@ -0,0 +1,184 @@ +package com.pubnub.api.vendor; + +import com.pubnub.api.builder.PubNubErrorBuilder; +import com.pubnub.api.PubNubError; +import com.pubnub.api.PubNubException; +import lombok.extern.slf4j.Slf4j; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.spec.AlgorithmParameterSpec; + + + +@Slf4j +public class Crypto { + + byte[] keyBytes = null; + byte[] ivBytes = null; + String initializationVector = "0123456789012345"; + String cipherKey; + boolean INIT = false; + + public Crypto(String cipherKey) { + this.cipherKey = cipherKey; + } + + public Crypto(String cipherKey, String customInitializationVector) { + if (customInitializationVector != null) { + this.initializationVector = customInitializationVector; + } + + this.cipherKey = cipherKey; + } + + public void initCiphers() throws PubNubException { + if (INIT) + return; + try { + + keyBytes = new String(hexEncode(sha256(this.cipherKey.getBytes("UTF-8"))), "UTF-8") + .substring(0, 32) + .toLowerCase().getBytes("UTF-8"); + ivBytes = initializationVector.getBytes("UTF-8"); + INIT = true; + } catch (UnsupportedEncodingException e) { + throw PubNubException.builder().pubnubError(newCryptoError(11, e.toString())).errormsg(e.getMessage()).build(); + } + } + + public static byte[] hexEncode(byte[] input) throws PubNubException { + StringBuffer result = new StringBuffer(); + for (byte byt : input) + result.append(Integer.toString((byt & 0xff) + 0x100, 16).substring(1)); + try { + return result.toString().getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw PubNubException.builder().pubnubError(newCryptoError(12, e.toString())).errormsg(e.getMessage()).build(); + } + } + + private static PubNubError newCryptoError(int code, String message) { + + return PubNubErrorBuilder.createCryptoError(code, message); + } + + public String encrypt(String input) throws PubNubException { + try { + initCiphers(); + AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes); + SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES"); + Cipher cipher = null; + cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec); + return new String(Base64.encode(cipher.doFinal(input.getBytes("UTF-8")), 0), Charset.forName("UTF-8")); + } catch (NoSuchAlgorithmException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (NoSuchPaddingException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (InvalidKeyException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (InvalidAlgorithmParameterException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (UnsupportedEncodingException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (IllegalBlockSizeException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (BadPaddingException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } + + } + + /** + * Decrypt + * + * @param cipher_text + * @return String + * @throws PubNubException + */ + public String decrypt(String cipher_text) throws PubNubException { + try { + initCiphers(); + AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes); + SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES"); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec); + return new String(cipher.doFinal(Base64.decode(cipher_text, 0)), "UTF-8"); + } catch (IllegalArgumentException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (UnsupportedEncodingException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (IllegalBlockSizeException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (BadPaddingException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (InvalidKeyException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (InvalidAlgorithmParameterException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (NoSuchAlgorithmException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } catch (NoSuchPaddingException e) { + throw PubNubException.builder().errormsg(e.toString()).build(); + } + } + + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); + } + return data; + } + + /** + * Get MD5 + * + * @param input + * @return byte[] + * @throws PubNubException + */ + public static byte[] md5(String input) throws PubNubException { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("MD5"); + byte[] hashedBytes = digest.digest(input.getBytes("UTF-8")); + return hashedBytes; + } catch (NoSuchAlgorithmException e) { + throw PubNubException.builder().pubnubError(newCryptoError(118, e.toString())).errormsg(e.getMessage()).build(); + } catch (UnsupportedEncodingException e) { + throw PubNubException.builder().pubnubError(newCryptoError(119, e.toString())).errormsg(e.getMessage()).build(); + } + } + + /** + * Get SHA256 + * + * @param input + * @return byte[] + * @throws PubNubException + */ + public static byte[] sha256(byte[] input) throws PubNubException { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-256"); + byte[] hashedBytes = digest.digest(input); + return hashedBytes; + } catch (NoSuchAlgorithmException e) { + throw PubNubException.builder().pubnubError(newCryptoError(111, e.toString())).errormsg(e.getMessage()).build(); + } + } + +} diff --git a/src/main/java/com/pubnub/api/workers/SubscribeMessageWorker.java b/src/main/java/com/pubnub/api/workers/SubscribeMessageWorker.java new file mode 100644 index 000000000..ea8e0ba16 --- /dev/null +++ b/src/main/java/com/pubnub/api/workers/SubscribeMessageWorker.java @@ -0,0 +1,141 @@ +package com.pubnub.api.workers; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.enums.PNStatusCategory; +import com.pubnub.api.managers.ListenerManager; +import com.pubnub.api.models.consumer.PNErrorData; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.pubsub.PNMessageResult; +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; +import com.pubnub.api.models.server.PresenceEnvelope; +import com.pubnub.api.models.server.PublishMetaData; +import com.pubnub.api.models.server.SubscribeMessage; +import com.pubnub.api.vendor.Crypto; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.concurrent.LinkedBlockingQueue; + + +@Slf4j +public class SubscribeMessageWorker implements Runnable { + + private PubNub pubnub; + private ListenerManager listenerManager; + private LinkedBlockingQueue queue; + private ObjectMapper mapper; + + private boolean isRunning; + + public SubscribeMessageWorker(PubNub pubnubInstance, ListenerManager listenerManagerInstance, LinkedBlockingQueue queueInstance) { + this.pubnub = pubnubInstance; + this.listenerManager = listenerManagerInstance; + this.mapper = new ObjectMapper(); + this.queue = queueInstance; + } + + @Override + public void run() { + takeMessage(); + } + + + private void takeMessage() { + this.isRunning = true; + + while (this.isRunning) { + try { + this.processIncomingPayload(this.queue.take()); + } catch (InterruptedException e) { + this.isRunning = false; + log.warn("take message interrupted", e); + } + } + } + + private JsonNode processMessage(final JsonNode input) { + if (pubnub.getConfiguration().getCipherKey() == null) { + return input; + } + + Crypto crypto = new Crypto(pubnub.getConfiguration().getCipherKey()); + String outputText; + JsonNode outputObject; + + try { + outputText = crypto.decrypt(input.toString()); + } catch (PubNubException e) { + PNStatus pnStatus = PNStatus.builder().error(true) + .errorData(new PNErrorData(e.getMessage(), e)) + .operation(PNOperationType.PNSubscribeOperation) + .category(PNStatusCategory.PNDecryptionErrorCategory) + .build(); + + listenerManager.announce(pnStatus); + return null; + } + + try { + outputObject = mapper.readValue(outputText, JsonNode.class); + } catch (IOException e) { + PNStatus pnStatus = PNStatus.builder().error(true) + .errorData(new PNErrorData(e.getMessage(), e)) + .operation(PNOperationType.PNSubscribeOperation) + .category(PNStatusCategory.PNMalformedResponseCategory) + .build(); + + listenerManager.announce(pnStatus); + return null; + } + + return outputObject; + } + + private void processIncomingPayload(final SubscribeMessage message) { + String channel = message.getChannel(); + String subscriptionMatch = message.getSubscriptionMatch(); + PublishMetaData publishMetaData = message.getPublishMetaData(); + + if (channel.equals(subscriptionMatch)) { + subscriptionMatch = null; + } + + if (message.getChannel().contains("-pnpres")) { + PresenceEnvelope presencePayload = mapper.convertValue(message.getPayload(), PresenceEnvelope.class); + + PNPresenceEventResult pnPresenceEventResult = PNPresenceEventResult.builder() + .event(presencePayload.getAction()) + .actualChannel((subscriptionMatch != null) ? channel : null) + .subscribedChannel(subscriptionMatch != null ? subscriptionMatch : channel) + .state(presencePayload.getData()) + .timetoken(publishMetaData.getPublishTimetoken()) + .occupancy(presencePayload.getOccupancy()) + .uuid(presencePayload.getUuid()) + .timestamp(presencePayload.getTimestamp()) + .build(); + + listenerManager.announce(pnPresenceEventResult); + } else { + JsonNode extractedMessage = processMessage(message.getPayload()); + + if (extractedMessage == null) { + log.debug("unable to parse payload on #processIncomingMessages"); + } + + PNMessageResult pnMessageResult = PNMessageResult.builder() + .message(extractedMessage) + .actualChannel((subscriptionMatch != null) ? channel : null) + .subscribedChannel(subscriptionMatch != null ? subscriptionMatch : channel) + .timetoken(publishMetaData.getPublishTimetoken()) + .build(); + + + listenerManager.announce(pnMessageResult); + } + } + +} diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties new file mode 100644 index 000000000..308c9f8ec --- /dev/null +++ b/src/main/resources/version.properties @@ -0,0 +1 @@ +version=${projectVersion} \ No newline at end of file diff --git a/src/test/checkstyle.xml b/src/test/checkstyle.xml new file mode 100644 index 000000000..f94685081 --- /dev/null +++ b/src/test/checkstyle.xml @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/PubNubExceptionTest.java b/src/test/java/PubNubExceptionTest.java new file mode 100644 index 000000000..9d1705789 --- /dev/null +++ b/src/test/java/PubNubExceptionTest.java @@ -0,0 +1,71 @@ +import com.fasterxml.jackson.databind.JsonNode; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubError; +import com.pubnub.api.PubNubException; +import com.pubnub.api.endpoints.TestHarness; +import com.pubnub.api.endpoints.pubsub.Publish; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; + +/** + * Created by tukunare on 7/5/2016. + */ +public class PubNubExceptionTest extends TestHarness { + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + private PubNub pubnub; + private Publish instance; + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + instance = pubnub.publish(); + } + + @Test + public void testPubnubError() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withStatus(404).withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + int statusCode = -1; + PubNubError pubnubError = null; + int pnErrorCode= - 1; + int pnErroCodeExtended = -1; + JsonNode pnErrorJNode = null; + String pnErrorMessage = null; + String pnErrorString = null; + String response = null; + String erroMsg = null; + JsonNode jNode = null; + + + try { + instance.channel("coolChannel").message(new Object()).sync(); + } + catch (PubNubException error) { + statusCode = error.getStatusCode(); + pubnubError = error.getPubnubError(); + pnErrorCode = pubnubError.getErrorCode(); + pnErroCodeExtended = pubnubError.getErrorCodeExtended(); + pnErrorJNode = pubnubError.getErrorObject(); + pnErrorMessage = pubnubError.getMessage(); + pnErrorString = pubnubError.getErrorString(); + response = error.getResponse(); + erroMsg = error.getErrormsg(); + jNode = error.getJso(); + } + + assertEquals(0, statusCode); + } + + +} diff --git a/src/test/java/com/pubnub/api/Base64Test.java b/src/test/java/com/pubnub/api/Base64Test.java new file mode 100644 index 000000000..72d4ee810 --- /dev/null +++ b/src/test/java/com/pubnub/api/Base64Test.java @@ -0,0 +1,15 @@ +package com.pubnub.api; + +import com.pubnub.api.vendor.Base64; +import org.junit.Assert; +import org.junit.Test; + +public class Base64Test { + + + @Test + public void testBase64Encode(){ + Assert.assertEquals("YWJj", Base64.encodeToString("abc".getBytes(), 0).trim()); + } + +} diff --git a/src/test/java/com/pubnub/api/PubNubTest.java b/src/test/java/com/pubnub/api/PubNubTest.java new file mode 100644 index 000000000..eb07719c3 --- /dev/null +++ b/src/test/java/com/pubnub/api/PubNubTest.java @@ -0,0 +1,109 @@ +package com.pubnub.api; + +import com.pubnub.api.enums.PNReconnectionPolicy; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +public class PubNubTest { + private PubNub pubnub; + private PNConfiguration pnConfiguration; + + @Before + public void beforeEach() throws IOException { + pnConfiguration = new PNConfiguration(); + pnConfiguration.setSubscribeKey("demo"); + pnConfiguration.setPublishKey("demo"); + } + + @org.junit.Test + public void testCreateSuccess() throws IOException, PubNubException { + pubnub = new PubNub(pnConfiguration); + Assert.assertEquals(true, pubnub.getConfiguration().isSecure()); + Assert.assertNotNull("pubnub object is null", pubnub); + Assert.assertNotNull(pubnub.getConfiguration()); + Assert.assertEquals("https://pubsub.pubnub.com", pubnub.getBaseUrl()); + } + + @Test + public void testEncryptCustomKey() throws PubNubException { + pubnub = new PubNub(pnConfiguration); + Assert.assertEquals("iALQtn3PfIXe74CT/wrS7g==", pubnub.encrypt("test1", "cipherKey").trim()); + + } + + @Test + public void testEncryptConfigurationKey() throws PubNubException { + pnConfiguration.setCipherKey("cipherKey"); + pubnub = new PubNub(pnConfiguration); + Assert.assertEquals("iALQtn3PfIXe74CT/wrS7g==", pubnub.encrypt("test1").trim()); + + } + + @Test + public void testDecryptCustomKey() throws PubNubException { + pubnub = new PubNub(pnConfiguration); + Assert.assertEquals("test1", pubnub.decrypt("iALQtn3PfIXe74CT/wrS7g==", "cipherKey").trim()); + + } + + @Test + public void testDecryptConfigurationKey() throws PubNubException { + pnConfiguration.setCipherKey("cipherKey"); + pubnub = new PubNub(pnConfiguration); + Assert.assertEquals("test1", pubnub.decrypt("iALQtn3PfIXe74CT/wrS7g==").trim()); + + } + + @org.junit.Test + public void testPNConfiguration() throws IOException, PubNubException { + pnConfiguration.setSubscribeTimeout(3000); + pnConfiguration.setConnectTimeout(4000); + pnConfiguration.setNonSubscribeRequestTimeout(5000); + pnConfiguration.setReconnectionPolicy(PNReconnectionPolicy.NONE); + pubnub = new PubNub(pnConfiguration); + + Assert.assertNotNull("pubnub object is null", pubnub); + Assert.assertNotNull(pubnub.getConfiguration()); + Assert.assertEquals("https://pubsub.pubnub.com", pubnub.getBaseUrl()); + Assert.assertEquals(3000, pnConfiguration.getSubscribeTimeout()); + Assert.assertEquals(4000, pnConfiguration.getConnectTimeout()); + Assert.assertEquals(5000, pnConfiguration.getNonSubscribeRequestTimeout()); + } + + @org.junit.Test(expected = PubNubException.class) + public void testDecryptNull() throws PubNubException { + pnConfiguration.setCipherKey("cipherKey"); + pubnub = new PubNub(pnConfiguration); + Assert.assertEquals("test1", pubnub.decrypt(null).trim()); + } + + @org.junit.Test(expected = PubNubException.class) + public void testDecryptNull_B() throws PubNubException { + pubnub = new PubNub(pnConfiguration); + Assert.assertEquals("test1", pubnub.decrypt(null, "cipherKey").trim()); + } + + @org.junit.Test + public void GetVersionAndTimeStamp() throws PubNubException { + pubnub = new PubNub(pnConfiguration); + String version = pubnub.getVersion(); + int timeStamp = pubnub.getTimestamp(); + Assert.assertEquals("4.0.8", version); + Assert.assertTrue(timeStamp > 0); + } + + @org.junit.Test(expected = PubNubException.class) + public void testEcryptNull() throws PubNubException { + pubnub = new PubNub(pnConfiguration); + Assert.assertEquals("test1", pubnub.encrypt(null)); + } + + @org.junit.Test(expected = PubNubException.class) + public void testEcryptNull_B() throws PubNubException { + pubnub = new PubNub(pnConfiguration); + Assert.assertEquals("test1", pubnub.encrypt(null, "chiperKey")); + } +} diff --git a/src/test/java/com/pubnub/api/endpoints/EndpointTest.java b/src/test/java/com/pubnub/api/endpoints/EndpointTest.java new file mode 100644 index 000000000..1c6b37227 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/EndpointTest.java @@ -0,0 +1,106 @@ +package com.pubnub.api.endpoints; + +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.endpoints.channel_groups.RemoveChannelChannelGroup; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsRemoveChannelResult; +import okhttp3.Request; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertNotNull; + +public class EndpointTest extends TestHarness { + + PubNub pubnub; + + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + } + + @Test + public void testUUID() throws PubNubException { + Endpoint endpoint = new Endpoint(pubnub, null) { + + @Override + protected void validateParams() throws PubNubException { + } + + @Override + protected Object createResponse(Response input) throws PubNubException { + return null; + } + + @Override + protected PNOperationType getOperationType() { + return null; + } + + @Override + protected boolean isAuthRequired() { + return true; + } + + @Override + protected Call doWork(Map baseParams) throws PubNubException { + + Call fakeCall = new Call() { + + @Override + public Response execute() throws IOException { + Response newResponse = Response.success(null); + return newResponse; + } + + @Override + public void enqueue(Callback callback) { + + } + + @Override + public boolean isExecuted() { + return false; + } + + @Override + public void cancel() { + + } + + @Override + public boolean isCanceled() { + return false; + } + + @Override + public Call clone() { + return null; + } + + @Override + public Request request() { + return null; + } + }; + + Assert.assertEquals("myUUID",baseParams.get("uuid")); + return fakeCall; + } + }; + + endpoint.sync(); + } + +} diff --git a/src/test/java/com/pubnub/api/endpoints/HeartbeatEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/HeartbeatEndpointTest.java new file mode 100644 index 000000000..91a27c44e --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/HeartbeatEndpointTest.java @@ -0,0 +1,183 @@ +package com.pubnub.api.endpoints; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.endpoints.presence.Heartbeat; +import com.pubnub.api.managers.RetrofitManager; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; + +public class HeartbeatEndpointTest extends TestHarness { + + private Heartbeat partialHeartbeat; + private PubNub pubnub; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + RetrofitManager retrofitManager = new RetrofitManager(pubnub); + partialHeartbeat = new Heartbeat(pubnub, retrofitManager.getTransactionInstance()); + } + + @org.junit.Test + public void testSuccessOneChannel() throws PubNubException, InterruptedException { + pubnub.getConfiguration().setPresenceTimeout(123); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\"}"))); + + partialHeartbeat.channels(Arrays.asList("ch1")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + LoggedRequest request = requests.get(0); + Assert.assertEquals("myUUID", request.queryParameter("uuid").firstValue()); + Assert.assertEquals("123", request.queryParameter("heartbeat").firstValue()); + + } + + @org.junit.Test + public void testSuccessManyChannels() throws PubNubException, InterruptedException { + pubnub.getConfiguration().setPresenceTimeout(123); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1,ch2/heartbeat")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\"}"))); + + partialHeartbeat.channels(Arrays.asList("ch1", "ch2")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + LoggedRequest request = requests.get(0); + Assert.assertEquals("myUUID", request.queryParameter("uuid").firstValue()); + Assert.assertEquals("123", request.queryParameter("heartbeat").firstValue()); + } + + @org.junit.Test + public void testSuccessOneChannelGroup() throws PubNubException, InterruptedException { + pubnub.getConfiguration().setPresenceTimeout(123); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/heartbeat")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\"}"))); + + partialHeartbeat.channelGroups(Arrays.asList("cg1")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + LoggedRequest request = requests.get(0); + Assert.assertEquals("myUUID", request.queryParameter("uuid").firstValue()); + Assert.assertEquals("cg1", request.queryParameter("channel-group").firstValue()); + Assert.assertEquals("123", request.queryParameter("heartbeat").firstValue()); + } + + @org.junit.Test + public void testSuccessManyChannelGroups() throws PubNubException, InterruptedException { + pubnub.getConfiguration().setPresenceTimeout(123); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/heartbeat")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\"}"))); + + partialHeartbeat.channelGroups(Arrays.asList("cg1", "cg2")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + LoggedRequest request = requests.get(0); + Assert.assertEquals("myUUID", request.queryParameter("uuid").firstValue()); + Assert.assertEquals("cg1,cg2", request.queryParameter("channel-group").firstValue()); + Assert.assertEquals("123", request.queryParameter("heartbeat").firstValue()); + + } + + @org.junit.Test + public void testSuccessIncludeState() throws PubNubException, InterruptedException { + Map state = new HashMap<>(); + state.put("CH1", "this-is-channel1"); + state.put("CH2", "this-is-channel2"); + + pubnub.getConfiguration().setPresenceTimeout(123); + + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1,ch2/heartbeat")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\"}"))); + + partialHeartbeat.channels(Arrays.asList("ch1", "ch2")).state(state).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + LoggedRequest request = requests.get(0); + Assert.assertEquals("myUUID", request.queryParameter("uuid").firstValue()); + Assert.assertEquals("123", request.queryParameter("heartbeat").firstValue()); + Assert.assertEquals("%7B%22CH2%22%3A%22this-is-channel2%22%2C%22CH1%22%3A%22this-is-channel1%22%7D", request.queryParameter("state").firstValue()); + + } + + @org.junit.Test(expected=PubNubException.class) + public void testMissingChannelAndGroupSync() throws PubNubException, InterruptedException { + pubnub.getConfiguration().setPresenceTimeout(123); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\"}"))); + + partialHeartbeat.sync(); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\"}"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + partialHeartbeat.channels(Arrays.asList("ch1")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubKeySync() throws PubNubException, InterruptedException { + pubnub.getConfiguration().setPresenceTimeout(123); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\"}"))); + + pubnub.getConfiguration().setSubscribeKey(null); + partialHeartbeat.channels(Arrays.asList("ch1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubKeySync() throws PubNubException, InterruptedException { + pubnub.getConfiguration().setPresenceTimeout(123); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\"}"))); + + pubnub.getConfiguration().setSubscribeKey(""); + partialHeartbeat.channels(Arrays.asList("ch1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testInvalidStateSync() throws PubNubException, InterruptedException { + pubnub.getConfiguration().setPresenceTimeout(123); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/heartbeat")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\"}"))); + + partialHeartbeat.channels(Arrays.asList("ch1")).state(new Object()).sync(); + } +} diff --git a/src/test/java/com/pubnub/api/endpoints/HistoryEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/HistoryEndpointTest.java new file mode 100644 index 000000000..5b870f749 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/HistoryEndpointTest.java @@ -0,0 +1,412 @@ +package com.pubnub.api.endpoints; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.history.PNHistoryResult; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; + + +public class HistoryEndpointTest extends TestHarness { + + private History partialHistory; + private PubNub pubnub; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + partialHistory = pubnub.history(); + } + + + @org.junit.Test + public void testSyncSuccess() throws IOException, PubNubException { + List testArray = new ArrayList(); + List historyItems = new ArrayList(); + ObjectMapper mapper = new ObjectMapper(); + + + Map historyEnvelope1 = new HashMap(); + Map historyItem1 = new HashMap(); + historyItem1.put("a", 11); + historyItem1.put("b", 22); + historyEnvelope1.put("timetoken", 1111); + historyEnvelope1.put("message", historyItem1); + + Map historyEnvelope2 = new HashMap(); + Map historyItem2 = new HashMap(); + historyItem2.put("a", 33); + historyItem2.put("b", 44); + historyEnvelope2.put("timetoken", 2222); + historyEnvelope2.put("message", historyItem2); + + historyItems.add(historyEnvelope1); + historyItems.add(historyEnvelope2); + + testArray.add(historyItems); + testArray.add(1234); + testArray.add(4321); + + stubFor(get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(mapper.writeValueAsString(testArray)))); + + PNHistoryResult response = partialHistory.channel("niceChannel").includeTimetoken(true).sync(); + + Assert.assertTrue(response.getStartTimetoken().equals(1234L)); + Assert.assertTrue(response.getEndTimetoken().equals(4321L)); + + Assert.assertEquals(response.getMessages().size(), 2); + + Assert.assertTrue(response.getMessages().get(0).getTimetoken().equals(1111L)); + Assert.assertEquals((response.getMessages().get(0).getEntry()).get("a").asInt(), 11); + Assert.assertEquals((response.getMessages().get(0).getEntry()).get("b").asInt(), 22); + + Assert.assertTrue(response.getMessages().get(1).getTimetoken().equals(2222L)); + Assert.assertEquals((response.getMessages().get(1).getEntry()).get("a").asInt(), 33); + Assert.assertEquals((response.getMessages().get(1).getEntry()).get("b").asInt(), 44); + } + + @Test + public void testSyncAuthSuccess() throws PubNubException, JsonProcessingException { + + pubnub.getConfiguration().setAuthKey("authKey"); + + List testArray = new ArrayList(); + List historyItems = new ArrayList(); + ObjectMapper mapper = new ObjectMapper(); + + + Map historyEnvelope1 = new HashMap(); + Map historyItem1 = new HashMap(); + historyItem1.put("a", 11); + historyItem1.put("b", 22); + historyEnvelope1.put("timetoken", 1111); + historyEnvelope1.put("message", historyItem1); + + Map historyEnvelope2 = new HashMap(); + Map historyItem2 = new HashMap(); + historyItem2.put("a", 33); + historyItem2.put("b", 44); + historyEnvelope2.put("timetoken", 2222); + historyEnvelope2.put("message", historyItem2); + + historyItems.add(historyEnvelope1); + historyItems.add(historyEnvelope2); + + testArray.add(historyItems); + testArray.add(1234); + testArray.add(4321); + + stubFor(get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(mapper.writeValueAsString(testArray)))); + + + PNHistoryResult response = partialHistory.channel("niceChannel").includeTimetoken(true).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals("authKey", requests.get(0).queryParameter("auth").firstValue()); + assertEquals(1, requests.size()); + } + + + @org.junit.Test + public void testSyncEncryptedSuccess() throws IOException, PubNubException { + pubnub.getConfiguration().setCipherKey("testCipher"); + + stubFor(get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody("[[\"EGwV+Ti43wh2TprPIq7o0KMuW5j6B3yWy352ucWIOmU=\\n\",\"EGwV+Ti43wh2TprPIq7o0KMuW5j6B3yWy352ucWIOmU=\\n\",\"EGwV+Ti43wh2TprPIq7o0KMuW5j6B3yWy352ucWIOmU=\\n\"],14606134331557853,14606134485013970]"))); + + PNHistoryResult response = partialHistory.channel("niceChannel").includeTimetoken(false).sync(); + + Assert.assertTrue(response.getStartTimetoken().equals(14606134331557853L)); + Assert.assertTrue(response.getEndTimetoken().equals(14606134485013970L)); + + Assert.assertEquals(response.getMessages().size(), 3); + + Assert.assertEquals(response.getMessages().get(0).getTimetoken(), null); + Assert.assertEquals("m1", (response.getMessages().get(0).getEntry()).get(0).asText()); + Assert.assertEquals("m2", (response.getMessages().get(0).getEntry()).get(1).asText()); + Assert.assertEquals("m3", (response.getMessages().get(0).getEntry()).get(2).asText()); + + Assert.assertEquals("m1", (response.getMessages().get(1).getEntry()).get(0).asText()); + Assert.assertEquals("m2", (response.getMessages().get(1).getEntry()).get(1).asText()); + Assert.assertEquals("m3", (response.getMessages().get(1).getEntry()).get(2).asText()); + + Assert.assertEquals("m1", (response.getMessages().get(2).getEntry()).get(0).asText()); + Assert.assertEquals("m2", (response.getMessages().get(2).getEntry()).get(1).asText()); + Assert.assertEquals("m3", (response.getMessages().get(2).getEntry()).get(2).asText()); + + } + + @org.junit.Test + public void testSyncSuccessWithoutTimeToken() throws IOException, PubNubException { + List testArray = new ArrayList(); + List historyItems = new ArrayList(); + ObjectMapper mapper = new ObjectMapper(); + + + Map historyItem1 = new HashMap(); + historyItem1.put("a", 11); + historyItem1.put("b", 22); + + Map historyItem2 = new HashMap(); + historyItem2.put("a", 33); + historyItem2.put("b", 44); + + historyItems.add(historyItem1); + historyItems.add(historyItem2); + + testArray.add(historyItems); + testArray.add(1234); + testArray.add(4321); + + stubFor(get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(mapper.writeValueAsString(testArray)))); + + PNHistoryResult response = partialHistory.channel("niceChannel").sync(); + + Assert.assertTrue(response.getStartTimetoken().equals(1234L)); + Assert.assertTrue(response.getEndTimetoken().equals(4321L)); + + Assert.assertEquals(response.getMessages().size(), 2); + + Assert.assertNull(response.getMessages().get(0).getTimetoken()); + Assert.assertEquals(response.getMessages().get(0).getEntry().get("a").asInt(), 11); + Assert.assertEquals(response.getMessages().get(0).getEntry().get("b").asInt(), 22); + + Assert.assertNull(response.getMessages().get(1).getTimetoken()); + Assert.assertEquals(response.getMessages().get(1).getEntry().get("a").asInt(), 33); + Assert.assertEquals(response.getMessages().get(1).getEntry().get("b").asInt(), 44); + } + + + @org.junit.Test(expected=PubNubException.class) + public void testMissinChannel() throws IOException, PubNubException { + List testArray = new ArrayList(); + List historyItems = new ArrayList(); + ObjectMapper mapper = new ObjectMapper(); + + + Map historyEnvelope1 = new HashMap(); + Map historyItem1 = new HashMap(); + historyItem1.put("a", 11); + historyItem1.put("b", 22); + historyEnvelope1.put("timetoken", 1111); + historyEnvelope1.put("message", historyItem1); + + Map historyEnvelope2 = new HashMap(); + Map historyItem2 = new HashMap(); + historyItem2.put("a", 33); + historyItem2.put("b", 44); + historyEnvelope2.put("timetoken", 2222); + historyEnvelope2.put("message", historyItem2); + + historyItems.add(historyEnvelope1); + historyItems.add(historyEnvelope2); + + testArray.add(historyItems); + testArray.add(1234); + testArray.add(4321); + + stubFor(get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(mapper.writeValueAsString(testArray)))); + + PNHistoryResult response = partialHistory.includeTimetoken(true).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testChannelIsEmpty() throws IOException, PubNubException { + List testArray = new ArrayList(); + List historyItems = new ArrayList(); + ObjectMapper mapper = new ObjectMapper(); + + + Map historyEnvelope1 = new HashMap(); + Map historyItem1 = new HashMap(); + historyItem1.put("a", 11); + historyItem1.put("b", 22); + historyEnvelope1.put("timetoken", 1111); + historyEnvelope1.put("message", historyItem1); + + Map historyEnvelope2 = new HashMap(); + Map historyItem2 = new HashMap(); + historyItem2.put("a", 33); + historyItem2.put("b", 44); + historyEnvelope2.put("timetoken", 2222); + historyEnvelope2.put("message", historyItem2); + + historyItems.add(historyEnvelope1); + historyItems.add(historyEnvelope2); + + testArray.add(historyItems); + testArray.add(1234); + testArray.add(4321); + + stubFor(get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(mapper.writeValueAsString(testArray)))); + + PNHistoryResult response = partialHistory.channel("").includeTimetoken(true).sync(); + } + + @org.junit.Test + public void testOperationTypeSuccessAsync() throws IOException, PubNubException, InterruptedException { + + List testArray = new ArrayList(); + List historyItems = new ArrayList(); + ObjectMapper mapper = new ObjectMapper(); + + + Map historyEnvelope1 = new HashMap(); + Map historyItem1 = new HashMap(); + historyItem1.put("a", 11); + historyItem1.put("b", 22); + historyEnvelope1.put("timetoken", 1111); + historyEnvelope1.put("message", historyItem1); + + Map historyEnvelope2 = new HashMap(); + Map historyItem2 = new HashMap(); + historyItem2.put("a", 33); + historyItem2.put("b", 44); + historyEnvelope2.put("timetoken", 2222); + historyEnvelope2.put("message", historyItem2); + + historyItems.add(historyEnvelope1); + historyItems.add(historyEnvelope2); + + testArray.add(historyItems); + testArray.add(1234); + testArray.add(4321); + + stubFor(get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(mapper.writeValueAsString(testArray)))); + + final AtomicInteger atomic = new AtomicInteger(0); + partialHistory.channel("niceChannel").includeTimetoken(true).async(new PNCallback() { + @Override + public void onResponse(PNHistoryResult result, PNStatus status) { + if (status != null && status.getOperation()== PNOperationType.PNHistoryOperation) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + @org.junit.Test + public void testSyncCountReverseStartEndSuccess() throws IOException, PubNubException { + List testArray = new ArrayList(); + List historyItems = new ArrayList(); + ObjectMapper mapper = new ObjectMapper(); + + + Map historyEnvelope1 = new HashMap(); + Map historyItem1 = new HashMap(); + historyItem1.put("a", 11); + historyItem1.put("b", 22); + historyEnvelope1.put("timetoken", 1111); + historyEnvelope1.put("message", historyItem1); + + Map historyEnvelope2 = new HashMap(); + Map historyItem2 = new HashMap(); + historyItem2.put("a", 33); + historyItem2.put("b", 44); + historyEnvelope2.put("timetoken", 2222); + historyEnvelope2.put("message", historyItem2); + + historyItems.add(historyEnvelope1); + historyItems.add(historyEnvelope2); + + testArray.add(historyItems); + testArray.add(1234); + testArray.add(4321); + + stubFor(get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(mapper.writeValueAsString(testArray)))); + + PNHistoryResult response = partialHistory.channel("niceChannel").count(5).reverse(true).start(1L).end(2L).includeTimetoken(true).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/v2/history/sub-key/mySubscribeKey/channel/niceChannel.*"))); + Assert.assertTrue(requests.get(0).queryParameter("reverse").firstValue().equals("true")); + Assert.assertTrue(Integer.valueOf(requests.get(0).queryParameter("count").firstValue()).equals(5)); + Assert.assertTrue(Integer.valueOf(requests.get(0).queryParameter("start").firstValue()).equals(1)); + Assert.assertTrue(Integer.valueOf(requests.get(0).queryParameter("end").firstValue()).equals(2)); + Assert.assertTrue(requests.get(0).queryParameter("include_token").firstValue().equals("true")); + + + Assert.assertTrue(response.getStartTimetoken().equals(1234L)); + Assert.assertTrue(response.getEndTimetoken().equals(4321L)); + + Assert.assertEquals(response.getMessages().size(), 2); + + Assert.assertTrue(response.getMessages().get(0).getTimetoken().equals(1111L)); + Assert.assertEquals((response.getMessages().get(0).getEntry()).get("a").asInt(), 11); + Assert.assertEquals((response.getMessages().get(0).getEntry()).get("b").asInt(), 22); + + Assert.assertTrue(response.getMessages().get(1).getTimetoken().equals(2222L)); + Assert.assertEquals((response.getMessages().get(1).getEntry()).get("a").asInt(), 33); + Assert.assertEquals((response.getMessages().get(1).getEntry()).get("b").asInt(), 44); + } + + @org.junit.Test(expected=PubNubException.class) + public void testSyncProcessMessageError() throws IOException, PubNubException { + List testArray = new ArrayList(); + List historyItems = new ArrayList(); + ObjectMapper mapper = new ObjectMapper(); + + + Map historyEnvelope1 = new HashMap(); + Map historyItem1 = new HashMap(); + historyItem1.put("a", 11); + historyItem1.put("b", 22); + historyEnvelope1.put("timetoken", 1111); + historyEnvelope1.put("message", historyItem1); + + Map historyEnvelope2 = new HashMap(); + Map historyItem2 = new HashMap(); + historyItem2.put("a", 33); + historyItem2.put("b", 44); + historyEnvelope2.put("timetoken", 2222); + historyEnvelope2.put("message", historyItem2); + + historyItems.add(historyEnvelope1); + historyItems.add(historyEnvelope2); + + testArray.add(historyItems); + testArray.add(1234); + testArray.add(4321); + + stubFor(get(urlPathEqualTo("/v2/history/sub-key/mySubscribeKey/channel/niceChannel")) + .willReturn(aResponse().withBody(mapper.writeValueAsString(testArray)))); + + pubnub.getConfiguration().setCipherKey("Test"); + PNHistoryResult response = partialHistory.channel("niceChannel").count(5).reverse(true).start(1L).end(2L).includeTimetoken(true).sync(); + } + + +} diff --git a/src/test/java/com/pubnub/api/endpoints/TestHarness.java b/src/test/java/com/pubnub/api/endpoints/TestHarness.java new file mode 100644 index 000000000..a0b709888 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/TestHarness.java @@ -0,0 +1,38 @@ +package com.pubnub.api.endpoints; + +import com.pubnub.api.PNConfiguration; +import com.pubnub.api.PubNub; +import com.pubnub.api.enums.PNLogVerbosity; + +public class TestHarness { + + protected PubNub createPubNubInstance(int port) { + PNConfiguration pnConfiguration = new PNConfiguration(); + pnConfiguration.setOrigin("localhost" + ":" + port); + pnConfiguration.setSecure(false); + pnConfiguration.setSubscribeKey("mySubscribeKey"); + pnConfiguration.setPublishKey("myPublishKey"); + pnConfiguration.setUuid("myUUID"); + pnConfiguration.setLogVerbosity(PNLogVerbosity.BODY); + + class MockedTimePubNub extends PubNub { + + public MockedTimePubNub(PNConfiguration initialConfig) { + super(initialConfig); + } + + @Override + public int getTimestamp() { + return 1337; + } + + @Override + public String getVersion() { + return "suchJava"; + } + + } + + return new MockedTimePubNub(pnConfiguration); + } +} diff --git a/src/test/java/com/pubnub/api/endpoints/TimeEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/TimeEndpointTest.java new file mode 100644 index 000000000..b4a131143 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/TimeEndpointTest.java @@ -0,0 +1,190 @@ +package com.pubnub.api.endpoints; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.callbacks.TimeCallback; +import com.pubnub.api.PubNubException; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.PNTimeResult; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class TimeEndpointTest extends TestHarness { + private Time partialTime; + private PubNub pubnub; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + partialTime = pubnub.time(); + } + + + @org.junit.Test + public void testSyncSuccess() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/time/0")) + .willReturn(aResponse().withBody("[14593046077243110]"))); + + PNTimeResult response = partialTime.sync(); + assertTrue(response.getTimetoken().equals(14593046077243110L)); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + + } + + @org.junit.Test(expected=PubNubException.class) + public void testSyncBrokenWithString() throws IOException, PubNubException { + stubFor(get(urlPathEqualTo("/time/0")) + .willReturn(aResponse().withBody("[abc]"))); + partialTime.sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testSyncBrokenWithoutJSON() throws IOException, PubNubException { + stubFor(get(urlPathEqualTo("/time/0")) + .willReturn(aResponse().withBody("zimp"))); + partialTime.sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testSyncBrokenWithout200() throws IOException, PubNubException { + stubFor(get(urlPathEqualTo("/time/0")) + .willReturn(aResponse().withBody("[14593046077243110]").withStatus(404))); + PNTimeResult response = partialTime.sync(); + assertEquals(response.getTimetoken(), "14593046077243110"); + } + + @org.junit.Test + public void testAsyncSuccess() throws IOException, PubNubException { + stubFor(get(urlPathEqualTo("/time/0")) + .willReturn(aResponse().withBody("[14593046077243110]"))); + final AtomicInteger atomic = new AtomicInteger(0); + partialTime.async(new TimeCallback(){ + + @Override + public void onResponse(PNTimeResult result, PNStatus status) { + assertTrue(result.getTimetoken().equals(14593046077243110L)); + atomic.incrementAndGet(); + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + @org.junit.Test + public void testAsyncRetrySuccess() throws IOException, PubNubException { + stubFor(get(urlPathEqualTo("/time/0")) + .willReturn(aResponse().withBody("[14593046077243110]"))); + final AtomicInteger atomic = new AtomicInteger(0); + partialTime.async(new TimeCallback(){ + + @Override + public void onResponse(PNTimeResult result, PNStatus status) { + + + assertTrue(result.getTimetoken().equals(14593046077243110L)); + atomic.incrementAndGet(); + + if (atomic.get() == 1) { + status.retry(); + } + + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(2)); + } + + @org.junit.Test + public void testAsyncBrokenWithString() throws IOException, PubNubException { + stubFor(get(urlPathEqualTo("/time/0")) + .willReturn(aResponse().withBody("[abc]"))); + final AtomicInteger atomic = new AtomicInteger(0); + partialTime.async(new TimeCallback(){ + + @Override + public void onResponse(PNTimeResult result, PNStatus status) { + if (status != null) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + + } + + @org.junit.Test + public void testAsyncBrokenWithoutJSON() throws IOException, PubNubException { + stubFor(get(urlPathEqualTo("/time/0")) + .willReturn(aResponse().withBody("zimp"))); + final AtomicInteger atomic = new AtomicInteger(0); + partialTime.async(new TimeCallback(){ + + @Override + public void onResponse(PNTimeResult result, PNStatus status) { + if (status != null) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + + } + + @org.junit.Test + public void testAsyncBrokenWithout200() throws IOException, PubNubException { + stubFor(get(urlPathEqualTo("/time/0")) + .willReturn(aResponse().withBody("[14593046077243110]").withStatus(404))); + final AtomicInteger atomic = new AtomicInteger(0); + partialTime.async(new TimeCallback(){ + + @Override + public void onResponse(PNTimeResult result, PNStatus status) { + if (status != null) { + atomic.incrementAndGet(); + } + } + + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/time/0")) + .willReturn(aResponse().withBody("[14593046077243110]"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + PNTimeResult response = partialTime.sync(); + assertTrue(response.getTimetoken().equals(14593046077243110L)); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + } + +} diff --git a/src/test/java/com/pubnub/api/endpoints/access/AuditEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/access/AuditEndpointTest.java new file mode 100644 index 000000000..2b5d8865f --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/access/AuditEndpointTest.java @@ -0,0 +1,284 @@ +package com.pubnub.api.endpoints.access; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerAuditResult; +import com.pubnub.api.endpoints.TestHarness; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.client.WireMock.matching; +import static org.junit.Assert.assertEquals; + +public class AuditEndpointTest extends TestHarness { + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + private Audit partialAudit; + private PubNub pubnub; + + @Before + public void beforeEach() throws IOException { + + pubnub = this.createPubNubInstance(8080); + partialAudit = pubnub.audit(); + + pubnub.getConfiguration().setSecretKey("secretKey"); + + } + + @Test + public void testSuccessChannelGroupSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("rXy69MNT1vceNs3Ob6HnjShUAzCV5x4OumSG1lSPL6s%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"channel-group\":\"cg2\",\"auths\":{\"key1\":{\"r\":1,\"m\":1,\"w\":1}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerAuditResult pnAccessManagerAuditResult = partialAudit.channelGroup("cg1").authKeys(Arrays.asList("key1")).sync(); + + Assert.assertEquals("cg2", pnAccessManagerAuditResult.getChannelGroup()); + Assert.assertEquals(true, pnAccessManagerAuditResult.getAuthKeys().get("key1").isManageEnabled()); + Assert.assertEquals(true, pnAccessManagerAuditResult.getAuthKeys().get("key1").isReadEnabled()); + Assert.assertEquals(true, pnAccessManagerAuditResult.getAuthKeys().get("key1").isWriteEnabled()); + Assert.assertEquals("channel-group+auth", pnAccessManagerAuditResult.getLevel()); + Assert.assertEquals("sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", pnAccessManagerAuditResult.getSubscribeKey()); + + List requests = findAll(getRequestedFor(urlMatching("/v1/auth/audit/sub-key/mySubscribeKey.*"))); + assertEquals(1, requests.size()); + + } + + @Test + public void testSuccessChannelSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("oICb_S5OubabiqrEA9vDqQ-Ri4PdztWLs6__fQQP_Gs%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":1,\"m\":1,\"w\":1}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerAuditResult pnAccessManagerAuditResult = partialAudit.channel("ch1").authKeys(Arrays.asList("key1")).sync(); + + Assert.assertEquals("ch1", pnAccessManagerAuditResult.getChannel()); + Assert.assertEquals(true, pnAccessManagerAuditResult.getAuthKeys().get("key1").isManageEnabled()); + Assert.assertEquals(true, pnAccessManagerAuditResult.getAuthKeys().get("key1").isReadEnabled()); + Assert.assertEquals(true, pnAccessManagerAuditResult.getAuthKeys().get("key1").isWriteEnabled()); + Assert.assertEquals("user", pnAccessManagerAuditResult.getLevel()); + Assert.assertEquals("sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f", pnAccessManagerAuditResult.getSubscribeKey()); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + } + + @org.junit.Test(expected=PubNubException.class) + public void testSuccessChannelMissingKeySync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("ZlPruaId7jzupmK4LUynpnjvA2CQYyrrT0475wWkbwY%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":1,\"m\":1,\"w\":1}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerAuditResult pnAccessManagerAuditResult = partialAudit.channel("ch1").sync(); + } + + @org.junit.Test + public void testOperationTypeSuccessAsync() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("rXy69MNT1vceNs3Ob6HnjShUAzCV5x4OumSG1lSPL6s%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"channel-group\":\"cg2\",\"auths\":{\"key1\":{\"r\":1,\"m\":1,\"w\":1}}},\"service\":\"Access Manager\",\"status\":200}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + partialAudit.channelGroup("cg1").authKeys(Arrays.asList("key1")).async(new PNCallback() { + @Override + public void onResponse(PNAccessManagerAuditResult result, PNStatus status) { + if (status != null && status.getOperation()== PNOperationType.PNAccessManagerAudit) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("rXy69MNT1vceNs3Ob6HnjShUAzCV5x4OumSG1lSPL6s%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"channel-group\":\"cg2\",\"auths\":{\"key1\":{\"r\":1,\"m\":1,\"w\":1}}},\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + PNAccessManagerAuditResult pnAccessManagerAuditResult = partialAudit.channelGroup("cg1").authKeys(Arrays.asList("key1")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullPayload() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("rXy69MNT1vceNs3Ob6HnjShUAzCV5x4OumSG1lSPL6s%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerAuditResult pnAccessManagerAuditResult = partialAudit.channelGroup("cg1").authKeys(Arrays.asList("key1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSecretKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("rXy69MNT1vceNs3Ob6HnjShUAzCV5x4OumSG1lSPL6s%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setSecretKey(null); + PNAccessManagerAuditResult pnAccessManagerAuditResult = partialAudit.channelGroup("cg1").authKeys(Arrays.asList("key1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySecretKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("rXy69MNT1vceNs3Ob6HnjShUAzCV5x4OumSG1lSPL6s%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setSecretKey(""); + PNAccessManagerAuditResult pnAccessManagerAuditResult = partialAudit.channelGroup("cg1").authKeys(Arrays.asList("key1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubscribeKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("rXy69MNT1vceNs3Ob6HnjShUAzCV5x4OumSG1lSPL6s%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setSubscribeKey(null); + PNAccessManagerAuditResult pnAccessManagerAuditResult = partialAudit.channelGroup("cg1").authKeys(Arrays.asList("key1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubscribeKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("rXy69MNT1vceNs3Ob6HnjShUAzCV5x4OumSG1lSPL6s%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setSubscribeKey(""); + PNAccessManagerAuditResult pnAccessManagerAuditResult = partialAudit.channelGroup("cg1").authKeys(Arrays.asList("key1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullPublishKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("rXy69MNT1vceNs3Ob6HnjShUAzCV5x4OumSG1lSPL6s%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setPublishKey(null); + PNAccessManagerAuditResult pnAccessManagerAuditResult = partialAudit.channelGroup("cg1").authKeys(Arrays.asList("key1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptyPublishKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("rXy69MNT1vceNs3Ob6HnjShUAzCV5x4OumSG1lSPL6s%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setPublishKey(""); + PNAccessManagerAuditResult pnAccessManagerAuditResult = partialAudit.channelGroup("cg1").authKeys(Arrays.asList("key1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testChannelAndChanneGroupNull() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/audit/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("rXy69MNT1vceNs3Ob6HnjShUAzCV5x4OumSG1lSPL6s%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerAuditResult pnAccessManagerAuditResult = partialAudit.authKeys(Arrays.asList("key1")).channel(null).channelGroup(null).sync(); + } + +} diff --git a/src/test/java/com/pubnub/api/endpoints/access/GrantEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/access/GrantEndpointTest.java new file mode 100644 index 000000000..86a9e6933 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/access/GrantEndpointTest.java @@ -0,0 +1,836 @@ +package com.pubnub.api.endpoints.access; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerGrantResult; +import com.pubnub.api.models.consumer.access_manager.PNAccessManagerKeyData; +import com.pubnub.api.endpoints.TestHarness; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.client.WireMock.matching; +import static org.junit.Assert.assertEquals; + +public class GrantEndpointTest extends TestHarness { + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + private Grant partialGrant; + private PubNub pubnub; + + @Before + public void beforeEach() throws IOException { + + pubnub = this.createPubNubInstance(8080); + partialGrant = pubnub.grant(); + + pubnub.getConfiguration().setSecretKey("secretKey"); + + } + + @Test + public void NoGroupsOneChannelOneKeyTest() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("HlyfXDFhdgNhKfBzGaouxh2T2SRimm4bVq_JVKLRPQI%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).sync(); + + assertEquals(1, result.getChannels().size()); + assertEquals(0, result.getChannelGroups().size()); + + assertEquals(1, result.getChannels().get("ch1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + + } + + @Test + public void NoGroupsOneChannelTwoKeyTest() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("signature", matching("sbcQS0RjU7uq0Q9YqN7HFJO26SlgVXPejhmVJJ2w5OU%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1", "key2")).channels(Arrays.asList("ch1")).sync(); + + assertEquals(1, result.getChannels().size()); + assertEquals(0, result.getChannelGroups().size()); + + assertEquals(2, result.getChannels().get("ch1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key2").getClass()); + } + + @Test + public void NoGroupsTwoChannelOneKeyTest() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1,ch2")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("pizC0huUiyQFdOnNDGxjU9xX6b9GFcslSm6bqfrimq4%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channels\":{\"ch1\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"ch2\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1", "ch2")).sync(); + + assertEquals(2, result.getChannels().size()); + assertEquals(0, result.getChannelGroups().size()); + + assertEquals(1, result.getChannels().get("ch1").size()); + assertEquals(1, result.getChannels().get("ch2").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch2").get("key1").getClass()); + } + + @Test + public void NoGroupsTwoChannelTwoKeyTest() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1,ch2")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("signature", matching("SRklo_IoAeNbvmp_xQV0kwL8vLz2vTEk14umB7v6K-w%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channels\":{\"ch1\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}},\"ch2\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1", "key2")).channels(Arrays.asList("ch1", "ch2")).sync(); + + assertEquals(2, result.getChannels().size()); + assertEquals(0, result.getChannelGroups().size()); + + assertEquals(2, result.getChannels().get("ch1").size()); + assertEquals(2, result.getChannels().get("ch2").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch2").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key2").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch2").get("key2").getClass()); + } + + @Test + public void OneGroupNoChannelOneKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("kqLDzzhvhUQ9Ri3j2xRFDvMYDbLQ2aM3erz52QL_IQ0%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel-groups\":\"cg1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channelGroups(Arrays.asList("cg1")).sync(); + + assertEquals(0, result.getChannels().size()); + assertEquals(1, result.getChannelGroups().size()); + + assertEquals(1, result.getChannelGroups().get("cg1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key1").getClass()); + + } + + @Test + public void OneGroupNoChannelTwoKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("signature", matching("ET_b6B8vPfdyvwM1eJLitDERcwykGhTkuQNeryH7q5o%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel-groups\":\"cg1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1", "key2")).channelGroups(Arrays.asList("cg1")).sync(); + + assertEquals(0, result.getChannels().size()); + assertEquals(1, result.getChannelGroups().size()); + + assertEquals(2, result.getChannelGroups().get("cg1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key2").getClass()); + + } + + @Test + public void OneGroupOneChannelOneKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("QqOQSjo6VEG4JUp6NSBjc4SQ5lqTPoPGeOgzuXF6fbE%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}},\"channel-groups\":\"cg1\"},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).channelGroups(Arrays.asList("cg1")).sync(); + + assertEquals(1, result.getChannels().size()); + assertEquals(1, result.getChannelGroups().size()); + + assertEquals(1, result.getChannelGroups().get("cg1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key1").getClass()); + + assertEquals(1, result.getChannels().get("ch1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + + } + + @Test + public void OneGroupOneChannelTwoKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("signature", matching("G3-cvSBeaQmwWabOOoHMxIH78MlvcOdGJS83z6cH7RY%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}},\"channel-groups\":\"cg1\"},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1", "key2")).channels(Arrays.asList("ch1")).channelGroups(Arrays.asList("cg1")).sync(); + + assertEquals(1, result.getChannels().size()); + assertEquals(1, result.getChannelGroups().size()); + + assertEquals(2, result.getChannelGroups().get("cg1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key2").getClass()); + + assertEquals(2, result.getChannels().get("ch1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key2").getClass()); + + } + + @Test + public void OneGroupTwoChannelOneKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1,ch2")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("LYQdmYjV0Gxbu_1uhey5wPyIJ2cZPTjp7Grc0oUwTl4%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channels\":{\"ch1\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"ch2\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}}},\"channel-groups\":\"cg1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}\n"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1", "ch2")).channelGroups(Arrays.asList("cg1")).sync(); + + assertEquals(2, result.getChannels().size()); + assertEquals(1, result.getChannelGroups().size()); + + assertEquals(1, result.getChannelGroups().get("cg1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key1").getClass()); + + assertEquals(1, result.getChannels().get("ch1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch2").get("key1").getClass()); + + } + + @Test + public void OneGroupTwoChannelTwoKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1,ch2")) + .withQueryParam("channel-group", matching("cg1")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("signature", matching("iMUJd9GHcLUeBpIgKD3JLUQ-B3t0XGa2D-IWrrV30_o%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channels\":{\"ch1\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}},\"ch2\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}}},\"channel-groups\":\"cg1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}\n"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1", "key2")).channels(Arrays.asList("ch1", "ch2")).channelGroups(Arrays.asList("cg1")).sync(); + + assertEquals(2, result.getChannels().size()); + assertEquals(1, result.getChannelGroups().size()); + + assertEquals(2, result.getChannelGroups().get("cg1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key2").getClass()); + + assertEquals(2, result.getChannels().get("ch1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key2").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch2").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch2").get("key2").getClass()); + } + + // + + @Test + public void TwoGroupNoChannelOneKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1,cg2")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("FRx_2Pes924avZU3ldKb7Ry1u86jHOztltxdlicWvyA%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel-groups\":{\"cg1\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"cg2\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}}}},\"service\":\"Access Manager\",\"status\":200}\n"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channelGroups(Arrays.asList("cg1", "cg2")).sync(); + + assertEquals(0, result.getChannels().size()); + assertEquals(2, result.getChannelGroups().size()); + + assertEquals(1, result.getChannelGroups().get("cg1").size()); + assertEquals(1, result.getChannelGroups().get("cg2").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg2").get("key1").getClass()); + + } + + @Test + public void TwoGroupNoChannelTwoKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel-group", matching("cg1,cg2")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("signature", matching("N5VsRLfm6PXduKXTApCEa95E99JcR9aVMecejEiUJkI%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel-groups\":{\"cg1\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}},\"cg2\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}}}},\"service\":\"Access Manager\",\"status\":200}\n"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1", "key2")).channelGroups(Arrays.asList("cg1", "cg2")).sync(); + + assertEquals(0, result.getChannels().size()); + assertEquals(2, result.getChannelGroups().size()); + + assertEquals(2, result.getChannelGroups().get("cg1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key2").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg2").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg2").get("key2").getClass()); + + } + + @Test + public void TwoGroupOneChannelOneKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("channel-group", matching("cg1,cg2")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("T3vn9bb7T6MG_KkST4i_80iYCAj0crgtJ-PUZVGI_Wg%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}},\"channel-groups\":{\"cg1\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"cg2\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}}}},\"service\":\"Access Manager\",\"status\":200}\n"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channelGroups(Arrays.asList("cg1", "cg2")).channels(Arrays.asList("ch1")).sync(); + + assertEquals(1, result.getChannels().size()); + assertEquals(2, result.getChannelGroups().size()); + + assertEquals(1, result.getChannelGroups().get("cg1").size()); + assertEquals(1, result.getChannelGroups().get("cg2").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg2").get("key1").getClass()); + + assertEquals(1, result.getChannels().get("ch1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + } + + @Test + public void TwoGroupOneChannelTwoKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("channel-group", matching("cg1,cg2")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("signature", matching("UzjgorbyDjwBxfu0Nr5V2VJ6J9614ihHiRwZ5yU20Ek%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}},\"channel-groups\":{\"cg1\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}},\"cg2\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}}}},\"service\":\"Access Manager\",\"status\":200}\n"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1", "key2")).channelGroups(Arrays.asList("cg1", "cg2")).channels(Arrays.asList("ch1")).sync(); + + assertEquals(1, result.getChannels().size()); + assertEquals(2, result.getChannelGroups().size()); + + assertEquals(2, result.getChannelGroups().get("cg1").size()); + assertEquals(2, result.getChannelGroups().get("cg2").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key2").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg2").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg2").get("key2").getClass()); + + assertEquals(2, result.getChannels().get("ch1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key2").getClass()); + + } + + @Test + public void TwoGroupTwoChannelOneKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1,ch2")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("7znRWub4WttnrofkghqBaxSE3XeQ0ZHtZU5QZB2ofR4%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channels\":{\"ch1\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"ch2\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}}},\"channel-groups\":{\"cg1\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"cg2\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}}}},\"service\":\"Access Manager\",\"status\":200}\n"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channelGroups(Arrays.asList("cg1", "cg2")).channels(Arrays.asList("ch1", "ch2")).sync(); + + assertEquals(2, result.getChannels().size()); + assertEquals(2, result.getChannelGroups().size()); + + assertEquals(1, result.getChannelGroups().get("cg1").size()); + assertEquals(1, result.getChannelGroups().get("cg2").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg2").get("key1").getClass()); + + assertEquals(1, result.getChannels().get("ch1").size()); + assertEquals(1, result.getChannels().get("ch2").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch2").get("key1").getClass()); + } + + @Test + public void TwoGroupTwoChannelTwoKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1,ch2")) + .withQueryParam("channel-group", matching("cg1,cg2")) + .withQueryParam("auth", matching("key1,key2")) + .withQueryParam("signature", matching("pX82pKukTC75_ZhHkcvUQXTuCYC7iuh_h2wmWWrO7hg%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"channel-group+auth\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channels\":{\"ch1\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}},\"ch2\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}}},\"channel-groups\":{\"cg1\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}},\"cg2\":{\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0},\"key2\":{\"r\":0,\"w\":0,\"m\":0}}}}},\"service\":\"Access Manager\",\"status\":200}\n"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1", "key2")).channelGroups(Arrays.asList("cg1", "cg2")).channels(Arrays.asList("ch1", "ch2")).sync(); + + assertEquals(2, result.getChannels().size()); + assertEquals(2, result.getChannelGroups().size()); + + assertEquals(2, result.getChannelGroups().get("cg1").size()); + assertEquals(2, result.getChannelGroups().get("cg2").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg1").get("key2").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg2").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannelGroups().get("cg2").get("key2").getClass()); + + assertEquals(2, result.getChannels().get("ch1").size()); + assertEquals(2, result.getChannels().get("ch2").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key2").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch2").get("key1").getClass()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch2").get("key2").getClass()); + + } + + @Test + public void NoGroupsOneChannelOneKeyTTLTest() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("2XtpqL0DBNHCh6cKeCqxfJeq4sa8AG4vZFXT1quaVnw%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .withQueryParam("ttl", matching("1334")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).ttl(1334).sync(); + + assertEquals(1, result.getChannels().size()); + assertEquals(0, result.getChannelGroups().size()); + + assertEquals(1, result.getChannels().get("ch1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + + } + + @Test + public void NoGroupsOneChannelOneReadKeyTest() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("KgI1t0Y50H6mIZ12re8TyaMDoEx63NMHJ260kjyge38%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("1")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).read(true).sync(); + + assertEquals(1, result.getChannels().size()); + assertEquals(0, result.getChannelGroups().size()); + + assertEquals(1, result.getChannels().get("ch1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + + } + + @Test + public void NoGroupsOneChannelOneWriteKeyTest() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("wQSMOVu0bvmyxt6F8_lc81dmsF_pz5f9VjOpe1FiB_w%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("1")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).write(true).sync(); + + assertEquals(1, result.getChannels().size()); + assertEquals(0, result.getChannelGroups().size()); + + assertEquals(1, result.getChannels().get("ch1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + + } + + @Test + public void NoGroupsOneChannelOneKeyManageTest() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("TUtUB_2F738wA1tuENkDUedkVuTwGbtGXqE1PekesHA%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("1")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).manage(true).sync(); + + assertEquals(1, result.getChannels().size()); + assertEquals(0, result.getChannelGroups().size()); + + assertEquals(1, result.getChannels().get("ch1").size()); + assertEquals(PNAccessManagerKeyData.class, result.getChannels().get("ch1").get("key1").getClass()); + + } + + + @org.junit.Test(expected=PubNubException.class) + public void NoGroupsOneChannelMissingKey() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("_znR67zw5cdCq3Cmn1QHUHtFolkquYARh_JYCeMb8ig%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("1")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.channels(Arrays.asList("ch1")).manage(true).sync(); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("HlyfXDFhdgNhKfBzGaouxh2T2SRimm4bVq_JVKLRPQI%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + } + + @org.junit.Test + public void testOperationTypeSuccessAsync() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("HlyfXDFhdgNhKfBzGaouxh2T2SRimm4bVq_JVKLRPQI%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).async(new PNCallback() { + @Override + public void onResponse(PNAccessManagerGrantResult result, PNStatus status) { + if (status != null && status.getOperation()== PNOperationType.PNAccessManagerGrant) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSecretKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("HlyfXDFhdgNhKfBzGaouxh2T2SRimm4bVq_JVKLRPQI%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setSecretKey(null); + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySecretKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("HlyfXDFhdgNhKfBzGaouxh2T2SRimm4bVq_JVKLRPQI%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setSecretKey(""); + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubscribeKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("HlyfXDFhdgNhKfBzGaouxh2T2SRimm4bVq_JVKLRPQI%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setSubscribeKey(null); + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubscribeKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("HlyfXDFhdgNhKfBzGaouxh2T2SRimm4bVq_JVKLRPQI%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setSubscribeKey(""); + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullPublishKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("HlyfXDFhdgNhKfBzGaouxh2T2SRimm4bVq_JVKLRPQI%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setPublishKey(null); + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptyPublishKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("HlyfXDFhdgNhKfBzGaouxh2T2SRimm4bVq_JVKLRPQI%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + pubnub.getConfiguration().setPublishKey(""); + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testMissingChannelsAndChannelGroup() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("HlyfXDFhdgNhKfBzGaouxh2T2SRimm4bVq_JVKLRPQI%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"payload\":{\"level\":\"user\",\"subscribe_key\":\"sub-c-82ab2196-b64f-11e5-8622-0619f8945a4f\",\"ttl\":1,\"channel\":\"ch1\",\"auths\":{\"key1\":{\"r\":0,\"w\":0,\"m\":0}}},\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullPayload() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/auth/grant/sub-key/mySubscribeKey")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("channel", matching("ch1")) + .withQueryParam("auth", matching("key1")) + .withQueryParam("signature", matching("HlyfXDFhdgNhKfBzGaouxh2T2SRimm4bVq_JVKLRPQI%3D%0A")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("timestamp", matching("1337")) + .withQueryParam("r", matching("0")) + .withQueryParam("w", matching("0")) + .withQueryParam("m", matching("0")) + .willReturn(aResponse().withBody("{\"message\":\"Success\",\"service\":\"Access Manager\",\"status\":200}"))); + + PNAccessManagerGrantResult result = partialGrant.authKeys(Arrays.asList("key1")).channels(Arrays.asList("ch1")).sync(); + } +} + diff --git a/src/test/java/com/pubnub/api/endpoints/channel_groups/AddChannelChannelGroupEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/channel_groups/AddChannelChannelGroupEndpointTest.java new file mode 100644 index 000000000..18ac15489 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/channel_groups/AddChannelChannelGroupEndpointTest.java @@ -0,0 +1,127 @@ +package com.pubnub.api.endpoints.channel_groups; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.endpoints.TestHarness; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAddChannelResult; +import org.junit.Before; +import org.junit.Rule; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class AddChannelChannelGroupEndpointTest extends TestHarness { + private AddChannelChannelGroup partialAddChannelChannelGroup; + private PubNub pubnub; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + partialAddChannelChannelGroup = pubnub.addChannelsToChannelGroup(); + } + + @org.junit.Test + public void testSyncSuccess() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {} , \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsAddChannelResult response = partialAddChannelChannelGroup.channelGroup("groupA").channels(Arrays.asList("ch1", "ch2")).sync(); + + assertNotNull(response); + } + + @org.junit.Test(expected = PubNubException.class) + public void testSyncGroupMissing() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {} , \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsAddChannelResult response = partialAddChannelChannelGroup.channels(Arrays.asList("ch1", "ch2")).sync(); + } + + @org.junit.Test(expected = PubNubException.class) + public void testSyncGroupIsEmpty() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {} , \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsAddChannelResult response = partialAddChannelChannelGroup.channelGroup("").channels(Arrays.asList("ch1", "ch2")).sync(); + } + + @org.junit.Test(expected = PubNubException.class) + public void testSyncChannelMissing() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {} , \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsAddChannelResult response = partialAddChannelChannelGroup.channelGroup("groupA").sync(); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {} , \"service\": \"ChannelGroups\"}"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + PNChannelGroupsAddChannelResult response = partialAddChannelChannelGroup.channelGroup("groupA").channels(Arrays.asList("ch1", "ch2")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test + public void testOperationTypeSuccessAsync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {} , \"service\": \"ChannelGroups\"}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + partialAddChannelChannelGroup.channelGroup("groupA").channels(Arrays.asList("ch1", "ch2")).async(new PNCallback() { + @Override + public void onResponse(PNChannelGroupsAddChannelResult result, PNStatus status) { + if (status != null && status.getOperation()== PNOperationType.PNAddChannelsToGroupOperation) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + @org.junit.Test + public void testErrorBodyForbiden() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withStatus(403).withBody("{\"status\": 403, \"message\": \"OK\", \"payload\": {} , \"service\": \"ChannelGroups\"}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + partialAddChannelChannelGroup.channelGroup("groupA").channels(Arrays.asList("ch1", "ch2")).async(new PNCallback() { + @Override + public void onResponse(PNChannelGroupsAddChannelResult result, PNStatus status) { + if (status != null && status.getOperation()== PNOperationType.PNAddChannelsToGroupOperation) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(15, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + +} diff --git a/src/test/java/com/pubnub/api/endpoints/channel_groups/AllChannelsChannelGroupEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/channel_groups/AllChannelsChannelGroupEndpointTest.java new file mode 100644 index 000000000..6bbc04d60 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/channel_groups/AllChannelsChannelGroupEndpointTest.java @@ -0,0 +1,108 @@ +package com.pubnub.api.endpoints.channel_groups; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAllChannelsResult; +import com.pubnub.api.endpoints.TestHarness; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsListAllResult; +import org.junit.Before; +import org.junit.Rule; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +public class AllChannelsChannelGroupEndpointTest extends TestHarness { + private AllChannelsChannelGroup partialAllChannelsChannelGroup; + private PubNub pubnub; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + partialAllChannelsChannelGroup = pubnub.listChannelsForChannelGroup(); + } + + @org.junit.Test + public void testSyncSuccess() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsAllChannelsResult response = partialAllChannelsChannelGroup.channelGroup("groupA").sync(); + assertThat(response.getChannels(), org.hamcrest.Matchers.contains("a", "b")); + } + + @org.junit.Test(expected=PubNubException.class) + public void testSyncMissingGroup() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsAllChannelsResult response = partialAllChannelsChannelGroup.sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testSyncEmptyGroup() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsAllChannelsResult response = partialAllChannelsChannelGroup.channelGroup("").sync(); + } + + @org.junit.Test(expected = PubNubException.class) + public void testNullPayload() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsAllChannelsResult response = partialAllChannelsChannelGroup.channelGroup("groupA").sync(); + assertThat(response.getChannels(), org.hamcrest.Matchers.contains("a", "b")); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"ChannelGroups\"}"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + PNChannelGroupsAllChannelsResult response = partialAllChannelsChannelGroup.channelGroup("groupA").sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test + public void testOperationTypeSuccessAsync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"ChannelGroups\"}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + partialAllChannelsChannelGroup.channelGroup("groupA").async(new PNCallback() { + @Override + public void onResponse(PNChannelGroupsAllChannelsResult result, PNStatus status) { + if (status != null && status.getOperation()==PNOperationType.PNChannelsForGroupOperation) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + +} diff --git a/src/test/java/com/pubnub/api/endpoints/channel_groups/DeleteChannelGroupEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/channel_groups/DeleteChannelGroupEndpointTest.java new file mode 100644 index 000000000..f4e5684e3 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/channel_groups/DeleteChannelGroupEndpointTest.java @@ -0,0 +1,98 @@ +package com.pubnub.api.endpoints.channel_groups; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.endpoints.TestHarness; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsDeleteGroupResult; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsListAllResult; +import org.junit.Before; +import org.junit.Rule; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class DeleteChannelGroupEndpointTest extends TestHarness { + private DeleteChannelGroup partialDeleteChannelGroup; + private PubNub pubnub; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + partialDeleteChannelGroup = pubnub.deleteChannelGroup(); + } + + @org.junit.Test + public void testSyncSuccess() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA/remove")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {}, \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsDeleteGroupResult response = partialDeleteChannelGroup.channelGroup("groupA").sync(); + assertNotNull(response); + } + + @org.junit.Test(expected = PubNubException.class) + public void testSyncMissingGroup() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA/remove")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {}, \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsDeleteGroupResult response = partialDeleteChannelGroup.sync(); + } + + @org.junit.Test(expected = PubNubException.class) + public void testSyncEmptyGroup() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA/remove")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {}, \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsDeleteGroupResult response = partialDeleteChannelGroup.channelGroup("").sync(); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA/remove")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {}, \"service\": \"ChannelGroups\"}"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + PNChannelGroupsDeleteGroupResult response = partialDeleteChannelGroup.channelGroup("groupA").sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test + public void testOperationTypeSuccessAsync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA/remove")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {}, \"service\": \"ChannelGroups\"}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + partialDeleteChannelGroup.channelGroup("groupA").async(new PNCallback() { + @Override + public void onResponse(PNChannelGroupsDeleteGroupResult result, PNStatus status) { + if (status != null && status.getOperation()==PNOperationType.PNRemoveGroupOperation) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + +} diff --git a/src/test/java/com/pubnub/api/endpoints/channel_groups/ListAllChannelGroupEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/channel_groups/ListAllChannelGroupEndpointTest.java new file mode 100644 index 000000000..3b791006e --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/channel_groups/ListAllChannelGroupEndpointTest.java @@ -0,0 +1,99 @@ +package com.pubnub.api.endpoints.channel_groups; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsListAllResult; +import com.pubnub.api.endpoints.TestHarness; +import org.junit.Before; +import org.junit.Rule; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +public class ListAllChannelGroupEndpointTest extends TestHarness { + private ListAllChannelGroup partialChannelGroup; + private PubNub pubnub; + + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + partialChannelGroup = pubnub.listAllChannelGroups(); + } + + @org.junit.Test + public void testSyncSuccess() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"groups\": [\"a\",\"b\"]}, \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsListAllResult response = partialChannelGroup.sync(); + assertThat(response.getGroups(), org.hamcrest.Matchers.contains("a", "b")); + } + + @org.junit.Test(expected = PubNubException.class) + public void testNullPayload() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsListAllResult response = partialChannelGroup.sync(); + assertThat(response.getGroups(), org.hamcrest.Matchers.contains("a", "b")); + } + + @org.junit.Test(expected = PubNubException.class) + public void testNullBody() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group")) + .willReturn(aResponse())); + + PNChannelGroupsListAllResult response = partialChannelGroup.sync(); + assertThat(response.getGroups(), org.hamcrest.Matchers.contains("a", "b")); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"groups\": [\"a\",\"b\"]}, \"service\": \"ChannelGroups\"}"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + PNChannelGroupsListAllResult response = partialChannelGroup.sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test + public void testOperationTypeSuccessAsync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"groups\": [\"a\",\"b\"]}, \"service\": \"ChannelGroups\"}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + partialChannelGroup.async(new PNCallback() { + @Override + public void onResponse(PNChannelGroupsListAllResult result, PNStatus status) { + if (status != null && status.getOperation()==PNOperationType.PNChannelGroupsOperation) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } +} diff --git a/src/test/java/com/pubnub/api/endpoints/channel_groups/RemoveChannelChannelGroupEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/channel_groups/RemoveChannelChannelGroupEndpointTest.java new file mode 100644 index 000000000..d9b894437 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/channel_groups/RemoveChannelChannelGroupEndpointTest.java @@ -0,0 +1,96 @@ +package com.pubnub.api.endpoints.channel_groups; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.endpoints.TestHarness; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsRemoveChannelResult; +import org.junit.Before; +import org.junit.Rule; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class RemoveChannelChannelGroupEndpointTest extends TestHarness { + private RemoveChannelChannelGroup partialRemoveChannelChannelGroup; + private PubNub pubnub; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + partialRemoveChannelChannelGroup = pubnub.removeChannelsFromChannelGroup(); + } + + @org.junit.Test + public void testSyncSuccess() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {}, \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsRemoveChannelResult response = partialRemoveChannelChannelGroup.channelGroup("groupA").channels(Arrays.asList("ch1", "ch2")).sync(); + assertNotNull(response); + } + + @org.junit.Test(expected = PubNubException.class) + public void testSyncMissinGroup() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {}, \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsRemoveChannelResult response = partialRemoveChannelChannelGroup.channels(Arrays.asList("ch1", "ch2")).sync(); + } + + @org.junit.Test(expected = PubNubException.class) + public void testSyncMissinChannel() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {}, \"service\": \"ChannelGroups\"}"))); + + PNChannelGroupsRemoveChannelResult response = partialRemoveChannelChannelGroup.channelGroup("groupA").sync(); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {}, \"service\": \"ChannelGroups\"}"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + PNChannelGroupsRemoveChannelResult response = partialRemoveChannelChannelGroup.channelGroup("groupA").channels(Arrays.asList("ch1", "ch2")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test + public void testOperationTypeSuccessAsync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/channel-registration/sub-key/mySubscribeKey/channel-group/groupA")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {}, \"service\": \"ChannelGroups\"}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + partialRemoveChannelChannelGroup.channelGroup("groupA").channels(Arrays.asList("ch1", "ch2")).async(new PNCallback() { + @Override + public void onResponse(PNChannelGroupsRemoveChannelResult result, PNStatus status) { + if (status != null && status.getOperation()== PNOperationType.PNRemoveChannelsFromGroupOperation) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } +} diff --git a/src/test/java/com/pubnub/api/endpoints/presence/GetStateEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/presence/GetStateEndpointTest.java new file mode 100644 index 000000000..6e6cf03c0 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/presence/GetStateEndpointTest.java @@ -0,0 +1,214 @@ +package com.pubnub.api.endpoints.presence; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.presence.PNGetStateResult; +import com.pubnub.api.endpoints.TestHarness; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; + + +public class GetStateEndpointTest extends TestHarness { + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + private PubNub pubnub; + private GetState partialGetState; + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + partialGetState = pubnub.getPresenceState(); + } + + @Test + public void testOneChannelSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\"}, \"service\": \"Presence\"}"))); + + + PNGetStateResult result = partialGetState.channels(Arrays.asList("testChannel")).uuid("sampleUUID").sync(); + Map ch1Data = (Map) result.getStateByUUID().get("testChannel"); + Assert.assertEquals(ch1Data.get("age"), 20); + Assert.assertEquals(ch1Data.get("status"), "online"); + } + + @Test + public void testOneChannelWithoutUUIDSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\"}, \"service\": \"Presence\"}"))); + + + PNGetStateResult result = partialGetState.channels(Arrays.asList("testChannel")).sync(); + Map ch1Data = (Map) result.getStateByUUID().get("testChannel"); + Assert.assertEquals(ch1Data.get("age"), 20); + Assert.assertEquals(ch1Data.get("status"), "online"); + } + + + @org.junit.Test(expected=PubNubException.class) + public void testFailedPayloadSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": \"age\" : 20, \"status\" : \"online\"}, \"service\": \"Presence\"}"))); + + partialGetState.channels(Arrays.asList("testChannel")).uuid("sampleUUID").sync(); + } + + @Test + public void testMultipleChannelSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1,ch2/uuid/sampleUUID")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"ch1\": { \"age\" : 20, \"status\" : \"online\"}, \"ch2\": { \"age\": 100, \"status\": \"offline\" } }, \"service\": \"Presence\"}"))); + + PNGetStateResult result = partialGetState.channels(Arrays.asList("ch1", "ch2")).uuid("sampleUUID").sync(); + Map ch1Data = (Map) result.getStateByUUID().get("ch1"); + Assert.assertEquals(ch1Data.get("age"), 20); + Assert.assertEquals(ch1Data.get("status"), "online"); + Map ch2Data = (Map) result.getStateByUUID().get("ch2"); + Assert.assertEquals(ch2Data.get("age"), 100); + Assert.assertEquals(ch2Data.get("status"), "offline"); + } + + @Test + public void testOneChannelGroupSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/uuid/sampleUUID")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"chcg1\": { \"age\" : 20, \"status\" : \"online\"}, \"chcg2\": { \"age\": 100, \"status\": \"offline\" } }, \"service\": \"Presence\"}"))); + + PNGetStateResult result = partialGetState.channelGroups(Arrays.asList("cg1")).uuid("sampleUUID").sync(); + Map ch1Data = (Map) result.getStateByUUID().get("chcg1"); + Assert.assertEquals(ch1Data.get("age"), 20); + Assert.assertEquals(ch1Data.get("status"), "online"); + Map ch2Data = (Map) result.getStateByUUID().get("chcg2"); + Assert.assertEquals(ch2Data.get("age"), 100); + Assert.assertEquals(ch2Data.get("status"), "offline"); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("cg1", requests.get(0).queryParameter("channel-group").firstValue()); + } + + @Test + public void testManyChannelGroupSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/uuid/sampleUUID")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"chcg1\": { \"age\" : 20, \"status\" : \"online\"}, \"chcg2\": { \"age\": 100, \"status\": \"offline\" } }, \"service\": \"Presence\"}"))); + + PNGetStateResult result = partialGetState.channelGroups(Arrays.asList("cg1", "cg2")).uuid("sampleUUID").sync(); + Map ch1Data = (Map) result.getStateByUUID().get("chcg1"); + Assert.assertEquals(ch1Data.get("age"), 20); + Assert.assertEquals(ch1Data.get("status"), "online"); + Map ch2Data = (Map) result.getStateByUUID().get("chcg2"); + Assert.assertEquals(ch2Data.get("age"), 100); + Assert.assertEquals(ch2Data.get("status"), "offline"); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("cg1,cg2", requests.get(0).queryParameter("channel-group").firstValue()); + } + + @Test + public void testCombinationSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/uuid/sampleUUID")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"chcg1\": { \"age\" : 20, \"status\" : \"online\"}, \"chcg2\": { \"age\": 100, \"status\": \"offline\" } }, \"service\": \"Presence\"}"))); + + PNGetStateResult result = partialGetState.channels(Arrays.asList("ch1")).channelGroups(Arrays.asList("cg1", "cg2")).uuid("sampleUUID").sync(); + Map ch1Data = (Map) result.getStateByUUID().get("chcg1"); + Assert.assertEquals(ch1Data.get("age"), 20); + Assert.assertEquals(ch1Data.get("status"), "online"); + Map ch2Data = (Map) result.getStateByUUID().get("chcg2"); + Assert.assertEquals(ch2Data.get("age"), 100); + Assert.assertEquals(ch2Data.get("status"), "offline"); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("cg1,cg2", requests.get(0).queryParameter("channel-group").firstValue()); + + } + + @org.junit.Test(expected=PubNubException.class) + public void testMissingChannelAndGroupSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\"}, \"service\": \"Presence\"}"))); + PNGetStateResult result = partialGetState.uuid("sampleUUID").sync(); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\"}, \"service\": \"Presence\"}"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + PNGetStateResult result = partialGetState.channels(Arrays.asList("testChannel")).uuid("sampleUUID").sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test + public void testOperationTypeSuccessAsync() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\"}, \"service\": \"Presence\"}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + partialGetState.channels(Arrays.asList("testChannel")).uuid("sampleUUID").async(new PNCallback() { + @Override + public void onResponse(PNGetStateResult result, PNStatus status) { + if (status != null && status.getOperation()== PNOperationType.PNGetState) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\"}, \"service\": \"Presence\"}"))); + + pubnub.getConfiguration().setSubscribeKey(null); + PNGetStateResult result = partialGetState.channels(Arrays.asList("testChannel")).uuid("sampleUUID").sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/sampleUUID")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\"}, \"service\": \"Presence\"}"))); + + pubnub.getConfiguration().setSubscribeKey(""); + PNGetStateResult result = partialGetState.channels(Arrays.asList("testChannel")).uuid("sampleUUID").sync(); + } +} diff --git a/src/test/java/com/pubnub/api/endpoints/presence/HereNowEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/presence/HereNowEndpointTest.java new file mode 100644 index 000000000..5373a116b --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/presence/HereNowEndpointTest.java @@ -0,0 +1,263 @@ +package com.pubnub.api.endpoints.presence; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.presence.PNHereNowResult; +import com.pubnub.api.endpoints.TestHarness; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; + +public class HereNowEndpointTest extends TestHarness { + private PubNub pubnub; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + private HereNow partialHereNow; + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + partialHereNow = pubnub.hereNow(); + } + + @Test + public void testMultipleChannelStateSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/ch1,ch2")) + .willReturn(aResponse().withBody("{\"status\":200,\"message\":\"OK\",\"payload\":{\"total_occupancy\":3,\"total_channels\":2,\"channels\":{\"ch1\":{\"occupancy\":1,\"uuids\":[{\"uuid\":\"user1\",\"state\":{\"age\":10}}]},\"ch2\":{\"occupancy\":2,\"uuids\":[{\"uuid\":\"user1\",\"state\":{\"age\":10}},{\"uuid\":\"user3\",\"state\":{\"age\":30}}]}}},\"service\":\"Presence\"}"))); + + PNHereNowResult response = partialHereNow.channels(Arrays.asList("ch1", "ch2")).includeState(true).sync(); + + Assert.assertEquals(response.getTotalChannels(), 2); + Assert.assertEquals(response.getTotalOccupancy(), 3); + + Assert.assertEquals(response.getChannels().get("ch1").getChannelName(), "ch1"); + Assert.assertEquals(response.getChannels().get("ch1").getOccupancy(), 1); + Assert.assertEquals(response.getChannels().get("ch1").getOccupants().size(), 1); + Assert.assertEquals(response.getChannels().get("ch1").getOccupants().get(0).getUuid(), "user1"); + Assert.assertEquals(response.getChannels().get("ch1").getOccupants().get(0).getState().toString(), "{age=10}"); + + Assert.assertEquals(response.getChannels().get("ch2").getChannelName(), "ch2"); + Assert.assertEquals(response.getChannels().get("ch2").getOccupancy(), 2); + Assert.assertEquals(response.getChannels().get("ch2").getOccupants().size(), 2); + Assert.assertEquals(response.getChannels().get("ch2").getOccupants().get(0).getUuid(), "user1"); + Assert.assertEquals(response.getChannels().get("ch2").getOccupants().get(0).getState().toString(), "{age=10}"); + Assert.assertEquals(response.getChannels().get("ch2").getOccupants().get(1).getUuid(), "user3"); + Assert.assertEquals(response.getChannels().get("ch2").getOccupants().get(1).getState().toString(), "{age=30}"); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("1", requests.get(0).queryParameter("state").firstValue()); + } + + @Test + public void testMultipleChannelSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/ch1,ch2")) + .willReturn(aResponse().withBody("{\"status\":200,\"message\":\"OK\",\"payload\":{\"total_occupancy\":3,\"total_channels\":2,\"channels\":{\"ch1\":{\"occupancy\":1,\"uuids\":[{\"uuid\":\"user1\"}]},\"ch2\":{\"occupancy\":2,\"uuids\":[{\"uuid\":\"user1\"},{\"uuid\":\"user3\"}]}}},\"service\":\"Presence\"}"))); + + PNHereNowResult response = partialHereNow.channels(Arrays.asList("ch1", "ch2")).includeState(true).sync(); + + Assert.assertEquals(response.getTotalChannels(), 2); + Assert.assertEquals(response.getTotalOccupancy(), 3); + + Assert.assertEquals(response.getChannels().get("ch1").getChannelName(), "ch1"); + Assert.assertEquals(response.getChannels().get("ch1").getOccupancy(), 1); + Assert.assertEquals(response.getChannels().get("ch1").getOccupants().size(), 1); + Assert.assertEquals(response.getChannels().get("ch1").getOccupants().get(0).getUuid(), "user1"); + Assert.assertEquals(response.getChannels().get("ch1").getOccupants().get(0).getState(), null); + + Assert.assertEquals(response.getChannels().get("ch2").getChannelName(), "ch2"); + Assert.assertEquals(response.getChannels().get("ch2").getOccupancy(), 2); + Assert.assertEquals(response.getChannels().get("ch2").getOccupants().size(), 2); + Assert.assertEquals(response.getChannels().get("ch2").getOccupants().get(0).getUuid(), "user1"); + Assert.assertEquals(response.getChannels().get("ch2").getOccupants().get(0).getState(), null); + Assert.assertEquals(response.getChannels().get("ch2").getOccupants().get(1).getUuid(), "user3"); + Assert.assertEquals(response.getChannels().get("ch2").getOccupants().get(1).getState(), null); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("1", requests.get(0).queryParameter("state").firstValue()); + } + + @Test + public void testMultipleChannelWithoutStateSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/game1,game2")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": {\"game1\": {\"uuids\": [\"a3ffd012-a3b9-478c-8705-64089f24d71e\"], \"occupancy\": 1}}, \"total_channels\": 1, \"total_occupancy\": 1}, \"service\": \"Presence\"}"))); + + PNHereNowResult response = partialHereNow.channels(Arrays.asList("game1", "game2")).includeState(false).sync(); + + Assert.assertEquals(response.getTotalChannels(), 1); + Assert.assertEquals(response.getTotalOccupancy(), 1); + + Assert.assertEquals(response.getChannels().get("game1").getChannelName(), "game1"); + Assert.assertEquals(response.getChannels().get("game1").getOccupancy(), 1); + Assert.assertEquals(response.getChannels().get("game1").getOccupants().size(), 1); + Assert.assertEquals(response.getChannels().get("game1").getOccupants().get(0).getUuid(), "a3ffd012-a3b9-478c-8705-64089f24d71e"); + Assert.assertEquals(response.getChannels().get("game1").getOccupants().get(0).getState(), null); + + } + + @Test + public void testMultipleChannelWithoutStateUUIDsSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/game1,game2")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": {\"game1\": {\"occupancy\": 1}}, \"total_channels\": 1, \"total_occupancy\": 1}, \"service\": \"Presence\"}"))); + + PNHereNowResult response = partialHereNow.channels(Arrays.asList("game1", "game2")).includeState(false).includeUUIDs(false).sync(); + + Assert.assertEquals(response.getTotalChannels(), 1); + Assert.assertEquals(response.getTotalOccupancy(), 1); + + Assert.assertEquals(response.getChannels().get("game1").getChannelName(), "game1"); + Assert.assertEquals(response.getChannels().get("game1").getOccupancy(), 1); + Assert.assertEquals(response.getChannels().get("game1").getOccupants(), null); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("1", requests.get(0).queryParameter("disable_uuids").firstValue()); + } + + @Test + public void testSingularChannelWithoutStateUUIDsSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/game1")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"occupancy\": 3}"))); + + PNHereNowResult response = partialHereNow.channels(Arrays.asList("game1")).includeState(false).includeUUIDs(false).sync(); + + Assert.assertEquals(response.getTotalChannels(), 1); + Assert.assertEquals(response.getTotalOccupancy(), 3); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("1", requests.get(0).queryParameter("disable_uuids").firstValue()); + + } + + @Test + public void testSingularChannelWithoutStateSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/game1")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"uuids\": [\"a3ffd012-a3b9-478c-8705-64089f24d71e\"], \"occupancy\": 1}"))); + + PNHereNowResult response = partialHereNow.channels(Arrays.asList("game1")).includeState(false).sync(); + + Assert.assertEquals(response.getTotalChannels(), 1); + Assert.assertEquals(response.getTotalOccupancy(), 1); + Assert.assertEquals(response.getChannels().size(), 1); + Assert.assertEquals(response.getChannels().get("game1").getOccupancy(), 1); + Assert.assertEquals(response.getChannels().get("game1").getOccupants().size(), 1); + Assert.assertEquals(response.getChannels().get("game1").getOccupants().get(0).getUuid(), "a3ffd012-a3b9-478c-8705-64089f24d71e"); + Assert.assertEquals(response.getChannels().get("game1").getOccupants().get(0).getState(), null); + + } + + @Test + public void testSingularChannelSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/game1")) + .willReturn(aResponse().withBody("{\"status\":200,\"message\":\"OK\",\"service\":\"Presence\",\"uuids\":[{\"uuid\":\"a3ffd012-a3b9-478c-8705-64089f24d71e\",\"state\":{\"age\":10}}],\"occupancy\":1}"))); + + PNHereNowResult response = partialHereNow.channels(Arrays.asList("game1")).includeState(true).sync(); + + Assert.assertEquals(response.getTotalChannels(), 1); + Assert.assertEquals(response.getTotalOccupancy(), 1); + Assert.assertEquals(response.getChannels().size(), 1); + Assert.assertEquals(response.getChannels().get("game1").getOccupancy(), 1); + Assert.assertEquals(response.getChannels().get("game1").getOccupants().size(), 1); + Assert.assertEquals(response.getChannels().get("game1").getOccupants().get(0).getUuid(), "a3ffd012-a3b9-478c-8705-64089f24d71e"); + Assert.assertEquals(response.getChannels().get("game1").getOccupants().get(0).getState().toString(), "{age=10}"); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("1", requests.get(0).queryParameter("state").firstValue()); + } + + @Test + public void testSingularChannelAndGroupSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/game1")) + .willReturn(aResponse().withBody("{\"status\":200,\"message\":\"OK\",\"payload\":{\"channels\":{}, \"total_channels\":0, \"total_occupancy\":0},\"service\":\"Presence\"}"))); + + PNHereNowResult response = partialHereNow.channelGroups(Arrays.asList("grp1")).channels(Arrays.asList("game1")).includeState(true).sync(); + + Assert.assertEquals(response.getTotalOccupancy(), 0); + } + + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/ch1,ch2")) + .willReturn(aResponse().withBody("{\"status\":200,\"message\":\"OK\",\"payload\":{\"total_occupancy\":3,\"total_channels\":2,\"channels\":{\"ch1\":{\"occupancy\":1,\"uuids\":[{\"uuid\":\"user1\",\"state\":{\"age\":10}}]},\"ch2\":{\"occupancy\":2,\"uuids\":[{\"uuid\":\"user1\",\"state\":{\"age\":10}},{\"uuid\":\"user3\",\"state\":{\"age\":30}}]}}},\"service\":\"Presence\"}"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + PNHereNowResult response = partialHereNow.channels(Arrays.asList("ch1", "ch2")).includeState(true).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test + public void testOperationTypeSuccessAsync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/ch1,ch2")) + .willReturn(aResponse().withBody("{\"status\":200,\"message\":\"OK\",\"payload\":{\"total_occupancy\":3,\"total_channels\":2,\"channels\":{\"ch1\":{\"occupancy\":1,\"uuids\":[{\"uuid\":\"user1\",\"state\":{\"age\":10}}]},\"ch2\":{\"occupancy\":2,\"uuids\":[{\"uuid\":\"user1\",\"state\":{\"age\":10}},{\"uuid\":\"user3\",\"state\":{\"age\":30}}]}}},\"service\":\"Presence\"}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + partialHereNow.async(new PNCallback() { + @Override + public void onResponse(PNHereNowResult result, PNStatus status) { + if (status != null && status.getOperation()== PNOperationType.PNHereNowOperation) { + atomic.incrementAndGet(); + } + + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/ch1,ch2")) + .willReturn(aResponse().withBody("{\"status\":200,\"message\":\"OK\",\"payload\":{\"total_occupancy\":3,\"total_channels\":2,\"channels\":{\"ch1\":{\"occupancy\":1,\"uuids\":[{\"uuid\":\"user1\",\"state\":{\"age\":10}}]},\"ch2\":{\"occupancy\":2,\"uuids\":[{\"uuid\":\"user1\",\"state\":{\"age\":10}},{\"uuid\":\"user3\",\"state\":{\"age\":30}}]}}},\"service\":\"Presence\"}"))); + + pubnub.getConfiguration().setSubscribeKey(null); + PNHereNowResult response = partialHereNow.channels(Arrays.asList("ch1", "ch2")).includeState(true).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub_key/mySubscribeKey/channel/ch1,ch2")) + .willReturn(aResponse().withBody("{\"status\":200,\"message\":\"OK\",\"payload\":{\"total_occupancy\":3,\"total_channels\":2,\"channels\":{\"ch1\":{\"occupancy\":1,\"uuids\":[{\"uuid\":\"user1\",\"state\":{\"age\":10}}]},\"ch2\":{\"occupancy\":2,\"uuids\":[{\"uuid\":\"user1\",\"state\":{\"age\":10}},{\"uuid\":\"user3\",\"state\":{\"age\":30}}]}}},\"service\":\"Presence\"}"))); + + pubnub.getConfiguration().setSubscribeKey(""); + PNHereNowResult response = partialHereNow.channels(Arrays.asList("ch1", "ch2")).includeState(true).sync(); + } + +} diff --git a/src/test/java/com/pubnub/api/endpoints/presence/LeaveTest.java b/src/test/java/com/pubnub/api/endpoints/presence/LeaveTest.java new file mode 100644 index 000000000..8a937836e --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/presence/LeaveTest.java @@ -0,0 +1,171 @@ +package com.pubnub.api.endpoints.presence; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.endpoints.TestHarness; +import com.pubnub.api.managers.RetrofitManager; +import com.pubnub.api.models.consumer.PNStatus; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; + + +public class LeaveTest extends TestHarness { + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + private Leave instance; + private PubNub pubnub; + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + RetrofitManager retrofitManager = new RetrofitManager(pubnub); + instance = new Leave(pubnub, retrofitManager.getTransactionInstance()); + } + + @Test + public void subscribeChannelSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + instance.channels(Arrays.asList("coolChannel")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + + } + + @Test + public void subscribeChannelsSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel,coolChannel2/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + instance.channels(Arrays.asList("coolChannel", "coolChannel2")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + } + + + @Test + public void subscribeChannelsWithGroupSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel,coolChannel2/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + instance.channels(Arrays.asList("coolChannel", "coolChannel2")).channelGroups(Arrays.asList("cg1")) .sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("cg1", requests.get(0).queryParameter("channel-group").firstValue()); + } + + @Test + public void subscribeChannelsWithGroupASync() throws PubNubException { + + final AtomicBoolean statusArrived = new AtomicBoolean(); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel,coolChannel2/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + instance.channels(Arrays.asList("coolChannel", "coolChannel2")).channelGroups(Arrays.asList("cg1")) .async(new PNCallback() { + @Override + public void onResponse(Boolean result, PNStatus status) { + assertEquals(status.getAffectedChannels().get(0), "coolChannel"); + assertEquals(status.getAffectedChannels().get(1), "coolChannel2"); + assertEquals(status.getAffectedChannelGroups().get(0), "cg1"); + statusArrived.set(true); + } + }); + + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(statusArrived, org.hamcrest.core.IsEqual.equalTo(true)); + } + + @Test + public void subscribeGroupsSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + instance.channelGroups(Arrays.asList("cg1", "cg2")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("cg1,cg2", requests.get(0).queryParameter("channel-group").firstValue()); + } + + @Test + public void subscribeGroupSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + instance.channelGroups(Arrays.asList("cg1")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("cg1", requests.get(0).queryParameter("channel-group").firstValue()); + } + + @org.junit.Test(expected=PubNubException.class) + public void testMissingChannelAndGroupSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + instance.sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + pubnub.getConfiguration().setSubscribeKey(null); + instance.sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + pubnub.getConfiguration().setSubscribeKey(""); + instance.sync(); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/coolChannel/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + instance.channels(Arrays.asList("coolChannel")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + +} diff --git a/src/test/java/com/pubnub/api/endpoints/presence/SetStateEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/presence/SetStateEndpointTest.java new file mode 100644 index 000000000..6344aeb8a --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/presence/SetStateEndpointTest.java @@ -0,0 +1,288 @@ +package com.pubnub.api.endpoints.presence; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.models.consumer.presence.PNSetStateResult; +import com.pubnub.api.endpoints.TestHarness; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; + + +public class SetStateEndpointTest extends TestHarness { + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + private SetState partialSetState; + private PubNub pubnub; + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + + partialSetState = pubnub.setPresenceState(); + } + + @Test + public void applyStateForChannelSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("state", matching("%7B%22age%22%3A20%7D")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}"))); + + Map myState = new HashMap<>(); + myState.put("age", 20); + + PNSetStateResult result = partialSetState.channels(Arrays.asList("testChannel")).state(myState).sync(); + assertEquals(result.getState().get("age"), 20); + assertEquals(result.getState().get("status"), "online"); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + } + + @Test + public void applyStateForSomebodyElseChannelSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/someoneElseUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("state", matching("%7B%22age%22%3A20%7D")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}"))); + + Map myState = new HashMap<>(); + myState.put("age", 20); + + PNSetStateResult result = partialSetState.channels(Arrays.asList("testChannel")).state(myState).uuid("someoneElseUUID").sync(); + assertEquals(result.getState().get("age"), 20); + assertEquals(result.getState().get("status"), "online"); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + } + + @Test + public void applyStateForChannelsSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel,testChannel2/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("state", matching("%7B%22age%22%3A20%7D")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}"))); + + Map myState = new HashMap<>(); + myState.put("age", 20); + + PNSetStateResult result = partialSetState.channels(Arrays.asList("testChannel", "testChannel2")).state(myState).sync(); + assertEquals(result.getState().get("age"), 20); + assertEquals(result.getState().get("status"), "online"); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + + } + + @Test + public void applyStateForChannelGroupSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("state", matching("%7B%22age%22%3A20%7D")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}"))); + + Map myState = new HashMap<>(); + myState.put("age", 20); + + PNSetStateResult result = partialSetState.channelGroups(Arrays.asList("cg1")).state(myState).sync(); + + assertEquals(result.getState().get("age"), 20); + assertEquals(result.getState().get("status"), "online"); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + + } + + @Test + public void applyStateForChannelGroupsSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/,/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("state", matching("%7B%22age%22%3A20%7D")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}"))); + + Map myState = new HashMap<>(); + myState.put("age", 20); + + PNSetStateResult result = partialSetState.channelGroups(Arrays.asList("cg1", "cg2")).state(myState).sync(); + + assertEquals(result.getState().get("age"), 20); + assertEquals(result.getState().get("status"), "online"); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("cg1,cg2", requests.get(0).queryParameter("channel-group").firstValue()); + + } + + @Test + public void applyStateForMixSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("state", matching("%7B%22age%22%3A20%7D")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}"))); + + Map myState = new HashMap<>(); + myState.put("age", 20); + + PNSetStateResult result = partialSetState.channels(Arrays.asList("ch1")).channelGroups(Arrays.asList("cg1", "cg2")).state(myState).sync(); + + assertEquals(result.getState().get("age"), 20); + assertEquals(result.getState().get("status"), "online"); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + + } + + @org.junit.Test(expected = PubNubException.class) + public void applyNon200Sync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("state", matching("%7B%22status%22%3A%22oneline%22%2C%22age%22%3A20%7D")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}").withStatus(400))); + + + Map myState = new HashMap<>(); + myState.put("age", 20); + + PNSetStateResult result = partialSetState.channels(Arrays.asList("ch1")).channelGroups(Arrays.asList("cg1", "cg2")).state(myState).sync(); + + } + + @org.junit.Test(expected = PubNubException.class) + public void MissingStateSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}"))); + + PNSetStateResult result = partialSetState.channels(Arrays.asList("testChannel")).sync(); + + } + + @org.junit.Test(expected = PubNubException.class) + public void InvalidStateSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}"))); + + PNSetStateResult result = partialSetState.channels(Arrays.asList("testChannel")).state(new Object()).sync(); + + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("state", matching("%7B%22age%22%3A20%7D")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}"))); + + Map myState = new HashMap<>(); + myState.put("age", 20); + + pubnub.getConfiguration().setAuthKey("myKey"); + PNSetStateResult result = partialSetState.channels(Arrays.asList("testChannel")).state(myState).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("state", matching("%7B%22age%22%3A20%7D")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}"))); + + Map myState = new HashMap<>(); + myState.put("age", 20); + + pubnub.getConfiguration().setSubscribeKey(null); + PNSetStateResult result = partialSetState.channels(Arrays.asList("testChannel")).state(myState).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("state", matching("%7B%22age%22%3A20%7D")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}"))); + + Map myState = new HashMap<>(); + myState.put("age", 20); + + pubnub.getConfiguration().setSubscribeKey(""); + PNSetStateResult result = partialSetState.channels(Arrays.asList("testChannel")).state(myState).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testChannelAndGroupMissingSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("state", matching("%7B%22age%22%3A20%7D")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"payload\": { \"age\" : 20, \"status\" : \"online\" }, \"service\": \"Presence\"}"))); + + Map myState = new HashMap<>(); + myState.put("age", 20); + + PNSetStateResult result = partialSetState.state(myState).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullPayloadSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/testChannel/uuid/myUUID/data")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("state", matching("%7B%22age%22%3A20%7D")) + .willReturn(aResponse().withBody("{ \"status\": 200, \"message\": \"OK\", \"service\": \"Presence\"}"))); + + Map myState = new HashMap<>(); + myState.put("age", 20); + + PNSetStateResult result = partialSetState.channels(Arrays.asList("testChannel")).state(myState).sync(); + } + +} diff --git a/src/test/java/com/pubnub/api/endpoints/presence/WhereNowEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/presence/WhereNowEndpointTest.java new file mode 100644 index 000000000..8a3ba4919 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/presence/WhereNowEndpointTest.java @@ -0,0 +1,212 @@ +package com.pubnub.api.endpoints.presence; + + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.callbacks.WhereNowCallback; +import com.pubnub.api.PubNubException; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.presence.PNWhereNowResult; +import com.pubnub.api.endpoints.TestHarness; +import org.junit.Before; +import org.junit.Rule; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +public class WhereNowEndpointTest extends TestHarness { + private PubNub pubnub; + private WhereNow partialWhereNow; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + partialWhereNow = pubnub.whereNow(); + } + + @org.junit.Test + public void testSyncSuccess() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"Presence\"}"))); + + PNWhereNowResult response = partialWhereNow.sync(); + assertThat(response.getChannels(), org.hamcrest.Matchers.contains("a", "b")); + } + + @org.junit.Test + public void testSyncSuccessCustomUUID() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/customUUID")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"Presence\"}"))); + + PNWhereNowResult response = partialWhereNow.uuid("customUUID").sync(); + assertThat(response.getChannels(), org.hamcrest.Matchers.contains("a", "b")); + } + + @org.junit.Test(expected=PubNubException.class) + public void testSyncBrokenWithString() throws IOException, PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [zimp]}, \"service\": \"Presence\"}"))); + + partialWhereNow.sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testSyncBrokenWithoutJSON() throws IOException, PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": zimp}, \"service\": \"Presence\"}"))); + + partialWhereNow.sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testSyncBrokenWithout200() throws IOException, PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn(aResponse() + .withStatus(404) + .withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"Presence\"}"))); + + partialWhereNow.sync(); + } + + @org.junit.Test + public void testAsyncSuccess() throws IOException, PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"Presence\"}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + partialWhereNow.async(new WhereNowCallback(){ + + @Override + public void onResponse(PNWhereNowResult result, PNStatus status) { + assertThat(result.getChannels(), org.hamcrest.Matchers.contains("a", "b")); + atomic.incrementAndGet(); + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + @org.junit.Test + public void testAsyncBrokenWithString() throws IOException, PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [zimp]}, \"service\": \"Presence\"}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + partialWhereNow.async(new WhereNowCallback(){ + + @Override + public void onResponse(PNWhereNowResult result, PNStatus status) { + atomic.incrementAndGet(); + } + + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + + } + + @org.junit.Test + public void testAsyncBrokenWithoutJSON() throws IOException, PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": zimp}, \"service\": \"Presence\"}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + partialWhereNow.async(new WhereNowCallback(){ + + @Override + public void onResponse(PNWhereNowResult result, PNStatus status) { + atomic.incrementAndGet(); + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + + } + + @org.junit.Test + public void testAsyncBrokenWithout200() throws IOException, PubNubException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn(aResponse() + .withStatus(400) + .withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"Presence\"}"))); + + final AtomicInteger atomic = new AtomicInteger(0); + partialWhereNow.async(new WhereNowCallback(){ + + @Override + public void onResponse(PNWhereNowResult result, PNStatus status) { + atomic.incrementAndGet(); + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + + } + + @org.junit.Test + public void testIsAuthRequiredSuccessSync() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"Presence\"}"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + PNWhereNowResult response = partialWhereNow.sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"Presence\"}"))); + + pubnub.getConfiguration().setSubscribeKey(null); + PNWhereNowResult response = partialWhereNow.sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"payload\": {\"channels\": [\"a\",\"b\"]}, \"service\": \"Presence\"}"))); + + pubnub.getConfiguration().setSubscribeKey(""); + PNWhereNowResult response = partialWhereNow.sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullPayloadSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/uuid/myUUID")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\"}"))); + + PNWhereNowResult response = partialWhereNow.sync(); + } + +} diff --git a/src/test/java/com/pubnub/api/endpoints/pubsub/PublishTest.java b/src/test/java/com/pubnub/api/endpoints/pubsub/PublishTest.java new file mode 100644 index 000000000..250f8caa9 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/pubsub/PublishTest.java @@ -0,0 +1,402 @@ +package com.pubnub.api.endpoints.pubsub; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubError; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.endpoints.TestHarness; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.models.consumer.PNPublishResult; +import com.pubnub.api.models.consumer.PNStatus; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class PublishTest extends TestHarness { + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + private PubNub pubnub; + private Publish instance; + + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + instance = pubnub.publish(); + } + + @Test + public void testFireSuccessSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + pubnub.fire().channel("coolChannel").message("hi").sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + assertEquals("true", requests.get(0).queryParameter("norep").firstValue()); + assertEquals("0", requests.get(0).queryParameter("store").firstValue()); + } + + @Test + public void testNoRepSuccessSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message("hi").replicate(false).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + assertEquals("true", requests.get(0).queryParameter("norep").firstValue()); + } + + @Test + public void testRepDefaultSuccessSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hirep%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message("hirep").sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + assertNull(requests.get(0).queryParameter("norep")); + } + + @Test + public void testSuccessSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message("hi").sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + } + + @Test + public void testSuccessSequenceSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message("hi").sync(); + instance.channel("coolChannel").message("hi").sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(2, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + assertEquals("1", requests.get(0).queryParameter("seqn").firstValue()); + assertEquals("2", requests.get(1).queryParameter("seqn").firstValue()); + + + } + + @Test + public void testSuccessPostSync() throws PubNubException, InterruptedException { + stubFor(post(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").usePOST(true).message(Arrays.asList("m1", "m2")).sync(); + + List requests = findAll(postRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + assertEquals("[\"m1\",\"m2\"]", new String(requests.get(0).getBody())); + } + + @Test + public void testSuccessStoreFalseSync() throws PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message("hi").shouldStore(false).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("0", requests.get(0).queryParameter("store").firstValue()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + } + + @Test + public void testSuccessStoreTrueSync() throws PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message("hi").shouldStore(true).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("1", requests.get(0).queryParameter("store").firstValue()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + } + + @Test + public void testSuccessMetaSync() throws PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("meta", matching("%5B%22m1%22%2C%22m2%22%5D")) + .withQueryParam("store", matching("0")) + .withQueryParam("seqn", matching("1")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message("hi").meta(Arrays.asList("m1", "m2")).shouldStore(false).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + } + + @Test + public void testSuccessAuthKeySync() throws PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + pubnub.getConfiguration().setAuthKey("authKey"); + instance.channel("coolChannel").message("hi").sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("authKey", requests.get(0).queryParameter("auth").firstValue()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + + } + + @Test + public void testSuccessIntSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/10")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message(10).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + + } + + @Test + public void testSuccessArraySync() throws PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%5B%22a%22%2C%22b%22%2C%22c%22%5D?")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message(Arrays.asList("a", "b", "c")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + + } + + @Test + public void testSuccessArrayEncryptedSync() throws PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22HFP7V6bDwBLrwc1t8Rnrog%3D%3D%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + pubnub.getConfiguration().setCipherKey("testCipher"); + instance.channel("coolChannel").message(Arrays.asList("m1", "m2")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + + } + + @Test + public void testSuccessPostEncryptedSync() throws PubNubException, InterruptedException { + stubFor(post(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + pubnub.getConfiguration().setCipherKey("testCipher"); + + instance.channel("coolChannel").usePOST(true).message(Arrays.asList("m1", "m2")).sync(); + + List requests = findAll(postRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + assertEquals("\"HFP7V6bDwBLrwc1t8Rnrog==\"", new String(requests.get(0).getBody())); + } + + @Test + public void testSuccessHashMapSync() throws PubNubException, InterruptedException { + Map params = new HashMap<>(); + params.put("a", 10); + params.put("z", "test"); + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%7B%22a%22%3A10%2C%22z%22%3A%22test%22%7D")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message(params).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + + } + + @Test + public void testSuccessPOJOSync() throws PubNubException, InterruptedException { + + @AllArgsConstructor + @Getter + class TestPojo { + String field1; + String field2; + } + + TestPojo testPojo = new TestPojo("10", "20"); + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%7B%22field1%22%3A%2210%22%2C%22field2%22%3A%2220%22%7D")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message(testPojo).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + + } + + @org.junit.Test(expected=PubNubException.class) + public void testMissingChannel() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.message("hi").sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptyChannel() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.message("hi").channel("").sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testMissingMessage() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").sync(); + } + + @org.junit.Test + public void testOperationTypeSuccessAsync() throws IOException, PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hi%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + instance.async(new PNCallback() { + @Override + public void onResponse(PNPublishResult result, PNStatus status) { + if (status != null && status.getOperation()== PNOperationType.PNPublishOperation) { + atomic.incrementAndGet(); + } + + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hirep%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + pubnub.getConfiguration().setSubscribeKey(null); + instance.channel("coolChannel").message("hirep").sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hirep%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + pubnub.getConfiguration().setSubscribeKey(""); + instance.channel("coolChannel").message("hirep").sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullPublishKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hirep%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + pubnub.getConfiguration().setPublishKey(null); + instance.channel("coolChannel").message("hirep").sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptyPublishKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hirep%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + pubnub.getConfiguration().setPublishKey(""); + instance.channel("coolChannel").message("hirep").sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testInvalidMessage() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hirep%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message(new Object()).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + assertNull(requests.get(0).queryParameter("norep")); + } + + @org.junit.Test(expected=PubNubException.class) + public void testInvalidMeta() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/publish/myPublishKey/mySubscribeKey/0/coolChannel/0/%22hirep%22")) + .willReturn(aResponse().withBody("[1,\"Sent\",\"14598111595318003\"]"))); + + instance.channel("coolChannel").message("hi").meta(new Object()).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myUUID", requests.get(0).queryParameter("uuid").firstValue()); + assertNull(requests.get(0).queryParameter("norep")); + } +} diff --git a/src/test/java/com/pubnub/api/endpoints/pubsub/SubscribeEndpointTest.java b/src/test/java/com/pubnub/api/endpoints/pubsub/SubscribeEndpointTest.java new file mode 100644 index 000000000..dc5957343 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/pubsub/SubscribeEndpointTest.java @@ -0,0 +1,218 @@ +package com.pubnub.api.endpoints.pubsub; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.endpoints.TestHarness; +import com.pubnub.api.managers.RetrofitManager; +import com.pubnub.api.models.server.SubscribeEnvelope; +import com.pubnub.api.models.server.SubscribeMessage; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class SubscribeEndpointTest extends TestHarness { + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + PubNub pubnub; + Subscribe instance; + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + RetrofitManager retrofitManager = new RetrofitManager(pubnub); + instance = new Subscribe(pubnub, retrofitManager.getSubscriptionInstance()); + } + + @Test + public void subscribeChannelSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + SubscribeEnvelope subscribeEnvelope = instance.channels(Arrays.asList("coolChannel")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + + assertEquals("1", subscribeEnvelope.getMetadata().getRegion()); + assertTrue(subscribeEnvelope.getMetadata().getTimetoken().equals(14607577960932487L)); + + assertEquals(1, subscribeEnvelope.getMessages().size()); + SubscribeMessage subscribeMessage = subscribeEnvelope.getMessages().get(0); + assertEquals("4", subscribeMessage.getShard()); + assertEquals("0", subscribeMessage.getFlags()); + assertEquals("coolChannel", subscribeMessage.getChannel()); + assertEquals("coolChan-bnel", subscribeMessage.getSubscriptionMatch()); + assertEquals("sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f", subscribeMessage.getSubscribeKey()); + assertEquals("Client-g5d4g", subscribeMessage.getIssuingClientId()); + assertEquals("{\"text\":\"Enter Message Here\"}", subscribeMessage.getPayload().toString()); + } + + @Test + public void subscribeChannelsSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel,coolChannel2/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + instance.channels(Arrays.asList("coolChannel", "coolChannel2")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + } + + @Test + public void subscribeChannelsAuthSync() throws PubNubException { + + pubnub.getConfiguration().setAuthKey("authKey"); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel,coolChannel2/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + instance.channels(Arrays.asList("coolChannel", "coolChannel2")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals("authKey", requests.get(0).queryParameter("auth").firstValue()); + assertEquals(1, requests.size()); + } + + @Test + public void subscribeChannelsWithGroupSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel,coolChannel2/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + instance.channels(Arrays.asList("coolChannel", "coolChannel2")).channelGroups(Arrays.asList("cg1")) .sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("cg1", requests.get(0).queryParameter("channel-group").firstValue()); + } + + @Test + public void subscribeGroupsSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + instance.channelGroups(Arrays.asList("cg1", "cg2")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("cg1,cg2", requests.get(0).queryParameter("channel-group").firstValue()); + } + + @Test + public void subscribeGroupSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + instance.channelGroups(Arrays.asList("cg1")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("cg1", requests.get(0).queryParameter("channel-group").firstValue()); + } + + @Test + public void subscribeWithTimeTokenSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + instance.channelGroups(Arrays.asList("cg1")).timetoken(1337L).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("cg1", requests.get(0).queryParameter("channel-group").firstValue()); + assertEquals("1337", requests.get(0).queryParameter("tt").firstValue()); + } + + @Test + public void subscribeWithFilter() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("filter-expr", matching("this%3D1%26that%3Dcool")) + .withQueryParam("channel-group", matching("cg1")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + instance.channelGroups(Arrays.asList("cg1")).filterExpression("this=1&that=cool").sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + } + + @Test + public void subscribeWithRegion() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + instance.channelGroups(Arrays.asList("cg1")).region("10").sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("cg1", requests.get(0).queryParameter("channel-group").firstValue()); + assertEquals("10", requests.get(0).queryParameter("tr").firstValue()); + } + + @org.junit.Test(expected=PubNubException.class) + public void subscribeMissingChannelAndGroupSync() throws PubNubException { + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + SubscribeEnvelope subscribeEnvelope = instance.sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel,coolChannel2/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.getConfiguration().setSubscribeKey(null); + instance.channels(Arrays.asList("coolChannel", "coolChannel2")).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubKeySync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel,coolChannel2/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.getConfiguration().setSubscribeKey(""); + instance.channels(Arrays.asList("coolChannel", "coolChannel2")).sync(); + } + + @org.junit.Test + public void StopAndReconnect() throws PubNubException { + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/coolChannel,coolChannel2/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + instance.channels(Arrays.asList("coolChannel", "coolChannel2")).sync(); + pubnub.stop(); + pubnub.reconnect(); + instance.channels(Arrays.asList("coolChannel", "coolChannel2")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(2, requests.size()); + } + + + +} diff --git a/src/test/java/com/pubnub/api/endpoints/push/ListPushProvisionsTest.java b/src/test/java/com/pubnub/api/endpoints/push/ListPushProvisionsTest.java new file mode 100644 index 000000000..feda06c1e --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/push/ListPushProvisionsTest.java @@ -0,0 +1,165 @@ +package com.pubnub.api.endpoints.push; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.enums.PNPushType; +import com.pubnub.api.endpoints.TestHarness; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.push.PNPushListProvisionsResult; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; + +public class ListPushProvisionsTest extends TestHarness { + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + private ListPushProvisions instance; + private PubNub pubnub; + + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + instance = pubnub.auditPushChannelProvisions(); + } + + @Test + public void testAppleSuccessSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[\"ch1\", \"ch2\", \"ch3\"]"))); + + instance.deviceId("niceDevice").pushType(PNPushType.APNS).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + + assertEquals("apns", requests.get(0).queryParameter("type").firstValue()); + } + + @Test + public void testGoogleSuccessSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[\"ch1\", \"ch2\", \"ch3\"]"))); + + instance.deviceId("niceDevice").pushType(PNPushType.GCM).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + + assertEquals("gcm", requests.get(0).queryParameter("type").firstValue()); + } + + @Test + public void testMicrosoftSuccessSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[\"ch1\", \"ch2\", \"ch3\"]"))); + + instance.deviceId("niceDevice").pushType(PNPushType.MPNS).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + + assertEquals("mpns", requests.get(0).queryParameter("type").firstValue()); + } + + @org.junit.Test + public void testIsAuthRequiredSuccess() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[\"ch1\", \"ch2\", \"ch3\"]"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + instance.deviceId("niceDevice").pushType(PNPushType.APNS).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test + public void testOperationTypeSuccess() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[\"ch1\", \"ch2\", \"ch3\"]"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + instance.deviceId("niceDevice").pushType(PNPushType.APNS).async(new PNCallback() { + @Override + public void onResponse(PNPushListProvisionsResult result, PNStatus status) { + if (status != null && status.getOperation()== PNOperationType.PNPushNotificationEnabledChannelsOperation) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubscribeKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[\"ch1\", \"ch2\", \"ch3\"]"))); + + pubnub.getConfiguration().setSubscribeKey(null); + instance.deviceId("niceDevice").pushType(PNPushType.APNS).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubscribeKey() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[\"ch1\", \"ch2\", \"ch3\"]"))); + + pubnub.getConfiguration().setSubscribeKey(""); + instance.deviceId("niceDevice").pushType(PNPushType.MPNS).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullPushType() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[\"ch1\", \"ch2\", \"ch3\"]"))); + + instance.deviceId("niceDevice").sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullDeviceId() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[\"ch1\", \"ch2\", \"ch3\"]"))); + + instance.pushType(PNPushType.MPNS).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptyDeviceIdRemoveAll() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[\"ch1\", \"ch2\", \"ch3\"]"))); + + instance.deviceId("").pushType(PNPushType.MPNS).sync(); + } + +} diff --git a/src/test/java/com/pubnub/api/endpoints/push/ModifyPushChannelsForDeviceTest.java b/src/test/java/com/pubnub/api/endpoints/push/ModifyPushChannelsForDeviceTest.java new file mode 100644 index 000000000..771c26cd6 --- /dev/null +++ b/src/test/java/com/pubnub/api/endpoints/push/ModifyPushChannelsForDeviceTest.java @@ -0,0 +1,491 @@ +package com.pubnub.api.endpoints.push; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNub; +import com.pubnub.api.PubNubException; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.enums.PNPushType; +import com.pubnub.api.endpoints.TestHarness; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.models.consumer.push.PNPushAddChannelResult; +import com.pubnub.api.models.consumer.push.PNPushRemoveAllChannelsResult; +import com.pubnub.api.models.consumer.push.PNPushRemoveChannelResult; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; + +public class ModifyPushChannelsForDeviceTest extends TestHarness { + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + private PubNub pubnub; + private RemoveAllPushChannelsForDevice instance; + private AddChannelsToPush instanceAdd; + private RemoveChannelsFromPush instanceRemove; + + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + instance = pubnub.removeAllPushNotificationsFromDeviceWithPushToken(); + instanceAdd = pubnub.addPushNotificationsOnChannels(); + instanceRemove = pubnub.removePushNotificationsFromChannels(); + } + + @Test + public void testAppleSuccessSyncRemoveAll() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instance.deviceId("niceDevice").pushType(PNPushType.APNS).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("apns", requests.get(0).queryParameter("type").firstValue()); + } + + @Test + public void testGoogleSuccessSyncRemoveAll() throws PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instance.deviceId("niceDevice").pushType(PNPushType.GCM).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("gcm", requests.get(0).queryParameter("type").firstValue()); + } + + @Test + public void testMicrosoftSuccessSyncRemoveAll() throws PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instance.deviceId("niceDevice").pushType(PNPushType.MPNS).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("mpns", requests.get(0).queryParameter("type").firstValue()); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessRemoveAll() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + instance.deviceId("niceDevice").pushType(PNPushType.MPNS).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test + public void testOperationTypeSuccessRemoveAll() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + instance.deviceId("niceDevice").pushType(PNPushType.MPNS).async(new PNCallback() { + @Override + public void onResponse(PNPushRemoveAllChannelsResult result, PNStatus status) { + if (status != null && status.getOperation()== null) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubscribeKeyRemoveAll() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + pubnub.getConfiguration().setSubscribeKey(null); + instance.deviceId("niceDevice").pushType(PNPushType.MPNS).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubscribeKeyRemoveAll() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + pubnub.getConfiguration().setSubscribeKey(""); + instance.deviceId("niceDevice").pushType(PNPushType.MPNS).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullPushTypeRemoveAll() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instance.deviceId("niceDevice").sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullDeviceIdRemoveAll() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instance.pushType(PNPushType.MPNS).sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptyDeviceIdRemoveAll() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice/remove")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instance.deviceId("").pushType(PNPushType.MPNS).sync(); + } + + @Test + public void testAddAppleSuccessSync() throws PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceAdd.deviceId("niceDevice").pushType(PNPushType.APNS) + .channels(Arrays.asList("ch1", "ch2", "ch3")) + .sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("ch1,ch2,ch3", requests.get(0).queryParameter("add").firstValue()); + assertEquals("apns", requests.get(0).queryParameter("type").firstValue()); + + } + + @Test + public void testAddGoogleSuccessSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceAdd.deviceId("niceDevice").pushType(PNPushType.GCM) + .channels(Arrays.asList("ch1", "ch2", "ch3")) + .sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("ch1,ch2,ch3", requests.get(0).queryParameter("add").firstValue()); + assertEquals("gcm", requests.get(0).queryParameter("type").firstValue()); + + } + + @Test + public void testAddMicrosoftSuccessSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceAdd.deviceId("niceDevice").pushType(PNPushType.MPNS) + .channels(Arrays.asList("ch1", "ch2", "ch3")) + .sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("ch1,ch2,ch3", requests.get(0).queryParameter("add").firstValue()); + assertEquals("mpns", requests.get(0).queryParameter("type").firstValue()); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessAdd() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + instanceAdd.deviceId("niceDevice").pushType(PNPushType.MPNS) + .channels(Arrays.asList("ch1", "ch2", "ch3")) + .sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test + public void testOperationTypeSuccessAdd() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + instanceAdd.deviceId("niceDevice").pushType(PNPushType.MPNS) + .channels(Arrays.asList("ch1", "ch2", "ch3")) + .async(new PNCallback() { + @Override + public void onResponse(PNPushAddChannelResult result, PNStatus status) { + if (status != null && status.getOperation()== PNOperationType.PNPushNotificationEnabledChannelsOperation) { + atomic.incrementAndGet(); + } + } + }); + + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubscribeKeyAdd() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + pubnub.getConfiguration().setSubscribeKey(null); + instanceAdd.deviceId("niceDevice").pushType(PNPushType.MPNS) + .channels(Arrays.asList("ch1", "ch2", "ch3")) + .sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("ch1,ch2,ch3", requests.get(0).queryParameter("add").firstValue()); + assertEquals("mpns", requests.get(0).queryParameter("type").firstValue()); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubscribeKeyAdd() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + pubnub.getConfiguration().setSubscribeKey(""); + instanceAdd.deviceId("niceDevice").pushType(PNPushType.MPNS) + .channels(Arrays.asList("ch1", "ch2", "ch3")) + .sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("ch1,ch2,ch3", requests.get(0).queryParameter("add").firstValue()); + assertEquals("mpns", requests.get(0).queryParameter("type").firstValue()); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullPushTypeAdd() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceAdd.deviceId("niceDevice") + .channels(Arrays.asList("ch1", "ch2", "ch3")) + .sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullDeviceIdAdd() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceAdd.pushType(PNPushType.MPNS) + .channels(Arrays.asList("ch1", "ch2", "ch3")) + .sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptyDeviceIdAdd() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceAdd.deviceId("").pushType(PNPushType.MPNS) + .channels(Arrays.asList("ch1", "ch2", "ch3")) + .sync(); + } + + @org.junit.Test(expected=PubNubException.class) + public void testMissingChannelsAdd() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceAdd.deviceId("niceDevice").pushType(PNPushType.MPNS) + .sync(); + } + + + + @Test + public void testRemoveAppleSuccessSync() throws PubNubException, InterruptedException { + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceRemove.deviceId("niceDevice").pushType(PNPushType.APNS) + .channels(Arrays.asList("chr1", "chr2", "chr3")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("apns", requests.get(0).queryParameter("type").firstValue()); + assertEquals("chr1,chr2,chr3", requests.get(0).queryParameter("remove").firstValue()); + + } + + @Test + public void testGoogleSuccessSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceRemove.deviceId("niceDevice").pushType(PNPushType.GCM) + .channels(Arrays.asList("chr1", "chr2", "chr3")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("gcm", requests.get(0).queryParameter("type").firstValue()); + assertEquals("chr1,chr2,chr3", requests.get(0).queryParameter("remove").firstValue()); + + } + + @Test + public void testMicrosoftSuccessSync() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceRemove.deviceId("niceDevice").pushType(PNPushType.MPNS) + .channels(Arrays.asList("chr1", "chr2", "chr3")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("mpns", requests.get(0).queryParameter("type").firstValue()); + assertEquals("chr1,chr2,chr3", requests.get(0).queryParameter("remove").firstValue()); + } + + @org.junit.Test + public void testIsAuthRequiredSuccessRemove() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + pubnub.getConfiguration().setAuthKey("myKey"); + instanceRemove.deviceId("niceDevice").pushType(PNPushType.MPNS) + .channels(Arrays.asList("chr1", "chr2", "chr3")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test + public void testOperationTypeSuccessRemove() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + final AtomicInteger atomic = new AtomicInteger(0); + + instanceRemove.deviceId("niceDevice").pushType(PNPushType.MPNS) + .channels(Arrays.asList("chr1", "chr2", "chr3")).async(new PNCallback() { + @Override + public void onResponse(PNPushRemoveChannelResult result, PNStatus status) { + if (status != null && status.getOperation()== PNOperationType.PNRemovePushNotificationsFromChannelsOperation) { + atomic.incrementAndGet(); + } + } + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullSubscribeKeyRemove() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + pubnub.getConfiguration().setSubscribeKey(null); + instanceRemove.deviceId("niceDevice").pushType(PNPushType.MPNS) + .channels(Arrays.asList("chr1", "chr2", "chr3")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptySubscribeKeyRemove() throws IOException, PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + pubnub.getConfiguration().setSubscribeKey(""); + instanceRemove.deviceId("niceDevice").pushType(PNPushType.MPNS) + .channels(Arrays.asList("chr1", "chr2", "chr3")).sync(); + + List requests = findAll(getRequestedFor(urlMatching("/.*"))); + assertEquals(1, requests.size()); + assertEquals("myKey", requests.get(0).queryParameter("auth").firstValue()); + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullPushType() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceRemove.deviceId("niceDevice").channels(Arrays.asList("chr1", "chr2", "chr3")).sync(); + + } + + @org.junit.Test(expected=PubNubException.class) + public void testNullDeviceId() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceRemove.pushType(PNPushType.MPNS) + .channels(Arrays.asList("chr1", "chr2", "chr3")).sync(); + + } + + @org.junit.Test(expected=PubNubException.class) + public void testEmptyDeviceId() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceRemove.deviceId("").pushType(PNPushType.MPNS) + .channels(Arrays.asList("chr1", "chr2", "chr3")).sync(); + + } + + @org.junit.Test(expected=PubNubException.class) + public void testMissingChannels() throws PubNubException, InterruptedException { + + stubFor(get(urlPathEqualTo("/v1/push/sub-key/mySubscribeKey/devices/niceDevice")) + .willReturn(aResponse().withBody("[1, \"Modified Channels\"]"))); + + instanceRemove.deviceId("niceDevice").pushType(PNPushType.MPNS).sync(); + + } + +} diff --git a/src/test/java/com/pubnub/api/managers/BasePathManagerTest.java b/src/test/java/com/pubnub/api/managers/BasePathManagerTest.java new file mode 100644 index 000000000..297377dd9 --- /dev/null +++ b/src/test/java/com/pubnub/api/managers/BasePathManagerTest.java @@ -0,0 +1,122 @@ +package com.pubnub.api.managers; + +import com.pubnub.api.PNConfiguration; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +public class BasePathManagerTest { + + PNConfiguration pnConfiguration; + + @Before + public void beforeEach() throws IOException { + pnConfiguration = new PNConfiguration(); + } + + @Test + public void stdOriginNotSecure(){ + pnConfiguration.setSecure(false); + BasePathManager basePathManager = new BasePathManager(pnConfiguration); + Assert.assertEquals("http://pubsub.pubnub.com", basePathManager.getBasePath()); + } + + @Test + public void stdOriginSecure(){ + pnConfiguration.setSecure(true); + BasePathManager basePathManager = new BasePathManager(pnConfiguration); + Assert.assertEquals("https://pubsub.pubnub.com", basePathManager.getBasePath()); + } + + @Test + public void customOriginNotSecure(){ + pnConfiguration.setOrigin("custom.origin.com"); + pnConfiguration.setSecure(false); + BasePathManager basePathManager = new BasePathManager(pnConfiguration); + Assert.assertEquals("http://custom.origin.com", basePathManager.getBasePath()); + } + + @Test + public void customOriginSecure(){ + pnConfiguration.setOrigin("custom.origin.com"); + pnConfiguration.setSecure(true); + BasePathManager basePathManager = new BasePathManager(pnConfiguration); + Assert.assertEquals("https://custom.origin.com", basePathManager.getBasePath()); + } + + @Test + public void customOriginNotSecureWithCacheBusting(){ + pnConfiguration.setOrigin("custom.origin.com"); + pnConfiguration.setCacheBusting(true); + pnConfiguration.setSecure(false); + BasePathManager basePathManager = new BasePathManager(pnConfiguration); + Assert.assertEquals("http://custom.origin.com", basePathManager.getBasePath()); + } + + @Test + public void customOriginSecureWithCacheBusting(){ + pnConfiguration.setOrigin("custom.origin.com"); + pnConfiguration.setSecure(true); + pnConfiguration.setCacheBusting(true); + BasePathManager basePathManager = new BasePathManager(pnConfiguration); + Assert.assertEquals("https://custom.origin.com", basePathManager.getBasePath()); + } + + @Test + public void cacheBustingNotSecure(){ + pnConfiguration.setCacheBusting(true); + pnConfiguration.setSecure(false); + BasePathManager basePathManager = new BasePathManager(pnConfiguration); + Assert.assertEquals("http://ps1.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps2.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps3.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps4.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps5.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps6.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps7.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps8.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps9.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps10.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps11.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps12.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps13.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps14.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps15.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps16.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps17.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps18.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps19.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps20.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("http://ps1.pubnub.com", basePathManager.getBasePath()); + } + + @Test + public void cacheBustingSecure(){; + pnConfiguration.setCacheBusting(true); + BasePathManager basePathManager = new BasePathManager(pnConfiguration); + Assert.assertEquals("https://ps1.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps2.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps3.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps4.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps5.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps6.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps7.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps8.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps9.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps10.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps11.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps12.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps13.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps14.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps15.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps16.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps17.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps18.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps19.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps20.pubnub.com", basePathManager.getBasePath()); + Assert.assertEquals("https://ps1.pubnub.com", basePathManager.getBasePath()); + } + +} diff --git a/src/test/java/com/pubnub/api/managers/PublishSequenceManagerTest.java b/src/test/java/com/pubnub/api/managers/PublishSequenceManagerTest.java new file mode 100644 index 000000000..2ab72802e --- /dev/null +++ b/src/test/java/com/pubnub/api/managers/PublishSequenceManagerTest.java @@ -0,0 +1,18 @@ +package com.pubnub.api.managers; + +import org.junit.Assert; +import org.junit.Test; + +public class PublishSequenceManagerTest { + + @Test + public void testSequenceManager() { + PublishSequenceManager publishSequenceManager = new PublishSequenceManager(2); + + Assert.assertEquals(1, publishSequenceManager.getNextSequence()); + Assert.assertEquals(2, publishSequenceManager.getNextSequence()); + Assert.assertEquals(1, publishSequenceManager.getNextSequence()); + Assert.assertEquals(2, publishSequenceManager.getNextSequence()); + } +} + diff --git a/src/test/java/com/pubnub/api/managers/SubscriptionManagerTest.java b/src/test/java/com/pubnub/api/managers/SubscriptionManagerTest.java new file mode 100644 index 000000000..d94085460 --- /dev/null +++ b/src/test/java/com/pubnub/api/managers/SubscriptionManagerTest.java @@ -0,0 +1,881 @@ +package com.pubnub.api.managers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.http.QueryParameter; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.verification.LoggedRequest; +import com.jayway.awaitility.Awaitility; +import com.pubnub.api.PubNubUtil; +import com.pubnub.api.callbacks.PNCallback; +import com.pubnub.api.callbacks.SubscribeCallback; +import com.pubnub.api.PubNub; +import com.pubnub.api.enums.PNHeartbeatNotificationOptions; +import com.pubnub.api.enums.PNOperationType; +import com.pubnub.api.enums.PNStatusCategory; +import com.pubnub.api.models.consumer.presence.PNSetStateResult; +import com.pubnub.api.models.consumer.pubsub.PNMessageResult; +import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult; +import com.pubnub.api.models.consumer.PNStatus; +import com.pubnub.api.endpoints.TestHarness; +import org.junit.*; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class SubscriptionManagerTest extends TestHarness { + + private PubNub pubnub; + + @Before + public void beforeEach() throws IOException { + pubnub = this.createPubNubInstance(8080); + } + + @After + public void afterEach() { + pubnub.stop(); + } + + @Rule + public WireMockRule wireMockRule = new WireMockRule(); + + @Test + public void testGetSubscribedChannels() { + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Message\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).execute(); + + List channels = pubnub.getSubscribedChannels(); + + assertTrue(channels.contains("ch1")); + assertTrue(channels.contains("ch2")); + } + + @Test + public void testGetSubscribedChannelGroups() { + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.subscribe().channelGroups(Arrays.asList("cg1", "cg2")).execute(); + + List groups = pubnub.getSubscribedChannelGroups(); + + assertTrue(groups.contains("cg1")); + assertTrue(groups.contains("cg2")); + } + + @Test + public void testPubNubUnsubscribeAll() { + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")) + .channelGroups(Arrays.asList("cg1","cg2")) + .withPresence() + .execute(); + + List channels = pubnub.getSubscribedChannels(); + assertTrue(channels.contains("ch1")); + assertTrue(channels.contains("ch2")); + + List groups = pubnub.getSubscribedChannelGroups(); + assertTrue(groups.contains("cg1")); + assertTrue(groups.contains("cg2")); + + pubnub.unsubscribeAll(); + + channels = pubnub.getSubscribedChannels(); + assertEquals(0, channels.size()); + + groups = pubnub.getSubscribedChannelGroups(); + assertEquals(0, groups.size()); + } + + @Test + public void testSubscribeBuilder() { + final AtomicInteger gotStatus = new AtomicInteger(); + final AtomicBoolean gotMessage = new AtomicBoolean(); + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Message\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.addListener(new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + + if (status.getCategory() == PNStatusCategory.PNConnectedCategory) { + gotStatus.addAndGet(1); + } + + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + List requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))); + + assertEquals("Message", message.getMessage().get("text").asText()); + gotMessage.set(true); + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }); + + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).execute(); + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(gotMessage, org.hamcrest.core.IsEqual.equalTo(true)); + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(gotStatus, org.hamcrest.core.IsEqual.equalTo(1)); + + } + + @Test + public void testSubscribeSlidingBuilder() { + final AtomicBoolean gotMessage1 = new AtomicBoolean(); + final AtomicBoolean gotMessage2 = new AtomicBoolean(); + final AtomicBoolean gotMessage3 = new AtomicBoolean(); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"3\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Message\"},\"b\":\"coolChan-bnel\"}]}"))); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("3")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"10\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Message3\"},\"b\":\"coolChan-bnel\"}]}"))); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("tt", matching("10")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"20\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Message10\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.addListener(new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + List requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))); + + if (message.getMessage().get("text").asText().equals("Message")) { + gotMessage1.set(true); + } else if (message.getMessage().get("text").asText().equals("Message3")) { + gotMessage2.set(true); + } else if (message.getMessage().get("text").asText().equals("Message10")) { + gotMessage3.set(true); + } + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }); + + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).execute(); + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(gotMessage1, org.hamcrest.core.IsEqual.equalTo(true)); + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(gotMessage2, org.hamcrest.core.IsEqual.equalTo(true)); + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(gotMessage3, org.hamcrest.core.IsEqual.equalTo(true)); + } + + @Test + public void testSubscribeBuilderNumber() { + final AtomicInteger atomic = new AtomicInteger(0); + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\": 10,\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.addListener(new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + List requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))); + + assertEquals(10, message.getMessage().asInt()); + atomic.addAndGet(1); + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }); + + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).execute(); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.Matchers.greaterThan(0)); + + } + + @Test + public void testSubscribeBuilderWithState() throws IOException { + final AtomicInteger atomic = new AtomicInteger(0); + + final ObjectMapper mapper = new ObjectMapper(); + final String expectedPayload = PubNubUtil.urlDecode("%7B%22ch1%22%3A%5B%22p1%22%2C%22p2%22%5D%2C%22cg2%22%3A%5B%22p1%22%2C%22p2%22%5D%7D"); + final Map expectedMap = mapper.readValue(expectedPayload, Map.class); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.getConfiguration().setHeartbeatNotificationOptions(PNHeartbeatNotificationOptions.ALL); + pubnub.addListener(new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + + List requests = findAll(getRequestedFor(urlMatching( + "/v2/presence/sub-key/" + pubnub.getConfiguration().getSubscribeKey() + "/channel/ch2,ch1/heartbeat.*"))); + + for (LoggedRequest request: requests) { + String stateString = PubNubUtil.urlDecode(request.queryParameter("state").firstValue()); + Map actualMap = null; + try { + actualMap = mapper.readValue(stateString, Map.class); + } catch (IOException e) { + e.printStackTrace(); + } + + if (actualMap != null && actualMap.equals(expectedMap)) { + atomic.getAndAdd(1); + } + } + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }); + + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).channelGroups(Arrays.asList("cg1", "cg2")).execute(); + pubnub.setPresenceState().channels(Arrays.asList("ch1")).channelGroups(Arrays.asList("cg2")) + .state(Arrays.asList("p1", "p2")) + .async(new PNCallback() { + @Override + public void onResponse(PNSetStateResult result, PNStatus status) {} + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.Matchers.greaterThan(0)); + + } + + @Test + public void testSubscribeChannelGroupBuilder() { + final AtomicInteger atomic = new AtomicInteger(0); + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.addListener(new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + List requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))); + + for (LoggedRequest request: requests) { + QueryParameter channelGroupQuery = request.queryParameter("channel-group"); + if (channelGroupQuery != null && channelGroupQuery.firstValue().equals("cg1,cg2")) { + atomic.addAndGet(1); + } + } + + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }); + + + pubnub.subscribe().channelGroups(Arrays.asList("cg1", "cg2")).execute(); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.Matchers.greaterThan(0)); + + } + + @Test + public void testSubscribeChannelGroupWithPresenceBuilder() { + final AtomicInteger atomic = new AtomicInteger(0); + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/,/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.addListener(new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + List requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))); + + for (LoggedRequest request: requests) { + String[] channelGroups = request.queryParameter("channel-group").firstValue().split(","); + Arrays.sort(channelGroups); + if ("cg1,cg1-pnpres,cg2,cg2-pnpres".equals(joinArray(channelGroups))) { + atomic.addAndGet(1); + } + + } + + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }); + + + pubnub.subscribe().channelGroups(Arrays.asList("cg1", "cg2")).withPresence().execute(); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.Matchers.greaterThan(0)); + + } + + @Test + public void testSubscribeWithTimeTokenBuilder() { + final AtomicInteger atomic = new AtomicInteger(0); + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.addListener(new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + List requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))); + + assertEquals("1337", requests.get(0).queryParameter("tt").firstValue()); + atomic.addAndGet(1); + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }); + + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).withTimetoken(1337L).execute(); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.Matchers.greaterThan(0)); + + } + + @Test + public void testSubscribeWithFilterExpressionBuilder() { + final AtomicInteger atomic = new AtomicInteger(0); + pubnub.getConfiguration().setFilterExpression("much=filtering"); + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1/0")) + .withQueryParam("uuid", matching("myUUID")) + .withQueryParam("pnsdk", matching("PubNub-Java-Unified/suchJava")) + .withQueryParam("filter-expr", matching("much%3Dfiltering")) + .withQueryParam("tt", matching("0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.addListener(new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + List requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))); + assertTrue(requests.size() > 0); + atomic.addAndGet(1); + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }); + + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).execute(); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.Matchers.greaterThan(0)); + + } + + @Test + public void testSubscribePresenceBuilder() { + final AtomicInteger atomic = new AtomicInteger(0); + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.addListener(new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + int moose = 10; + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + List requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))); + + Assert.assertEquals("{\"text\":\"Enter Message Here\"}", message.getMessage().toString()); + atomic.addAndGet(1); + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }); + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).withPresence().execute(); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.Matchers.greaterThan(0)); + + } + + @Test + public void testSubscribePresencePayloadBuilder() { + final AtomicInteger atomic = new AtomicInteger(0); + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14614512228786519\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"p\":{\"t\":\"14614512228418349\",\"r\":2},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel-pnpres\",\"d\":{\"action\": \"join\", \"timestamp\": 1461451222, \"uuid\": \"4a6d5df7-e301-4e73-a7b7-6af9ab484eb0\", \"occupancy\": 1},\"b\":\"coolChannel-pnpres\"}]}\n"))); + + pubnub.addListener(new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + if (atomic.get() == 0) { + assertEquals("join", presence.getEvent()); + assertEquals("4a6d5df7-e301-4e73-a7b7-6af9ab484eb0", presence.getUuid()); + Assert.assertTrue(presence.getOccupancy().equals(1)); + Assert.assertTrue(presence.getTimestamp().equals(1461451222L)); + atomic.incrementAndGet(); + } + } + }); + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).withPresence().execute(); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(1)); + + } + + @Test + public void testSubscribePresenceStateCallback() { + final AtomicBoolean atomic = new AtomicBoolean(); + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch10,ch10-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14637536741734954\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":512,\"p\":{\"t\":\"14637536740940378\",\"r\":1},\"k\":\"demo-36\",\"c\":\"ch10-pnpres\",\"d\":{\"action\": \"join\", \"timestamp\": 1463753674, \"uuid\": \"24c9bb19-1fcd-4c40-a6f1-522a8a1329ef\", \"occupancy\": 3},\"b\":\"ch10-pnpres\"},{\"a\":\"4\",\"f\":512,\"p\":{\"t\":\"14637536741726901\",\"r\":1},\"k\":\"demo-36\",\"c\":\"ch10-pnpres\",\"d\":{\"action\": \"state-change\", \"timestamp\": 1463753674, \"data\": {\"state\": \"cool\"}, \"uuid\": \"24c9bb19-1fcd-4c40-a6f1-522a8a1329ef\", \"occupancy\": 3},\"b\":\"ch10-pnpres\"}]}"))); + + pubnub.addListener(new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + if (presence.getEvent().equals("state-change")) { + if (presence.getState().has("state") && presence.getState().get("state").asText().equals("cool")) { + atomic.set(true); + } + } + } + }); + + pubnub.subscribe().channels(Arrays.asList("ch10")).withPresence().execute(); + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(true)); + + } + + @Test + public void testSubscribeRegionBuilder() { + final AtomicBoolean atomic = new AtomicBoolean(); + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":8},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + pubnub.addListener(new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + List requests = findAll(getRequestedFor(urlMatching("/v2/subscribe.*"))); + + if (requests.size() > 1) { + assertEquals("8", requests.get(1).queryParameter("tr").firstValue()); + atomic.set(true); + } + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }); + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).withPresence().execute(); + + Awaitility.await().atMost(5, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(true)); + + } + + @Test + public void testRemoveListener() { + + final AtomicInteger atomic = new AtomicInteger(0); + + SubscribeCallback sub1 = new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + atomic.addAndGet(1); + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + atomic.addAndGet(1); + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + atomic.addAndGet(1); + } + }; + + pubnub.addListener(sub1); + pubnub.removeListener(sub1); + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).withPresence().execute(); + + Awaitility.await().atMost(2, TimeUnit.SECONDS) + .untilAtomic(atomic, org.hamcrest.core.IsEqual.equalTo(0)); + + } + + @Test + public void testUnsubscribe() { + + final AtomicBoolean statusRecieved = new AtomicBoolean(); + final AtomicBoolean messageRecieved = new AtomicBoolean(); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch2-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + SubscribeCallback sub1 = new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + + if (status.getCategory() == PNStatusCategory.PNConnectedCategory) { + pubnub.unsubscribe().channels(Arrays.asList("ch1")).execute(); + } + + if (status.getAffectedChannels().size() == 1 && status.getOperation() == PNOperationType.PNUnsubscribeOperation){ + if (status.getAffectedChannels().get(0).equals("ch1")) { + statusRecieved.set(true); + } + } + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + List requests = findAll(getRequestedFor(urlMatching("/v2/subscribe/mySubscribeKey/ch2,ch2-pnpres/0.*"))); + + if (!requests.isEmpty()) { + messageRecieved.set(true); + } + + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }; + + pubnub.addListener(sub1); + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).withPresence().execute(); + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(messageRecieved, org.hamcrest.core.IsEqual.equalTo(true)); + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(statusRecieved, org.hamcrest.core.IsEqual.equalTo(true)); + } + + @Test + public void testAllHeartbeats() { + + final AtomicBoolean statusRecieved = new AtomicBoolean(); + pubnub.getConfiguration().setHeartbeatNotificationOptions(PNHeartbeatNotificationOptions.ALL); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch2,ch1/heartbeat")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + SubscribeCallback sub1 = new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + if (status.getOperation() == PNOperationType.PNHeartbeatOperation && !status.isError()) { + statusRecieved.set(true); + } + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }; + + pubnub.addListener(sub1); + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).withPresence().execute(); + + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(statusRecieved, org.hamcrest.core.IsEqual.equalTo(true)); + } + + @Test + public void testSuccessOnFailureVerbosityHeartbeats() { + + final AtomicBoolean statusRecieved = new AtomicBoolean(); + pubnub.getConfiguration().setHeartbeatNotificationOptions(PNHeartbeatNotificationOptions.FAILURES); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch2-pnpres,ch1,ch1-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + + SubscribeCallback sub1 = new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + if (status.getOperation() == PNOperationType.PNHeartbeatOperation) { + statusRecieved.set(true); + } + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }; + + pubnub.addListener(sub1); + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).withPresence().execute(); + + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(statusRecieved, org.hamcrest.core.IsEqual.equalTo(true)); + } + + @Test + public void testFailedHeartbeats() { + + final AtomicBoolean statusRecieved = new AtomicBoolean(); + pubnub.getConfiguration().setHeartbeatNotificationOptions(PNHeartbeatNotificationOptions.ALL); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch2-pnpres,ch1,ch1-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + SubscribeCallback sub1 = new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + if (status.getOperation() == PNOperationType.PNHeartbeatOperation && status.isError()) { + statusRecieved.set(true); + } + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }; + + pubnub.addListener(sub1); + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).withPresence().execute(); + + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(statusRecieved, org.hamcrest.core.IsEqual.equalTo(true)); + } + + @Test + public void testSilencedHeartbeats() { + + final AtomicBoolean statusRecieved = new AtomicBoolean(); + pubnub.getConfiguration().setHeartbeatNotificationOptions(PNHeartbeatNotificationOptions.NONE); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch2-pnpres,ch1,ch1-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + SubscribeCallback sub1 = new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + if (status.getOperation() == PNOperationType.PNHeartbeatOperation) { + statusRecieved.set(true); + } + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }; + + pubnub.addListener(sub1); + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).withPresence().execute(); + + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(statusRecieved, org.hamcrest.core.IsEqual.equalTo(false)); + } + + @Test + public void testFailedNoneHeartbeats() { + + final AtomicBoolean statusRecieved = new AtomicBoolean(); + pubnub.getConfiguration().setHeartbeatNotificationOptions(PNHeartbeatNotificationOptions.NONE); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch2-pnpres,ch1,ch1-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch2,ch1/heartbeat")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + SubscribeCallback sub1 = new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + if (status.getOperation() == PNOperationType.PNHeartbeatOperation) { + statusRecieved.set(true); + } + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }; + + pubnub.addListener(sub1); + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).withPresence().execute(); + + + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAtomic(statusRecieved, org.hamcrest.core.IsEqual.equalTo(false)); + } + + @Test + public void testUnsubscribeAll() { + + final AtomicBoolean statusRecieved = new AtomicBoolean(); + final AtomicBoolean messageRecieved = new AtomicBoolean(); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch1,ch2-pnpres,ch1-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + stubFor(get(urlPathEqualTo("/v2/subscribe/mySubscribeKey/ch2,ch2-pnpres/0")) + .willReturn(aResponse().withBody("{\"t\":{\"t\":\"14607577960932487\",\"r\":1},\"m\":[{\"a\":\"4\",\"f\":0,\"i\":\"Client-g5d4g\",\"p\":{\"t\":\"14607577960925503\",\"r\":1},\"k\":\"sub-c-4cec9f8e-01fa-11e6-8180-0619f8945a4f\",\"c\":\"coolChannel\",\"d\":{\"text\":\"Enter Message Here\"},\"b\":\"coolChan-bnel\"}]}"))); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch1/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + stubFor(get(urlPathEqualTo("/v2/presence/sub-key/mySubscribeKey/channel/ch2/leave")) + .willReturn(aResponse().withBody("{\"status\": 200, \"message\": \"OK\", \"service\": \"Presence\", \"action\": \"leave\"}"))); + + SubscribeCallback sub1 = new SubscribeCallback() { + @Override + public void status(PubNub pubnub, PNStatus status) { + + if (status.getCategory() == PNStatusCategory.PNConnectedCategory) { + pubnub.unsubscribe().channels(Arrays.asList("ch1")).execute(); + } + + if (status.getAffectedChannels()!=null && status.getAffectedChannels().size() == 1 && status.getOperation() == PNOperationType.PNUnsubscribeOperation){ + if (status.getAffectedChannels().get(0).equals("ch1")) { + pubnub.unsubscribe().channels(Arrays.asList("ch2")).execute(); + } + } + + if (status.getAffectedChannels()!=null && status.getAffectedChannels().size() == 1 && status.getOperation() == PNOperationType.PNUnsubscribeOperation){ + if (status.getAffectedChannels().get(0).equals("ch2")) { + statusRecieved.set(true); + } + } + } + + @Override + public void message(PubNub pubnub, PNMessageResult message) { + } + + @Override + public void presence(PubNub pubnub, PNPresenceEventResult presence) { + } + }; + + pubnub.addListener(sub1); + + pubnub.subscribe().channels(Arrays.asList("ch1", "ch2")).withPresence().execute(); + + Awaitility.await().atMost(4, TimeUnit.SECONDS).untilAtomic(statusRecieved, org.hamcrest.core.IsEqual.equalTo(true)); + } + + + private String joinArray(String[] arr) { + StringBuilder builder = new StringBuilder(); + for(String s : arr) { + if (builder.length() != 0) { + builder.append(","); + } + builder.append(s); + } + return builder.toString(); + } +} diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml new file mode 100644 index 000000000..97d61f22f --- /dev/null +++ b/src/test/resources/logback.xml @@ -0,0 +1,18 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + \ No newline at end of file