diff --git a/.gitignore b/.gitignore
index cd968238..92715af9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,25 +1,14 @@
*.class
-.idea
-*.iml
-
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
-
-# Package Files #
*.jar
*.war
*.ear
-
-# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
-hs_err_pid*
-
-# Maven
+.gradle/
+.idea/
+.syntastic_javac_config
+build/
+gradle.properties
+gradle/
+gradlew
+gradlew.bat
+out/
target/
-pom.xml.tag
-pom.xml.releaseBackup
-pom.xml.versionsBackup
-pom.xml.next
-release.properties
-dependency-reduced-pom.xml
-buildNumber.properties
-.mvn/timing.properties
diff --git a/.java-version b/.java-version
new file mode 100644
index 00000000..ba6a626d
--- /dev/null
+++ b/.java-version
@@ -0,0 +1 @@
+oracle64-10.0.2
diff --git a/.travis/publish.sh b/.travis/publish.sh
deleted file mode 100755
index 6f5021e6..00000000
--- a/.travis/publish.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo.
-#
-# Adapted from https://coderwall.com/p/9b_lfq and
-# http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/
-
-SLUG="GetStream/stream-java"
-JDK="oraclejdk8"
-BRANCH="master"
-
-set -e
-
-if [ "$TRAVIS_REPO_SLUG" != "$SLUG" ]; then
- echo "Skipping snapshot deployment: wrong repository. Expected '$SLUG' but was '$TRAVIS_REPO_SLUG'."
-elif [ "$TRAVIS_JDK_VERSION" != "$JDK" ]; then
- echo "Skipping snapshot deployment: wrong JDK. Expected '$JDK' but was '$TRAVIS_JDK_VERSION'."
-elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
- echo "Skipping snapshot deployment: was pull request."
-elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then
- echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'."
-else
- echo "Deploying snapshot..."
- mvn clean deploy --settings=".travis/settings.xml" -Dmaven.test.skip=true
- echo "Snapshot deployed!"
-fi
\ No newline at end of file
diff --git a/.travis/settings.xml b/.travis/settings.xml
deleted file mode 100644
index de371552..00000000
--- a/.travis/settings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- ossrh
- ${env.CI_DEPLOY_USERNAME}
- ${env.CI_DEPLOY_PASSWORD}
-
-
-
\ No newline at end of file
diff --git a/LICENSE b/LICENCE
similarity index 59%
rename from LICENSE
rename to LICENCE
index 8a25cc4d..c59b050e 100644
--- a/LICENSE
+++ b/LICENCE
@@ -1,4 +1,4 @@
-Copyright (c) 2016-2017 Stream.io Inc, and individual contributors.
+Copyright (c) 2016-2018 Stream.io Inc, and individual contributors.
All rights reserved.
@@ -24,25 +24,4 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
-This software incorporates additional Open Source components. You can find the source code of these
-open source projects along with license information below.
-
- Base64.java:
-
- https://github.com/android/platform_frameworks_base/blob/master/core/java/android/util/Base64.java
-
- 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.
+POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/README.md b/README.md
index 90694696..e49391e5 100644
--- a/README.md
+++ b/README.md
@@ -1,49 +1,25 @@
stream-java
===========
-[](https://travis-ci.org/GetStream/stream-java)
-
[stream-java](https://github.com/GetStream/stream-java) is a Java client for [Stream](https://getstream.io/).
You can sign up for a Stream account at https://getstream.io/get_started.
-The Stream's Java client come in two different flavours, you should decide which one to drag into your project.
-Those two implementations differ according to the underlying library used to handle HTTP connections:
-
-- *stream-repo-apache* uses Apache HttpClient and we recommend it for backend applications. Apache HttpClient is a mature, reliable and rock-solid HTTP library.
-- *stream-repo-okhttp* uses Square's OkHttp which is lightweight, powerful and mobile-oriented HTTP library. We recommend it for mobile application.
-
### Installation
-If you decide to go for the *Apache HttpClient* implementation, add the following dependency to your pom.xml:
-
-```xml
-
- io.getstream.client
- stream-repo-apache
- 2.1.3
-
-```
-
-or in your build.gradle:
-
-```gradle
-compile 'io.getstream.client:stream-repo-apache:2.1.3'
-```
-
-Instead, if you opted for the *OkHttp* implementation please add it to your pom.xml
+Add the following dependency to your pom.xml:
```xml
io.getstream.client
- stream-repo-okhttp
- 2.1.3
+ stream-java
+ 3.0.0
```
or in your build.gradle:
```gradle
-compile 'io.getstream.client:stream-repo-okhttp:2.1.3'
+compile 'io.getstream.client:stream-java:3.0.0'
```
In case you want to download the artifact and put it manually into your project,
@@ -55,127 +31,32 @@ Snapshots of the development version are available in [Sonatype](https://oss.son
This API Client project requires Java SE 8.
-See the [Travis configuration](.travis.yml) for details of how it is built, tested and packaged.
-
### Full documentation
Documentation for this Java client are available at the [Stream website](https://getstream.io/docs/?language=java).
-### Usage
-
-```java
-/**
- * Instantiate a new client to connect to us east API endpoint
- * Find your API keys here https://getstream.io/dashboard/
- **/
-
-StreamClient streamClient = new StreamClientImpl(new ClientConfiguration(), "", "");
-```
-
-#### Create a new Feed
-
-```java
-/* Instantiate a feed object */
-Feed feed = streamClient.newFeed("user", "1");
-```
-
-#### Working with Activities
-
-```java
-/* Create an activity service */
-FlatActivityServiceImpl flatActivityService = feed.newFlatActivityService(SimpleActivity.class);
-
-/* Get activities from 5 to 10 (using offset pagination) */
-FeedFilter filter = new FeedFilter.Builder().withLimit(5).withOffset(5).build();
-List activities = flatActivityService.getActivities(filter).getResults();
-
-/* Filter on an id less than the given UUID */
-aid = "e561de8f-00f1-11e4-b400-0cc47a024be0";
-FeedFilter filter = new FeedFilter.Builder().withIdLowerThan(aid).withLimit(5).build();
-List activities = flatActivityService.getActivities(filter).getResults();
+For examples have a look [here](https://github.com/GetStream/stream-java/tree/master/example/Example.java).
-/* Create a new activity */
-SimpleActivity activity = new SimpleActivity();
-activity.setActor("user:1");
-activity.setObject("tweet:1");
-activity.setVerb("tweet");
-activity.setForeignId("tweet:1");
-SimpleActivity response = flatActivityService.addActivity(activity);
-
-/* Remove an activity by its id */
-feed.deleteActivity("e561de8f-00f1-11e4-b400-0cc47a024be0");
-
-/* Remove activities by their foreign_id */
-feed.deleteActivityByForeignId("tweet:1");
-```
-
-In case you want to add a single activity to multiple feeds, you can use the batch feature _addToMany_:
-
-```java
-/* Batch adding activities to many feeds */
-flatActivityService.addActivityToMany(ImmutableList.of("user:1", "user:2").asList(), myActivity);
-```
-
-The API client allows you to send activities with custom field as well, you can find a
-complete example [here](https://github.com/GetStream/stream-java/blob/master/stream-repo-apache/src/test/java/io/getstream/client/example/mixtype/MixedType.java)
-
-#### Follow and Unfollow
-
-```java
-/* Follow another feed */
-feed.follow(flat", "42");
-
-/* Stop following another feed */
-feed.unfollow(flat", "42");
-
-/* Retrieve first 10 followers of a feed */
-FeedFilter filter = new FeedFilter.Builder().withLimit(10).build();
-List followingPaged = feed.getFollowing(filter);
-
-/* Retrieve the first 10 followed feeds */
-FeedFilter filter = new FeedFilter.Builder().withLimit(10).build();
-List followingPaged = feed.getFollowing(filter);
-```
-
-In case you want to send to Stream a long list of following relationships you can use the batch feature _followMany_:
-
-```java
-/* Batch following many feeds */
-FollowMany followMany = new FollowMany.Builder()
- .add("user:1", "user:2")
- .add("user:1", "user:3")
- .add("user:1", "user:4")
- .add("user:2", "user:3")
- .build();
-feed.followMany(followMany);
-
-```
-
-#### Client token
-
-In order to generate a token for client side usage (e.g. JS client), you can use the following code:
-
-```java
-/* Generating tokens for client side usage */
-String token = feed.getToken();
-```
+Docs are available on [GetStream.io](http://getstream.io/docs/).
-#### Further references
+Javadocs are available [here](https://getstream.github.io/stream-java/).
-For more examples have a look [here](https://github.com/GetStream/stream-java/tree/master/stream-repo-apache/src/test/java/io/getstream/client/apache/example).
+### Building & Testing
-Docs are available on [GetStream.io](http://getstream.io/docs/).
+Run `gradle wrapper --gradle-version 5.0` to generate gradle wrapper files
-Javadocs are available [here](https://getstream.github.io/stream-java/).
+Run `gradle test` to execute integration tests
### Credits & Contributors
-This project was originally contributed by [Alessandro Pieri](sirio7g), prior to him joining Stream as an employee.
+Project is maintained by [Max Klyga](nekuromento).
+
+This project was originally contributed by [Alessandro Pieri](sirio7g).
We continue to welcome pull requests from community members.
### Copyright and License Information
-Copyright (c) 2016-2017 Stream.io Inc, and individual contributors. All rights reserved.
+Copyright (c) 2016-2018 Stream.io Inc, and individual contributors. All rights reserved.
-See the file "LICENSE" for information on the history of this software, terms & conditions for usage, and a DISCLAIMER OF ALL WARRANTIES.
+See the file "LICENSE" for information on the history of this software, terms & conditions for usage, and a DISCLAIMER OF ALL WARRANTIES.
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000..d3573920
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,127 @@
+plugins {
+ id 'java-library'
+ id 'eclipse'
+ id 'idea'
+ id 'maven-publish'
+ id 'signing'
+ id "com.scuilion.syntastic" version "0.3.8"
+ id "com.prot.versioninfo" version "0.5"
+}
+
+group 'io.getstream.client'
+version = '3.0.0'
+
+dependencies {
+ testCompile 'org.junit.jupiter:junit-jupiter-api:5.3.1'
+ testCompile 'org.junit.jupiter:junit-jupiter-params:5.3.1'
+ testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.3.1'
+ testRuntime 'org.junit.vintage:junit-vintage-engine:5.3.1'
+ testCompile 'com.pholser:junit-quickcheck-core:0.8.1'
+ testCompile 'com.pholser:junit-quickcheck-generators:0.8.1'
+
+ implementation 'com.google.guava:guava:26.0-jre'
+
+ implementation 'com.squareup.okhttp3:okhttp:3.11.0'
+
+ compile 'com.fasterxml.jackson.core:jackson-core:2.9.6'
+ compile 'com.fasterxml.jackson.core:jackson-annotations:2.9.6'
+ compile 'com.fasterxml.jackson.core:jackson-databind:2.9.6'
+
+ compile 'com.auth0:java-jwt:3.4.0'
+}
+
+compileJava {
+ sourceCompatibility = 1.8
+ targetCompatibility = 1.8
+}
+
+test {
+ useJUnitPlatform {
+ includeEngines 'junit-jupiter', 'junit-vintage'
+ }
+ testLogging {
+ exceptionFormat = 'full'
+ events 'standard_out', 'standard_error', "passed", "skipped", "failed"
+ }
+}
+
+repositories {
+ jcenter()
+}
+
+processResources {
+ filesMatching('stream-java2.info') {
+ expand(project.properties)
+ }
+}
+
+task sourcesJar(type: Jar) {
+ from sourceSets.main.allJava
+ classifier = 'sources'
+}
+
+task javadocJar(type: Jar) {
+ from javadoc
+ classifier = 'javadoc'
+}
+
+publishing {
+ publications {
+ mavenJava(MavenPublication) {
+ artifactId = 'stream-java'
+ from components.java
+ artifact sourcesJar
+ artifact javadocJar
+
+ pom {
+ name = 'client'
+ description = 'Stream API official client'
+ url = 'https://github.com/GetStream/stream-java'
+
+ scm {
+ url = 'scm:git@github.com:GetStream/stream-java.git'
+ connection = 'scm:git@github.com:GetStream/stream-java.git'
+ developerConnection = 'scm:git@github.com:GetStream/stream-java.git'
+ }
+
+ licenses {
+ license {
+ name = 'The 3-Clause BSD License'
+ url = 'https://opensource.org/licenses/BSD-3-Clause'
+ distribution = 'repo'
+ }
+ }
+
+ developers {
+ developer {
+ id = 'sirio7g'
+ name = 'Alessandro Pieri'
+ }
+ developer {
+ id = 'nekuromento'
+ name = 'Max Klyga'
+ }
+ }
+ }
+ }
+ }
+ repositories {
+ maven {
+ url 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
+ credentials {
+ username = sonatypeUsername
+ password = sonatypePassword
+ }
+ }
+ }
+}
+
+signing {
+ sign publishing.publications.mavenJava
+}
+
+javadoc {
+ if (JavaVersion.current().isJava9Compatible()) {
+ options.addBooleanOption('html5', true)
+ }
+}
diff --git a/data/test.jpg b/data/test.jpg
new file mode 100644
index 00000000..fd87cca4
Binary files /dev/null and b/data/test.jpg differ
diff --git a/data/test.txt b/data/test.txt
new file mode 100644
index 00000000..f0c79c33
--- /dev/null
+++ b/data/test.txt
@@ -0,0 +1 @@
+Hello Stream!
\ No newline at end of file
diff --git a/example/Example.java b/example/Example.java
new file mode 100644
index 00000000..c5795aca
--- /dev/null
+++ b/example/Example.java
@@ -0,0 +1,628 @@
+package example;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import io.getstream.client.Client;
+import io.getstream.client.FlatFeed;
+import io.getstream.client.NotificationFeed;
+import io.getstream.core.KeepHistory;
+import io.getstream.core.LookupKind;
+import io.getstream.core.Region;
+import io.getstream.core.models.*;
+import io.getstream.core.options.*;
+
+import java.io.File;
+import java.net.URL;
+import java.time.LocalDateTime;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import static io.getstream.core.utils.Enrichment.createCollectionReference;
+import static io.getstream.core.utils.Enrichment.createUserReference;
+
+class Example {
+ private static final String apiKey = "gp6e8sxxzud6";
+ private static final String secret = "7j7exnksc4nxy399fdxvjqyqsqdahax3nfgtp27pumpc7sfm9um688pzpxjpjbf2";
+
+ public static void main(String[] args) throws Exception {
+ Client client = Client.builder(apiKey, secret)
+ .build();
+
+ FlatFeed chris = client.flatFeed("user", "chris");
+ // Add an Activity; message is a custom field - tip: you can add unlimited custom fields!
+ chris.addActivity(Activity.builder()
+ .actor("chris")
+ .verb("add")
+ .object("picture:10")
+ .foreignID("picture:10")
+ .extraField("message", "Beautiful bird!")
+ .build());
+
+ // Create a following relationship between Jack's "timeline" feed and Chris' "user" feed:
+ FlatFeed jack = client.flatFeed("timeline", "jack");
+ jack.follow(chris);
+
+ // Read Jack's timeline and Chris' post appears in the feed:
+ List response = jack.getActivities(new Pagination().limit(10)).join();
+ for (Activity activity : response) {
+ // ...
+ }
+
+ // Remove an Activity by referencing it's foreign_id
+ chris.removeActivityByForeignID("picture:10");
+
+ /* -------------------------------------------------------- */
+
+ // Instantiate a feed object
+ FlatFeed userFeed = client.flatFeed("user", "1");
+
+ // Add an activity to the feed, where actor, object and target are references to objects (`Eric`, `Hawaii`, `Places to Visit`)
+ Activity activity = Activity.builder()
+ .actor("User:1")
+ .verb("pin")
+ .object("Place:42")
+ .target("Board:1")
+ .build();
+ userFeed.addActivity(activity);
+
+ // Create a bit more complex activity
+ activity = Activity.builder()
+ .actor("User:1")
+ .verb("run")
+ .object("Exercise:42")
+ .foreignID("run:1")
+ .extra(new ImmutableMap.Builder()
+ .put("course", new ImmutableMap.Builder()
+ .put("name", "Golden Gate park")
+ .put("distance", 10)
+ .build())
+ .put("participants", new String[]{
+ "Thierry",
+ "Tommaso",
+ })
+ .put("started_at", LocalDateTime.now())
+ .put("location", new ImmutableMap.Builder()
+ .put("type", "point")
+ .put("coordinates", new double[]{37.769722, -122.476944})
+ .build())
+ .build())
+ .build();
+ userFeed.addActivity(activity);
+
+ // Remove an activity by its id
+ userFeed.removeActivityByID("e561de8f-00f1-11e4-b400-0cc47a024be0");
+
+ // Remove activities with foreign_id 'run:1'
+ userFeed.removeActivityByForeignID("run:1");
+
+ activity = Activity.builder()
+ .actor("1")
+ .verb("like")
+ .object("3")
+ .time(new Date())
+ .foreignID("like:3")
+ .extraField("popularity", 100)
+ .build();
+
+ // first time the activity is added
+ userFeed.addActivity(activity);
+
+ // update the popularity value for the activity
+ activity = Activity.builder()
+ .fromActivity(activity)
+ .extraField("popularity", 10)
+ .build();
+
+ client.batch().updateActivities(activity);
+
+ /* -------------------------------------------------------- */
+
+ // partial update by activity ID
+
+ // prepare the set operations
+ Map set = new ImmutableMap.Builder()
+ .put("product.price", 19.99)
+ .put("shares", new ImmutableMap.Builder()
+ .put("facebook", "...")
+ .put("twitter", "...")
+ .build())
+ .build();
+ // prepare the unset operations
+ String[] unset = new String[] { "daily_likes", "popularity" };
+
+ String id = "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4";
+ client.updateActivityByID(id, set, unset);
+
+ String foreignID = "product:123";
+ Date timestamp = new Date();
+ client.updateActivityByForeignID(foreignID, timestamp, set, unset);
+
+ FeedID[] add = new FeedID[0];
+ FeedID[] remove = new FeedID[0];
+ userFeed.updateActivityToTargets(activity, add, remove);
+
+ FeedID[] newTargets = new FeedID[0];
+ userFeed.replaceActivityToTargets(activity, newTargets);
+
+ /* -------------------------------------------------------- */
+
+ Date now = new Date();
+ Activity firstActivity = userFeed.addActivity(Activity.builder()
+ .actor("1")
+ .verb("like")
+ .object("3")
+ .time(now)
+ .foreignID("like:3")
+ .build()).join();
+ Activity secondActivity = userFeed.addActivity(Activity.builder()
+ .actor("1")
+ .verb("like")
+ .object("3")
+ .time(now)
+ .extraField("extra", "extra_value")
+ .foreignID("like:3")
+ .build()).join();
+ // foreign ID and time are the same for both activities
+ // hence only one activity is created and first and second IDs are equal
+ // firstActivity.ID == secondActivity.ID
+
+ /* -------------------------------------------------------- */
+
+ // Get 5 activities with id less than the given UUID (Faster - Recommended!)
+ response = userFeed.getActivities(new Filter().idLessThan("e561de8f-00f1-11e4-b400-0cc47a024be0").limit(5)).join();
+ // Get activities from 5 to 10 (Pagination-based - Slower)
+ response = userFeed.getActivities(new Pagination().offset(0).limit(5)).join();
+ // Get activities sorted by rank (Ranked Feeds Enabled):
+ response = userFeed.getActivities(new Pagination().limit(5), "popularity").join();
+
+ /* -------------------------------------------------------- */
+
+ // timeline:timeline_feed_1 follows user:user_42
+ FlatFeed user = client.flatFeed("user", "user_42");
+ FlatFeed timeline = client.flatFeed("timeline", "timeline_feed_1");
+ timeline.follow(user);
+
+ // follow feed without copying the activities:
+ timeline.follow(user, 0);
+
+ /* -------------------------------------------------------- */
+
+ // user := client.FlatFeed("user", "42")
+
+ // Stop following feed user:user_42
+ timeline.unfollow(user);
+
+ // Stop following feed user:user_42 but keep history of activities
+ timeline.unfollow(user, KeepHistory.YES);
+
+ // list followers
+ List followers = userFeed.getFollowers(new Pagination().offset(0).limit(10)).join();
+ for (FollowRelation follow : followers) {
+ System.out.format("%s -> %s", follow.getSource(), follow.getTarget());
+ // ...
+ }
+
+ // Retrieve last 10 feeds followed by user_feed_1
+ List followed = userFeed.getFollowed(new Pagination().offset(0).limit(10)).join();
+
+ // Retrieve 10 feeds followed by user_feed_1 starting from the 11th
+ followed = userFeed.getFollowed(new Pagination().offset(10).limit(10)).join();
+
+ // Check if user_feed_1 follows specific feeds
+ followed = userFeed.getFollowed(new Pagination().offset(0).limit(2), new FeedID("user:42"), new FeedID("user", "43")).join();
+
+ /* -------------------------------------------------------- */
+
+ NotificationFeed notifications = client.notificationFeed("notifications", "1");
+ // Mark all activities in the feed as seen
+ List> activityGroups = notifications.getActivities(new ActivityMarker().allSeen()).join();
+ for (NotificationGroup group : activityGroups) {
+ // ...
+ }
+ // Mark some activities as read via specific Activity Group Ids
+ activityGroups = notifications.getActivities(new ActivityMarker().read("groupID1", "groupID2" /* ... */)).join();
+
+ /* -------------------------------------------------------- */
+
+ // Add an activity to the feed, where actor, object and target are references to objects - adding your ranking method as a parameter (in this case, "popularity"):
+ activity = Activity.builder()
+ .actor("User:1")
+ .verb("pin")
+ .object("place:42")
+ .target("board:1")
+ .extraField("popularity", 5)
+ .build();
+ userFeed.addActivity(activity);
+
+ // Get activities sorted by the ranking method labelled 'activity_popularity' (Ranked Feeds Enabled)
+ response = userFeed.getActivities(new Pagination().limit(5), "activity_popularity").join();
+
+ /* -------------------------------------------------------- */
+
+ // Add the activity to Eric's feed and to Jessica's notification feed
+ activity = Activity.builder()
+ .actor("User:Eric")
+ .verb("tweet")
+ .object("tweet:id")
+ .to(Lists.newArrayList(new FeedID("notification:Jessica")))
+ .extraField("message", "@Jessica check out getstream.io it's so dang awesome.")
+ .build();
+ userFeed.addActivity(activity);
+
+ // The TO field ensures the activity is send to the player, match and team feed
+ activity = Activity.builder()
+ .actor("Player:Suarez")
+ .verb("foul")
+ .object("Player:Ramos")
+ .to(Lists.newArrayList(new FeedID("team:barcelona"), new FeedID("match:1")))
+ .extraField("match", ImmutableMap.of("El Classico", 10))
+ .build();
+ // playerFeed.addActivity(activity);
+ userFeed.addActivity(activity);
+
+ /* -------------------------------------------------------- */
+
+ // Batch following many feeds
+ // Let timeline:1 will follow user:1, user:2 and user:3
+ FollowRelation[] follows = new FollowRelation[]{
+ new FollowRelation("timeline:1", "user:1"),
+ new FollowRelation("timeline:3", "user:2"),
+ new FollowRelation("timeline:1", "user:3")
+ };
+ client.batch().followMany(follows);
+ // copy only the last 10 activities from every feed
+ client.batch().followMany(10, follows);
+
+ /* -------------------------------------------------------- */
+
+ Activity[] activities = new Activity[]{
+ Activity.builder()
+ .actor("User:1")
+ .verb("tweet")
+ .object("Tweet:1")
+ .build(),
+ Activity.builder()
+ .actor("User:2")
+ .verb("watch")
+ .object("Movie:1")
+ .build()
+ };
+ userFeed.addActivities(activities);
+
+ /* -------------------------------------------------------- */
+
+ // adds 1 activity to many feeds in one request
+ activity = Activity.builder()
+ .actor("User:2")
+ .verb("pin")
+ .object("Place:42")
+ .target("Board:1")
+ .build();
+ FeedID[] feeds = new FeedID[]{
+ new FeedID("timeline", "1"),
+ new FeedID("timeline", "2"),
+ new FeedID("timeline", "3"),
+ new FeedID("timeline", "4")
+ };
+ client.batch().addToMany(activity, feeds);
+
+ /* -------------------------------------------------------- */
+
+ // retrieve two activities by ID
+ client.batch().getActivitiesByID("01b3c1dd-e7ab-4649-b5b3-b4371d8f7045", "ed2837a6-0a3b-4679-adc1-778a1704852");
+
+ // retrieve an activity by foreign ID and time
+ client.batch().getActivitiesByForeignID(new ForeignIDTimePair("foreignID1", new Date()), new ForeignIDTimePair("foreignID2", new Date()));
+
+ /* -------------------------------------------------------- */
+
+ // connect to the us-east region
+ client = Client.builder(apiKey, secret)
+ .region(Region.US_EAST)
+ .build();
+
+ /* -------------------------------------------------------- */
+ Reaction like = new Reaction.Builder()
+ .kind("like")
+ .activityID(activity.getID())
+ .build();
+
+ // add a like reaction to the activity with id activityId
+ like = client.reactions().add("john-doe", like).join();
+
+ Reaction comment = new Reaction.Builder()
+ .kind("comment")
+ .activityID(activity.getID())
+ .extraField("text", "awesome post!")
+ .build();
+
+ // adds a comment reaction to the activity with id activityId
+ comment = client.reactions().add("john-doe", comment).join();
+
+ /* -------------------------------------------------------- */
+
+ // first let's read current user's timeline feed and pick one activity
+ response = client.flatFeed("timeline", "mike").getActivities().join();
+ activity = response.get(0);
+
+ // then let's add a like reaction to that activity
+ client.reactions().add("john-doe", Reaction.builder()
+ .kind("like")
+ .activityID(activity.getID())
+ .build());
+
+ /* -------------------------------------------------------- */
+
+ comment = new Reaction.Builder()
+ .kind("comment")
+ .activityID(activity.getID())
+ .extraField("text", "awesome post!")
+ .build();
+
+ // adds a comment reaction to the activity and notify Thierry's notification feed
+ client.reactions().add("john-doe", comment, new FeedID("notification:thierry"));
+
+ /* -------------------------------------------------------- */
+
+ // read bob's timeline and include most recent reactions to all activities and their total count
+ client.flatFeed("timeline", "bob")
+ .getEnrichedActivities(new EnrichmentFlags()
+ .withRecentReactions()
+ .withReactionCounts());
+
+ // read bob's timeline and include most recent reactions to all activities and her own reactions
+ client.flatFeed("timeline", "bob")
+ .getEnrichedActivities(new EnrichmentFlags()
+ .withOwnReactions()
+ .withRecentReactions()
+ .withReactionCounts());
+
+ /* -------------------------------------------------------- */
+
+ // retrieve all kind of reactions for an activity
+ List reactions = client.reactions().filter(LookupKind.ACTIVITY, "ed2837a6-0a3b-4679-adc1-778a1704852d").join();
+
+ // retrieve first 10 likes for an activity
+ reactions = client.reactions()
+ .filter(LookupKind.ACTIVITY,
+ "ed2837a6-0a3b-4679-adc1-778a1704852d",
+ new Filter().limit(10),
+ "like").join();
+
+ // retrieve the next 10 likes using the id_lt param
+ reactions = client.reactions()
+ .filter(LookupKind.ACTIVITY,
+ "ed2837a6-0a3b-4679-adc1-778a1704852d",
+ new Filter().idLessThan("e561de8f-00f1-11e4-b400-0cc47a024be0"),
+ "like").join();
+
+ /* -------------------------------------------------------- */
+
+ // adds a like to the previously created comment
+ Reaction reaction = client.reactions().addChild("john-doe", comment.getId(), Reaction.builder().kind("like").build()).join();
+
+ /* -------------------------------------------------------- */
+
+ client.reactions().update(Reaction.builder()
+ .id(reaction.getId())
+ .extraField("text", "love it!")
+ .build());
+
+ /* -------------------------------------------------------- */
+
+ client.reactions().delete(reaction.getId());
+
+ /* -------------------------------------------------------- */
+
+ client.collections().add("food", new CollectionData("cheese-burger")
+ .set("name", "Cheese Burger")
+ .set("rating", "4 stars"));
+
+ // if you don't have an id on your side, just use null as the ID and Stream will generate a unique ID
+ client.collections().add("food", new CollectionData()
+ .set("name", "Cheese Burger")
+ .set("rating", "4 stars"));
+
+ /* -------------------------------------------------------- */
+
+ CollectionData collection = client.collections().get("food", "cheese-burger").join();
+
+ /* -------------------------------------------------------- */
+
+ client.collections().delete("food", "cheese-burger");
+
+ /* -------------------------------------------------------- */
+
+ client.collections().update("food", new CollectionData("cheese-burger")
+ .set("name", "Cheese Burger")
+ .set("rating", "1 star"));
+
+ /* -------------------------------------------------------- */
+
+ client.collections().upsert("visitor",
+ new CollectionData("123")
+ .set("name", "John")
+ .set("favorite_color", "blue"),
+ new CollectionData("124")
+ .set("name", "Jane")
+ .set("favorite_color", "purple")
+ .set("interests", Lists.newArrayList("fashion", "jazz")));
+
+ /* -------------------------------------------------------- */
+
+ // select the entries with ID 123 and 124 from items collection
+ List objects = client.collections().select("items", "123", "124").join();
+
+ /* -------------------------------------------------------- */
+
+ // delete the entries with ID 123 and 124 from visitor collection
+ client.collections().deleteMany("visitor", "123", "124");
+
+ /* -------------------------------------------------------- */
+
+ // first we add our object to the food collection
+ CollectionData cheeseBurger = client.collections().add("food", new CollectionData("123")
+ .set("name", "Cheese Burger")
+ .set("ingredients", Lists.newArrayList("cheese", "burger", "bread", "lettuce", "tomato"))).join();
+
+ // the object returned by .add can be embedded directly inside of an activity
+ userFeed.addActivity(Activity.builder()
+ .actor(createUserReference("john-doe"))
+ .verb("grill")
+ .object(createCollectionReference(cheeseBurger.getCollection(), cheeseBurger.getID()))
+ .build());
+
+ // if we now read the feed, the activity we just added will include the entire full object
+ userFeed.getEnrichedActivities();
+
+ // we can then update the object and Stream will propagate the change to all activities
+ client.collections().update(cheeseBurger.getCollection(), cheeseBurger
+ .set("name", "Amazing Cheese Burger")
+ .set("ingredients", Lists.newArrayList("cheese", "burger", "bread", "lettuce", "tomato"))).join();
+
+ /* -------------------------------------------------------- */
+
+ // First create a collection entry with upsert api
+ client.collections().upsert("food", new CollectionData().set("name", "Cheese Burger"));
+
+ // Then create a user
+ client.user("john-doe").create(new Data()
+ .set("name", "John Doe")
+ .set("occupation", "Software Engineer")
+ .set("gender", "male"));
+
+ // Since we know their IDs we can create references to both without reading from APIs
+ String cheeseBurgerRef = createCollectionReference("food", "cheese-burger");
+ String johnDoeRef = createUserReference("john-doe");
+
+ client.flatFeed("user", "john").addActivity(Activity.builder()
+ .actor(johnDoeRef)
+ .verb("eat")
+ .object(cheeseBurgerRef)
+ .build());
+
+ /* -------------------------------------------------------- */
+
+ // create a new user, if the user already exist an error is returned
+ client.user("john-doe").create(new Data()
+ .set("name", "John Doe")
+ .set("occupation", "Software Engineer")
+ .set("gender", "male"));
+
+ // get or create a new user, if the user already exist the user is returned
+ client.user("john-doe").getOrCreate(new Data()
+ .set("name", "John Doe")
+ .set("occupation", "Software Engineer")
+ .set("gender", "male"));
+
+ /* -------------------------------------------------------- */
+
+ client.user("123").get();
+
+ /* -------------------------------------------------------- */
+
+ client.user("123").delete();
+
+ /* -------------------------------------------------------- */
+
+ client.user("123").update(new Data()
+ .set("name", "Jane Doe")
+ .set("occupation", "Software Engineer")
+ .set("gender", "female"));
+
+ /* -------------------------------------------------------- */
+
+ // Read the personalization feed for a given user
+ client.personalization().get("personalized_feed", new ImmutableMap.Builder()
+ .put("user_id", 123)
+ .put("feed_slug", "timeline")
+ .build());
+
+ // Our data science team will typically tell you which endpoint to use
+ client.personalization().get("discovery_feed", new ImmutableMap.Builder()
+ .put("user_id", 123)
+ .put("source_feed_slug", "timeline")
+ .put("target_feed_slug", "user")
+ .build());
+
+ /* -------------------------------------------------------- */
+
+ client.analytics().trackEngagement(Engagement.builder()
+ .feedID("user:thierry")
+ .content(new Content("message:34349698")
+ .set("verb", "share")
+ .set("actor", ImmutableMap.of("1", "user1")))
+ .boost(2)
+ .location("profile_page")
+ .position(3)
+ .build());
+
+ /* -------------------------------------------------------- */
+
+ client.analytics().trackImpression(Impression.builder()
+ .contentList(new Content("tweet:34349698")
+ .set("verb", "share")
+ .set("actor", ImmutableMap.of("1", "user1")),
+ new Content("tweet:34349699"),
+ new Content("tweet:34349700"))
+ .feedID("flat:tommaso")
+ .location("android-app")
+ .build());
+
+ /* -------------------------------------------------------- */
+
+ // the URL to direct to
+ URL targetURL = new URL("http://mysite.com/detail");
+
+ // track the impressions and a click
+ List impressions = Lists.newArrayList(Impression.builder()
+ .contentList(new Content("tweet:1"),
+ new Content("tweet:2"),
+ new Content("tweet:3"))
+ .userData(new UserData("tommaso", null))
+ .location("email")
+ .feedID("user:global")
+ .build());
+ List engagements = Lists.newArrayList(Engagement.builder()
+ .content(new Content("tweet:2"))
+ .label("click")
+ .position(1)
+ .userData(new UserData("tommaso", null))
+ .location("email")
+ .feedID("user:global")
+ .build());
+
+ // when the user opens the tracking URL in their browser gets redirected to the target URL
+ // the events are added to our analytics platform
+ URL trackingURL = client.analytics().createRedirectURL(targetURL, impressions, engagements);
+
+ /* -------------------------------------------------------- */
+
+ File image = new File("...");
+ URL imageURL = client.images().upload(image).join();
+
+ File file = new File("...");
+ URL fileURL = client.files().upload(file).join();
+
+ /* -------------------------------------------------------- */
+
+ // deleting an image using the url returned by the APIs
+ client.images().delete(imageURL);
+
+ // deleting a file using the url returned by the APIs
+ client.files().delete(fileURL);
+
+ /* -------------------------------------------------------- */
+
+ // create a 50x50 thumbnail and crop from center
+ client.images().process(imageURL, new Resize(50, 50, Resize.Type.CROP));
+
+ // create a 50x50 thumbnail using clipping (keeps aspect ratio)
+ client.images().process(imageURL, new Resize(50, 50, Resize.Type.CLIP));
+
+ /* -------------------------------------------------------- */
+
+ OGData urlPreview = client.openGraph(new URL("http://www.imdb.com/title/tt0117500/")).join();
+ }
+}
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index dbf15999..00000000
--- a/pom.xml
+++ /dev/null
@@ -1,331 +0,0 @@
-
-
- 4.0.0
-
- io.getstream.client
- stream-java
- pom
- 2.1.4-SNAPSHOT
-
- stream-java
- stream-java is a Java client for http://getstream.io.
- https://github.com/GetStream/stream-java
-
-
-
- The 3-Clause BSD License
- https://opensource.org/licenses/BSD-3-Clause
-
-
- Apache License, Version 2.0
- http://www.apache.org/licenses/LICENSE-2.0.txt
-
-
-
-
-
- sirio7g
- Alessandro Pieri
- https://github.com/sirio7g
-
-
- tbarbugli
- Tommaso Barbugli
- https://github.com/tbarbugli
-
-
-
-
- scm:git:git@github.com:GetStream/stream-java.git
- scm:git:git@github.com:GetStream/stream-java.git
- git@github.com:GetStream/stream-java.git
- HEAD
-
-
-
- stream-core
- stream-repo-apache
- stream-repo-okhttp
-
-
-
- UTF-8
- UTF-8
- 1.6.6
- 1.1.2
- 18.0
- 2.4.3
- 1.0
- 4.11
- 1.10.19
- 3.3.0
- 1.57
-
- true
- false
-
-
-
-
-
- org.slf4j
- slf4j-api
- ${slf4j-api.version}
-
-
- ch.qos.logback
- logback-classic
- ${logback.version}
-
-
- ch.qos.logback
- logback-core
- ${logback.version}
-
-
- com.google.guava
- guava
- ${guava.version}
-
-
- com.fasterxml.jackson.core
- jackson-core
- ${jackson.version}
-
-
- com.fasterxml.jackson.core
- jackson-annotations
- ${jackson.version}
-
-
- com.fasterxml.jackson.core
- jackson-databind
- ${jackson.version}
-
-
- org.tomitribe
- tomitribe-http-signatures
- ${tomitribe-http-signatures.version}
-
-
- com.auth0
- java-jwt
- ${java-jwt.version}
-
-
-
-
- junit
- junit
- ${junit.version}
- test
-
-
- org.mockito
- mockito-core
- ${mockito.version}
- test
-
-
- com.github.tomakehurst
- wiremock
- ${wiremock.version}
- test
-
-
- standalone
-
-
- org.mortbay.jetty
- jetty
-
-
- com.google.guava
- guava
-
-
- com.fasterxml.jackson.core
- jackson-core
-
-
- com.fasterxml.jackson.core
- jackson-annotations
-
-
- com.fasterxml.jackson.core
- jackson-databind
-
-
- org.apache.httpcomponents
- httpclient
-
-
- org.skyscreamer
- jsonassert
-
-
- xmlunit
- xmlunit
-
-
- com.jayway.jsonpath
- json-path
-
-
- net.sf.jopt-simple
- jopt-simple
-
-
-
-
-
-
-
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-release-plugin
- 2.5
-
- false
- release
- deploy
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 2.10.3
-
-
- aggregate
-
- aggregate
-
- site
-
-
-
-
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.3
- true
-
- ossrh
- https://oss.sonatype.org/
- true
-
-
-
- maven-compiler-plugin
- 2.3.2
-
- 1.7
- 1.7
-
-
-
- maven-release-plugin
- 2.5
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 2.19.1
-
- ${skipTests}
-
- **/*IntegrationTest.java
-
-
-
-
- org.apache.maven.plugins
- maven-failsafe-plugin
- 2.19.1
-
- ${skipTests}
- ${skipITs}
-
- **/IntegrationTest.java
-
-
-
-
- integration-test
-
- integration-test
- verify
-
-
-
-
-
-
-
-
-
- release
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
- 2.2.1
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 2.9.1
-
-
- attach-javadocs
-
- jar
-
-
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.5
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
-
-
-
-
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 00000000..4f791be5
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,10 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * The settings file is used to specify which projects to include in your build.
+ *
+ * Detailed information about configuring a multi-project build in Gradle can be found
+ * in the user guide at https://docs.gradle.org/4.10.2/userguide/multi_project_builds.html
+ */
+
+rootProject.name = 'stream-java'
diff --git a/src/main/java/io/getstream/client/AggregatedFeed.java b/src/main/java/io/getstream/client/AggregatedFeed.java
new file mode 100644
index 00000000..0d653617
--- /dev/null
+++ b/src/main/java/io/getstream/client/AggregatedFeed.java
@@ -0,0 +1,217 @@
+package io.getstream.client;
+
+import io.getstream.core.exceptions.StreamException;
+import io.getstream.core.models.Activity;
+import io.getstream.core.models.EnrichedActivity;
+import io.getstream.core.models.FeedID;
+import io.getstream.core.models.Group;
+import io.getstream.core.options.ActivityMarker;
+import io.getstream.core.options.EnrichmentFlags;
+import io.getstream.core.options.Filter;
+import io.getstream.core.options.Pagination;
+import io.getstream.core.utils.DefaultOptions;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+import static io.getstream.core.utils.Serialization.deserializeContainer;
+
+public class AggregatedFeed extends Feed {
+ AggregatedFeed(Client client, FeedID id) {
+ super(client, id);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities() throws StreamException {
+ return getActivities(DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(Pagination pagination) throws StreamException {
+ return getActivities(pagination, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(Filter filter) throws StreamException {
+ return getActivities(DefaultOptions.DEFAULT_PAGINATION, filter, DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(ActivityMarker marker) throws StreamException {
+ return getActivities(DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(Filter filter, ActivityMarker marker) throws StreamException {
+ return getActivities(DefaultOptions.DEFAULT_PAGINATION, filter, marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(Pagination pagination, ActivityMarker marker) throws StreamException {
+ return getActivities(pagination, DefaultOptions.DEFAULT_FILTER, marker);
+ }
+
+ CompletableFuture extends List extends Group>> getActivities(Pagination pagination, Filter filter, ActivityMarker marker) throws StreamException {
+ return getClient()
+ .getActivities(getID(), pagination, filter, marker)
+ .thenApply(response -> {
+ try {
+ return deserializeContainer(response, Group.class, Activity.class);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(Class type) throws StreamException {
+ return getCustomActivities(type, DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(Class type, Pagination pagination) throws StreamException {
+ return getCustomActivities(type, pagination, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(Class type, Filter filter) throws StreamException {
+ return getCustomActivities(type, DefaultOptions.DEFAULT_PAGINATION, filter, DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(Class type, ActivityMarker marker) throws StreamException {
+ return getCustomActivities(type, DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(Class type, Filter filter, ActivityMarker marker) throws StreamException {
+ return getCustomActivities(type, DefaultOptions.DEFAULT_PAGINATION, filter, marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(Class type, Pagination pagination, ActivityMarker marker) throws StreamException {
+ return getCustomActivities(type, pagination, DefaultOptions.DEFAULT_FILTER, marker);
+ }
+
+ CompletableFuture extends List extends Group>> getCustomActivities(Class type, Pagination pagination, Filter filter, ActivityMarker marker) throws StreamException {
+ return getClient()
+ .getActivities(getID(), pagination, filter, marker)
+ .thenApply(response -> {
+ try {
+ return deserializeContainer(response, Group.class, type);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities() throws StreamException {
+ return getEnrichedActivities(DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER, DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(Pagination pagination) throws StreamException {
+ return getEnrichedActivities(pagination, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER, DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(Pagination pagination, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(pagination, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(Filter filter) throws StreamException {
+ return getEnrichedActivities(DefaultOptions.DEFAULT_PAGINATION, filter, DefaultOptions.DEFAULT_MARKER, DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(Filter filter, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(DefaultOptions.DEFAULT_PAGINATION, filter, DefaultOptions.DEFAULT_MARKER, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(ActivityMarker marker) throws StreamException {
+ return getEnrichedActivities(DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, marker, DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(ActivityMarker marker, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, marker, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(Filter filter, ActivityMarker marker) throws StreamException {
+ return getEnrichedActivities(DefaultOptions.DEFAULT_PAGINATION, filter, marker, DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(Filter filter, ActivityMarker marker, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(DefaultOptions.DEFAULT_PAGINATION, filter, marker, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(Pagination pagination, ActivityMarker marker) throws StreamException {
+ return getEnrichedActivities(pagination, DefaultOptions.DEFAULT_FILTER, marker, DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(Pagination pagination, ActivityMarker marker, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(pagination, DefaultOptions.DEFAULT_FILTER, marker, flags);
+ }
+
+ CompletableFuture extends List extends Group>> getEnrichedActivities(Pagination pagination, Filter filter, ActivityMarker marker, EnrichmentFlags flags) throws StreamException {
+ return getClient()
+ .getEnrichedActivities(getID(), pagination, filter, marker, flags)
+ .thenApply(response -> {
+ try {
+ return deserializeContainer(response, Group.class, EnrichedActivity.class);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type) throws StreamException {
+ return getEnrichedCustomActivities(type, DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER, DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedCustomActivities(type, DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type, Pagination pagination) throws StreamException {
+ return getEnrichedCustomActivities(type, pagination, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER, DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type, Pagination pagination, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedCustomActivities(type, pagination, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type, Filter filter) throws StreamException {
+ return getEnrichedCustomActivities(type, DefaultOptions.DEFAULT_PAGINATION, filter, DefaultOptions.DEFAULT_MARKER, DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type, Filter filter, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedCustomActivities(type, DefaultOptions.DEFAULT_PAGINATION, filter, DefaultOptions.DEFAULT_MARKER, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type, ActivityMarker marker) throws StreamException {
+ return getEnrichedCustomActivities(type, DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, marker, DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type, ActivityMarker marker, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedCustomActivities(type, DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, marker, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type, Filter filter, ActivityMarker marker) throws StreamException {
+ return getEnrichedCustomActivities(type, DefaultOptions.DEFAULT_PAGINATION, filter, marker, DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type, Filter filter, ActivityMarker marker, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedCustomActivities(type, DefaultOptions.DEFAULT_PAGINATION, filter, marker, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type, Pagination pagination, ActivityMarker marker) throws StreamException {
+ return getEnrichedCustomActivities(type, pagination, DefaultOptions.DEFAULT_FILTER, marker, DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type, Pagination pagination, ActivityMarker marker, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedCustomActivities(type, pagination, DefaultOptions.DEFAULT_FILTER, marker, flags);
+ }
+
+ CompletableFuture extends List extends Group>> getEnrichedCustomActivities(Class type, Pagination pagination, Filter filter, ActivityMarker marker, EnrichmentFlags flags) throws StreamException {
+ return getClient()
+ .getEnrichedActivities(getID(), pagination, filter, marker, flags)
+ .thenApply(response -> {
+ try {
+ return deserializeContainer(response, Group.class, type);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+}
diff --git a/src/main/java/io/getstream/client/AnalyticsClient.java b/src/main/java/io/getstream/client/AnalyticsClient.java
new file mode 100644
index 00000000..742d47d7
--- /dev/null
+++ b/src/main/java/io/getstream/client/AnalyticsClient.java
@@ -0,0 +1,56 @@
+package io.getstream.client;
+
+import com.google.common.collect.Iterables;
+import io.getstream.core.StreamAnalytics;
+import io.getstream.core.exceptions.StreamException;
+import io.getstream.core.http.Token;
+import io.getstream.core.models.Engagement;
+import io.getstream.core.models.Impression;
+import io.getstream.core.utils.Auth.TokenAction;
+
+import java.net.URL;
+import java.util.concurrent.CompletableFuture;
+
+import static io.getstream.core.utils.Auth.buildAnalyticsRedirectToken;
+import static io.getstream.core.utils.Auth.buildAnalyticsToken;
+
+public final class AnalyticsClient {
+ private final String secret;
+ private final StreamAnalytics analytics;
+
+ AnalyticsClient(String secret, StreamAnalytics analytics) {
+ this.secret = secret;
+ this.analytics = analytics;
+ }
+
+ public CompletableFuture trackEngagement(Iterable events) throws StreamException {
+ return trackEngagement(Iterables.toArray(events, Engagement.class));
+ }
+
+ public CompletableFuture trackEngagement(Engagement... events) throws StreamException {
+ final Token token = buildAnalyticsToken(secret, TokenAction.WRITE);
+ return analytics.trackEngagement(token, events);
+ }
+
+ public CompletableFuture trackImpression(Impression event) throws StreamException {
+ final Token token = buildAnalyticsToken(secret, TokenAction.WRITE);
+ return analytics.trackImpression(token, event);
+ }
+
+ public URL createRedirectURL(URL url, Engagement... engagements) throws StreamException {
+ return createRedirectURL(url, new Impression[0], engagements);
+ }
+
+ public URL createRedirectURL(URL url, Impression... impressions) throws StreamException {
+ return createRedirectURL(url, impressions, new Engagement[0]);
+ }
+
+ public URL createRedirectURL(URL url, Iterable impressions, Iterable engagements) throws StreamException {
+ return createRedirectURL(url, Iterables.toArray(impressions, Impression.class), Iterables.toArray(engagements, Engagement.class));
+ }
+
+ public URL createRedirectURL(URL url, Impression[] impressions, Engagement[] engagements) throws StreamException {
+ final Token token = buildAnalyticsRedirectToken(secret);
+ return analytics.createRedirectURL(token, url, impressions, engagements);
+ }
+}
diff --git a/src/main/java/io/getstream/client/BatchClient.java b/src/main/java/io/getstream/client/BatchClient.java
new file mode 100644
index 00000000..7cb05333
--- /dev/null
+++ b/src/main/java/io/getstream/client/BatchClient.java
@@ -0,0 +1,80 @@
+package io.getstream.client;
+
+import com.google.common.collect.Iterables;
+import io.getstream.core.StreamBatch;
+import io.getstream.core.exceptions.StreamException;
+import io.getstream.core.http.Token;
+import io.getstream.core.models.Activity;
+import io.getstream.core.models.FeedID;
+import io.getstream.core.models.FollowRelation;
+import io.getstream.core.models.ForeignIDTimePair;
+import io.getstream.core.utils.DefaultOptions;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import static io.getstream.core.utils.Auth.*;
+
+public final class BatchClient {
+ private final String secret;
+ private final StreamBatch batch;
+
+ BatchClient(String secret, StreamBatch batch) {
+ this.secret = secret;
+ this.batch = batch;
+ }
+
+ public CompletableFuture addToMany(Activity activity, FeedID... feeds) throws StreamException {
+ final Token token = buildFeedToken(secret, TokenAction.WRITE);
+ return batch.addToMany(token, activity, feeds);
+ }
+
+ public CompletableFuture followMany(int activityCopyLimit, FollowRelation... follows) throws StreamException {
+ final Token token = buildFollowToken(secret, TokenAction.WRITE);
+ return batch.followMany(token, activityCopyLimit, follows);
+ }
+
+ public CompletableFuture followMany(int activityCopyLimit, Iterable follows) throws StreamException {
+ return followMany(activityCopyLimit, Iterables.toArray(follows, FollowRelation.class));
+ }
+
+ public CompletableFuture followMany(FollowRelation... follows) throws StreamException {
+ return followMany(DefaultOptions.DEFAULT_ACTIVITY_COPY_LIMIT, follows);
+ }
+
+ public CompletableFuture followMany(Iterable follows) throws StreamException {
+ return followMany(Iterables.toArray(follows, FollowRelation.class));
+ }
+
+ public CompletableFuture unfollowMany(FollowRelation... follows) throws StreamException {
+ final Token token = buildFollowToken(secret, TokenAction.DELETE);
+ return batch.unfollowMany(token, follows);
+ }
+
+ public CompletableFuture> getActivitiesByID(Iterable activityIDs) throws StreamException {
+ return getActivitiesByID(Iterables.toArray(activityIDs, String.class));
+ }
+
+ public CompletableFuture> getActivitiesByID(String... activityIDs) throws StreamException {
+ final Token token = buildActivityToken(secret, TokenAction.READ);
+ return batch.getActivitiesByID(token, activityIDs);
+ }
+
+ public CompletableFuture> getActivitiesByForeignID(Iterable activityIDTimePairs) throws StreamException {
+ return getActivitiesByForeignID(Iterables.toArray(activityIDTimePairs, ForeignIDTimePair.class));
+ }
+
+ public CompletableFuture> getActivitiesByForeignID(ForeignIDTimePair... activityIDTimePairs) throws StreamException {
+ final Token token = buildActivityToken(secret, TokenAction.READ);
+ return batch.getActivitiesByForeignID(token, activityIDTimePairs);
+ }
+
+ public CompletableFuture updateActivities(Iterable activities) throws StreamException {
+ return updateActivities(Iterables.toArray(activities, Activity.class));
+ }
+
+ public CompletableFuture updateActivities(Activity... activities) throws StreamException {
+ final Token token = buildActivityToken(secret, TokenAction.WRITE);
+ return batch.updateActivities(token, activities);
+ }
+}
diff --git a/src/main/java/io/getstream/client/Client.java b/src/main/java/io/getstream/client/Client.java
new file mode 100644
index 00000000..77d41a1e
--- /dev/null
+++ b/src/main/java/io/getstream/client/Client.java
@@ -0,0 +1,296 @@
+package io.getstream.client;
+
+import com.google.common.collect.Iterables;
+import io.getstream.core.Region;
+import io.getstream.core.Stream;
+import io.getstream.core.exceptions.StreamException;
+import io.getstream.core.http.HTTPClient;
+import io.getstream.core.http.OKHTTPClientAdapter;
+import io.getstream.core.http.Response;
+import io.getstream.core.http.Token;
+import io.getstream.core.models.*;
+import io.getstream.core.options.RequestOption;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static io.getstream.core.utils.Auth.*;
+
+public final class Client {
+ private final String secret;
+ private final Stream stream;
+
+ private Client(String key, String secret, URL baseURL, HTTPClient httpClient) {
+ this.secret = secret;
+ this.stream = new Stream(key, baseURL, httpClient);
+ }
+
+ public static Builder builder(String apiKey, String secret) {
+ return new Builder(apiKey, secret);
+ }
+
+ public CompletableFuture updateActivityByID(String id, Map set, Iterable unset) throws StreamException {
+ return updateActivityByID(id, set, Iterables.toArray(unset, String.class));
+ }
+
+ public CompletableFuture updateActivityByID(String id, Map set, String[] unset) throws StreamException {
+ final Token token = buildFeedToken(secret, TokenAction.WRITE);
+ return stream.updateActivityByID(token, id, set, unset);
+ }
+
+ public CompletableFuture updateActivityByForeignID(ForeignIDTimePair foreignIDTimePair, Map set, Iterable unset) throws StreamException {
+ checkNotNull(foreignIDTimePair, "No activity to update");
+ return updateActivityByForeignID(foreignIDTimePair.getForeignID(), foreignIDTimePair.getTime(), set, unset);
+ }
+
+ public CompletableFuture updateActivityByForeignID(ForeignIDTimePair foreignIDTimePair, Map set, String[] unset) throws StreamException {
+ checkNotNull(foreignIDTimePair, "No activity to update");
+ return updateActivityByForeignID(foreignIDTimePair.getForeignID(), foreignIDTimePair.getTime(), set, unset);
+ }
+
+ public CompletableFuture updateActivityByForeignID(String foreignID, Date timestamp, Map set, Iterable unset) throws StreamException {
+ return updateActivityByForeignID(foreignID, timestamp, set, Iterables.toArray(unset, String.class));
+ }
+
+ public CompletableFuture updateActivityByForeignID(String foreignID, Date timestamp, Map set, String[] unset) throws StreamException {
+ final Token token = buildActivityToken(secret, TokenAction.WRITE);
+ return stream.updateActivityByForeignID(token, foreignID, timestamp, set, unset);
+ }
+
+ public CompletableFuture openGraph(URL url) throws StreamException {
+ final Token token = buildOpenGraphToken(secret);
+ return stream.openGraph(token, url);
+ }
+
+ public static final class Builder {
+ private static final String DEFAULT_HOST = "stream-io-api.com";
+
+ private final String apiKey;
+ private final String secret;
+ private HTTPClient httpClient;
+
+ private String scheme = "https";
+ private String region = Region.US_EAST.toString();
+ private String host = DEFAULT_HOST;
+ private int port = 443;
+
+ public Builder(String apiKey, String secret) {
+ checkNotNull(apiKey, "API key can't be null");
+ checkNotNull(secret, "Secret can't be null");
+ checkArgument(!apiKey.isEmpty(), "API key can't be empty");
+ checkArgument(!secret.isEmpty(), "Secret can't be empty");
+ this.apiKey = apiKey;
+ this.secret = secret;
+ }
+
+ public Builder httpClient(HTTPClient httpClient) {
+ checkNotNull(httpClient, "HTTP client can't be null");
+ this.httpClient = httpClient;
+ return this;
+ }
+
+ public Builder scheme(String scheme) {
+ checkNotNull(scheme, "Scheme can't be null");
+ checkArgument(!scheme.isEmpty(), "Scheme can't be empty");
+ this.scheme = scheme;
+ return this;
+ }
+
+ public Builder host(String host) {
+ checkNotNull(host, "Host can't be null");
+ checkArgument(!host.isEmpty(), "Host can't be empty");
+ this.host = host;
+ return this;
+ }
+
+ public Builder port(int port) {
+ checkArgument(port > 0, "Port has to be a non-zero positive number");
+ this.port = port;
+ return this;
+ }
+
+ public Builder region(Region region) {
+ checkNotNull(region, "Region can't be null");
+ this.region = region.toString();
+ return this;
+ }
+
+ public Builder region(String region) {
+ checkNotNull(region, "Region can't be null");
+ checkArgument(!region.isEmpty(), "Region can't be empty");
+ this.region = region;
+ return this;
+ }
+
+ private String buildHost() {
+ final StringBuilder sb = new StringBuilder();
+ if (host.equals(DEFAULT_HOST)) {
+ sb.append(region).append(".");
+ }
+ sb.append(host);
+ return sb.toString();
+ }
+
+ public Client build() throws MalformedURLException {
+ if (httpClient == null) {
+ httpClient = new OKHTTPClientAdapter();
+ }
+ return new Client(apiKey, secret, new URL(scheme, buildHost(), port, ""), httpClient);
+ }
+ }
+
+ public T getHTTPClientImplementation() {
+ return stream.getHTTPClientImplementation();
+ }
+
+ public Token frontendToken(String userID) {
+ return buildFrontendToken(secret, userID);
+ }
+
+ public FlatFeed flatFeed(FeedID id) {
+ return new FlatFeed(this, id);
+ }
+
+ public FlatFeed flatFeed(String slug, String userID) {
+ return flatFeed(new FeedID(slug, userID));
+ }
+
+ public AggregatedFeed aggregatedFeed(FeedID id) {
+ return new AggregatedFeed(this, id);
+ }
+
+ public AggregatedFeed aggregatedFeed(String slug, String userID) {
+ return aggregatedFeed(new FeedID(slug, userID));
+ }
+
+ public NotificationFeed notificationFeed(FeedID id) {
+ return new NotificationFeed(this, id);
+ }
+
+ public NotificationFeed notificationFeed(String slug, String userID) {
+ return notificationFeed(new FeedID(slug, userID));
+ }
+
+ public User user(String userID) {
+ return new User(this, userID);
+ }
+
+ public BatchClient batch() {
+ return new BatchClient(secret, stream.batch());
+ }
+
+ public CollectionsClient collections() {
+ return new CollectionsClient(secret, stream.collections());
+ }
+
+ public PersonalizationClient personalization() {
+ return new PersonalizationClient(secret, stream.personalization());
+ }
+
+ public AnalyticsClient analytics() {
+ return new AnalyticsClient(secret, stream.analytics());
+ }
+
+ public ReactionsClient reactions() {
+ return new ReactionsClient(secret, stream.reactions());
+ }
+
+ public FileStorageClient files() {
+ return new FileStorageClient(secret, stream.files());
+ }
+
+ public ImageStorageClient images() {
+ return new ImageStorageClient(secret, stream.images());
+ }
+
+ CompletableFuture getActivities(FeedID feed, RequestOption... options) throws StreamException {
+ final Token token = buildFeedToken(secret, feed, TokenAction.READ);
+ return stream.getActivities(token, feed, options);
+ }
+
+ CompletableFuture getEnrichedActivities(FeedID feed, RequestOption... options) throws StreamException {
+ final Token token = buildFeedToken(secret, feed, TokenAction.READ);
+ return stream.getEnrichedActivities(token, feed, options);
+ }
+
+ CompletableFuture addActivity(FeedID feed, Activity activity) throws StreamException {
+ final Token token = buildFeedToken(secret, feed, TokenAction.WRITE);
+ return stream.addActivity(token, feed, activity);
+ }
+
+ CompletableFuture addActivities(FeedID feed, Activity... activities) throws StreamException {
+ final Token token = buildFeedToken(secret, feed, TokenAction.WRITE);
+ return stream.addActivities(token, feed, activities);
+ }
+
+ CompletableFuture removeActivityByID(FeedID feed, String id) throws StreamException {
+ final Token token = buildFeedToken(secret, feed, TokenAction.DELETE);
+ return stream.removeActivityByID(token, feed, id);
+ }
+
+ CompletableFuture removeActivityByForeignID(FeedID feed, String foreignID) throws StreamException {
+ final Token token = buildFeedToken(secret, feed, TokenAction.DELETE);
+ return stream.removeActivityByForeignID(token, feed, foreignID);
+ }
+
+ CompletableFuture follow(FeedID source, FeedID target, int activityCopyLimit) throws StreamException {
+ final Token token = buildFollowToken(secret, source, TokenAction.WRITE);
+ final Token targetToken = buildFeedToken(secret, target, TokenAction.READ);
+ return stream.follow(token, targetToken, source, target, activityCopyLimit);
+ }
+
+ CompletableFuture getFollowers(FeedID feed, RequestOption... options) throws StreamException {
+ final Token token = buildFollowToken(secret, feed, TokenAction.READ);
+ return stream.getFollowers(token, feed, options);
+ }
+
+ CompletableFuture getFollowed(FeedID feed, RequestOption... options) throws StreamException {
+ final Token token = buildFollowToken(secret, feed, TokenAction.READ);
+ return stream.getFollowed(token, feed, options);
+ }
+
+ CompletableFuture unfollow(FeedID source, FeedID target, RequestOption... options) throws StreamException {
+ final Token token = buildFollowToken(secret, source, TokenAction.DELETE);
+ return stream.unfollow(token, source, target, options);
+ }
+
+ CompletableFuture updateActivityToTargets(FeedID feed, Activity activity, FeedID[] add, FeedID[] remove, FeedID[] newTargets) throws StreamException {
+ final Token token = buildToTargetUpdateToken(secret, feed, TokenAction.WRITE);
+ return stream.updateActivityToTargets(token, feed, activity, add, remove, newTargets);
+ }
+
+ CompletableFuture getUser(String id) throws StreamException {
+ final Token token = buildUsersToken(secret, TokenAction.READ);
+ return stream.getUser(token, id, false);
+ }
+
+ CompletableFuture deleteUser(String id) throws StreamException {
+ final Token token = buildUsersToken(secret, TokenAction.DELETE);
+ return stream.deleteUser(token, id);
+ }
+
+ CompletableFuture getOrCreateUser(String id, Data data) throws StreamException {
+ final Token token = buildUsersToken(secret, TokenAction.WRITE);
+ return stream.createUser(token, id, data, true);
+ }
+
+ CompletableFuture createUser(String id, Data data) throws StreamException {
+ final Token token = buildUsersToken(secret, TokenAction.WRITE);
+ return stream.createUser(token, id, data, false);
+ }
+
+ CompletableFuture updateUser(String id, Data data) throws StreamException {
+ final Token token = buildUsersToken(secret, TokenAction.WRITE);
+ return stream.updateUser(token, id, data);
+ }
+
+ CompletableFuture userProfile(String id) throws StreamException {
+ final Token token = buildUsersToken(secret, TokenAction.READ);
+ return stream.getUser(token, id, true);
+ }
+}
diff --git a/src/main/java/io/getstream/client/CollectionsClient.java b/src/main/java/io/getstream/client/CollectionsClient.java
new file mode 100644
index 00000000..aac154e9
--- /dev/null
+++ b/src/main/java/io/getstream/client/CollectionsClient.java
@@ -0,0 +1,139 @@
+package io.getstream.client;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Streams;
+import io.getstream.core.StreamCollections;
+import io.getstream.core.exceptions.StreamException;
+import io.getstream.core.http.Token;
+import io.getstream.core.models.CollectionData;
+import io.getstream.core.utils.Auth.TokenAction;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import static io.getstream.core.utils.Auth.buildCollectionsToken;
+import static io.getstream.core.utils.Serialization.convert;
+
+public final class CollectionsClient {
+ private final String secret;
+ private final StreamCollections collections;
+
+ CollectionsClient(String secret, StreamCollections collections) {
+ this.secret = secret;
+ this.collections = collections;
+ }
+
+ public CompletableFuture addCustom(String collection, T item) throws StreamException {
+ return addCustom(null, collection, item);
+ }
+
+ public CompletableFuture addCustom(String userID, String collection, T item) throws StreamException {
+ return add(userID, collection, convert(item, CollectionData.class))
+ .thenApply(data -> convert(data, (Class) item.getClass()));
+ }
+
+ public CompletableFuture add(String collection, CollectionData item) throws StreamException {
+ return add(null, collection, item);
+ }
+
+ public CompletableFuture add(String userID, String collection, CollectionData item) throws StreamException {
+ final Token token = buildCollectionsToken(secret, TokenAction.WRITE);
+ return collections.add(token, userID, collection, item);
+ }
+
+ public CompletableFuture updateCustom(String collection, T item) throws StreamException {
+ return updateCustom(null, collection, item);
+ }
+
+ public CompletableFuture updateCustom(String userID, String collection, T item) throws StreamException {
+ return update(userID, collection, convert(item, CollectionData.class))
+ .thenApply(data -> convert(data, (Class) item.getClass()));
+ }
+
+ public CompletableFuture update(String collection, CollectionData item) throws StreamException {
+ return update(null, collection, item);
+ }
+
+ public CompletableFuture update(String userID, String collection, CollectionData item) throws StreamException {
+ final Token token = buildCollectionsToken(secret, TokenAction.WRITE);
+ return collections.update(token, userID, collection, item);
+ }
+
+ public CompletableFuture upsertCustom(String collection, Iterable items) throws StreamException {
+ final CollectionData[] custom = Streams.stream(items)
+ .map(item -> CollectionData.buildFrom(item))
+ .toArray(CollectionData[]::new);
+ return upsert(collection, custom);
+ }
+
+ public CompletableFuture upsertCustom(String collection, T... items) throws StreamException {
+ final CollectionData[] custom = Arrays.stream(items)
+ .map(item -> CollectionData.buildFrom(item))
+ .toArray(CollectionData[]::new);
+ return upsert(collection, custom);
+ }
+
+ public CompletableFuture upsert(String collection, Iterable items) throws StreamException {
+ return upsert(collection, Iterables.toArray(items, CollectionData.class));
+ }
+
+ public CompletableFuture upsert(String collection, CollectionData... items) throws StreamException {
+ final Token token = buildCollectionsToken(secret, TokenAction.WRITE);
+ return collections.upsert(token, collection, items);
+ }
+
+ public CompletableFuture> customItems(Class type, String collection) throws StreamException {
+ return items(collection)
+ .thenApply(result -> result.stream()
+ .map(item -> convert(item, type))
+ .collect(Collectors.toList()));
+ }
+
+ public CompletableFuture> items(String collection) throws StreamException {
+ final Token token = buildCollectionsToken(secret, TokenAction.READ);
+ return collections.items(token, collection);
+ }
+
+ public CompletableFuture getCustom(Class type, String collection, String id) throws StreamException {
+ return get(collection, id).thenApply(data -> convert(data, type));
+ }
+
+ public CompletableFuture get(String collection, String id) throws StreamException {
+ final Token token = buildCollectionsToken(secret, TokenAction.READ);
+ return collections.get(token, collection, id);
+ }
+
+ public CompletableFuture> selectCustom(Class type, String collection, Iterable ids) throws StreamException {
+ return selectCustom(type, collection, Iterables.toArray(ids, String.class));
+ }
+
+ public CompletableFuture> selectCustom(Class type, String collection, String... ids) throws StreamException {
+ return select(collection, ids)
+ .thenApply(data -> data.stream().map(item -> convert(item, type)).collect(Collectors.toList()));
+ }
+
+ public CompletableFuture> select(String collection, Iterable ids) throws StreamException {
+ return select(collection, Iterables.toArray(ids, String.class));
+ }
+
+ public CompletableFuture> select(String collection, String... ids) throws StreamException {
+ final Token token = buildCollectionsToken(secret, TokenAction.READ);
+ return collections.select(token, collection, ids);
+ }
+
+ public CompletableFuture delete(String collection, String id) throws StreamException {
+ final Token token = buildCollectionsToken(secret, TokenAction.DELETE);
+ return collections.delete(token, collection, id);
+ }
+
+ public CompletableFuture deleteMany(String collection, Iterable ids) throws StreamException {
+ return deleteMany(collection, Iterables.toArray(ids, String.class));
+ }
+
+ public CompletableFuture deleteMany(String collection, String... ids) throws StreamException {
+ final Token token = buildCollectionsToken(secret, TokenAction.DELETE);
+ return collections.deleteMany(token, collection, ids);
+ }
+}
diff --git a/src/main/java/io/getstream/client/Feed.java b/src/main/java/io/getstream/client/Feed.java
new file mode 100644
index 00000000..3ab25599
--- /dev/null
+++ b/src/main/java/io/getstream/client/Feed.java
@@ -0,0 +1,278 @@
+package io.getstream.client;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Streams;
+import io.getstream.core.exceptions.StreamException;
+import io.getstream.core.models.Activity;
+import io.getstream.core.models.FeedID;
+import io.getstream.core.models.FollowRelation;
+import io.getstream.core.options.CustomQueryParameter;
+import io.getstream.core.options.Pagination;
+import io.getstream.core.options.RequestOption;
+import io.getstream.core.utils.DefaultOptions;
+
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static io.getstream.core.utils.Serialization.*;
+
+public class Feed {
+ private final Client client;
+ private final FeedID id;
+
+ Feed(Client client, FeedID id) {
+ checkNotNull(client, "Can't create feed w/o a client");
+ checkNotNull(id, "Can't create feed w/o an ID");
+
+ this.client = client;
+ this.id = id;
+ }
+
+ protected final Client getClient() {
+ return client;
+ }
+
+ public final FeedID getID() {
+ return id;
+ }
+
+ public final String getSlug() {
+ return id.getSlug();
+ }
+
+ public final String getUserID() {
+ return id.getUserID();
+ }
+
+ public final CompletableFuture addActivity(Activity activity) throws StreamException {
+ return getClient()
+ .addActivity(id, activity)
+ .thenApply(response -> {
+ try {
+ return deserialize(response, Activity.class);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public final CompletableFuture addCustomActivity(T activity) throws StreamException {
+ return getClient()
+ .addActivity(id, Activity.builder().fromCustomActivity(activity).build())
+ .thenApply(response -> {
+ try {
+ return deserialize(response, (Class) activity.getClass());
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public final CompletableFuture> addActivities(Iterable activities) throws StreamException {
+ return addActivities(Iterables.toArray(activities, Activity.class));
+ }
+
+ public final CompletableFuture> addCustomActivities(Iterable activities) throws StreamException {
+ final Activity[] custom = Streams.stream(activities)
+ .map(activity -> Activity.builder().fromCustomActivity(activity).build())
+ .toArray(Activity[]::new);
+ return getClient()
+ .addActivities(id, custom)
+ .thenApply(response -> {
+ try {
+ Class element = (Class) ((ParameterizedType) getClass().getGenericSuperclass()) .getActualTypeArguments()[0];
+ return deserializeContainer(response, "activities", element);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public final CompletableFuture> addActivities(Activity... activities) throws StreamException {
+ return getClient()
+ .addActivities(id, activities)
+ .thenApply(response -> {
+ try {
+ return deserializeContainer(response, "activities", Activity.class);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public final CompletableFuture> addCustomActivities(T... activities) throws StreamException {
+ final Activity[] custom = Arrays.stream(activities)
+ .map(activity -> Activity.builder().fromCustomActivity(activity).build())
+ .toArray(Activity[]::new);
+ return getClient()
+ .addActivities(id, custom)
+ .thenApply(response -> {
+ try {
+ Class element = (Class) activities.getClass().getComponentType();
+ return deserializeContainer(response, "activities", element);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public final CompletableFuture removeActivityByID(String id) throws StreamException {
+ return client
+ .removeActivityByID(this.id, id)
+ .thenApply(response -> {
+ try {
+ return deserializeError(response);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public final CompletableFuture removeActivityByForeignID(String foreignID) throws StreamException {
+ return client
+ .removeActivityByForeignID(id, foreignID)
+ .thenApply(response -> {
+ try {
+ return deserializeError(response);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public final CompletableFuture follow(FlatFeed feed) throws StreamException {
+ return follow(feed, DefaultOptions.DEFAULT_ACTIVITY_COPY_LIMIT);
+ }
+
+ public final CompletableFuture follow(FlatFeed feed, int activityCopyLimit) throws StreamException {
+ checkArgument(activityCopyLimit <= DefaultOptions.MAX_ACTIVITY_COPY_LIMIT, String.format("Activity copy limit should be less then %d", DefaultOptions.MAX_ACTIVITY_COPY_LIMIT));
+
+ return client
+ .follow(id, feed.getID(), activityCopyLimit)
+ .thenApply(response -> {
+ try {
+ return deserializeError(response);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public final CompletableFuture> getFollowers(Iterable feedIDs) throws StreamException {
+ return getFollowers(DefaultOptions.DEFAULT_PAGINATION, Iterables.toArray(feedIDs, FeedID.class));
+ }
+
+ public final CompletableFuture> getFollowers(FeedID... feedIDs) throws StreamException {
+ return getFollowers(DefaultOptions.DEFAULT_PAGINATION, feedIDs);
+ }
+
+ public final CompletableFuture> getFollowers(Pagination pagination, Iterable feedIDs) throws StreamException {
+ return getFollowers(pagination, Iterables.toArray(feedIDs, FeedID.class));
+ }
+
+ public final CompletableFuture> getFollowers(Pagination pagination, FeedID... feeds) throws StreamException {
+ checkNotNull(feeds, "No feed ids to filter on");
+
+ final String[] feedIDs = Arrays.stream(feeds)
+ .map(id -> id.toString())
+ .toArray(String[]::new);
+ final RequestOption[] options = feedIDs.length == 0
+ ? new RequestOption[] { pagination }
+ : new RequestOption[] { pagination, new CustomQueryParameter("filter", String.join(",", feedIDs)) };
+ return client
+ .getFollowers(id, options)
+ .thenApply(response -> {
+ try {
+ return deserializeContainer(response, FollowRelation.class);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public final CompletableFuture> getFollowed(Iterable feedIDs) throws StreamException {
+ return getFollowed(DefaultOptions.DEFAULT_PAGINATION, Iterables.toArray(feedIDs, FeedID.class));
+ }
+
+ public final CompletableFuture> getFollowed(FeedID... feedIDs) throws StreamException {
+ return getFollowed(DefaultOptions.DEFAULT_PAGINATION, feedIDs);
+ }
+
+ public final CompletableFuture> getFollowed(Pagination pagination, Iterable feedIDs) throws StreamException {
+ return getFollowed(pagination, Iterables.toArray(feedIDs, FeedID.class));
+ }
+
+ public final CompletableFuture> getFollowed(Pagination pagination, FeedID... feeds) throws StreamException {
+ checkNotNull(feeds, "No feed ids to filter on");
+
+ final String[] feedIDs = Arrays.stream(feeds)
+ .map(id -> id.toString())
+ .toArray(String[]::new);
+ final RequestOption[] options = feedIDs.length == 0
+ ? new RequestOption[] { pagination }
+ : new RequestOption[] { pagination, new CustomQueryParameter("filter", String.join(",", feedIDs)) };
+ return client
+ .getFollowed(id, options)
+ .thenApply(response -> {
+ try {
+ return deserializeContainer(response, FollowRelation.class);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public final CompletableFuture unfollow(FlatFeed feed) throws StreamException {
+ return unfollow(feed, io.getstream.core.KeepHistory.NO);
+ }
+
+ public final CompletableFuture unfollow(FlatFeed feed, io.getstream.core.KeepHistory keepHistory) throws StreamException {
+ return client
+ .unfollow(id, feed.getID(), new io.getstream.core.options.KeepHistory(keepHistory))
+ .thenApply(response -> {
+ try {
+ return deserializeError(response);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public final CompletableFuture updateActivityToTargets(Activity activity, Iterable add, Iterable remove) throws StreamException {
+ return updateActivityToTargets(activity, Iterables.toArray(add, FeedID.class), Iterables.toArray(remove, FeedID.class));
+ }
+
+ public final CompletableFuture updateActivityToTargets(Activity activity, FeedID[] add, FeedID[] remove) throws StreamException {
+ return client
+ .updateActivityToTargets(id, activity, add, remove, new FeedID[0])
+ .thenApply(response -> {
+ try {
+ return deserializeError(response);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public final CompletableFuture replaceActivityToTargets(Activity activity, Iterable newTargets) throws StreamException {
+ return replaceActivityToTargets(activity, Iterables.toArray(newTargets, FeedID.class));
+ }
+
+ public final CompletableFuture replaceActivityToTargets(Activity activity, FeedID... newTargets) throws StreamException {
+ return client
+ .updateActivityToTargets(id, activity, new FeedID[0], new FeedID[0], newTargets)
+ .thenApply(response -> {
+ try {
+ return deserializeError(response);
+ } catch (StreamException | IOException e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+}
diff --git a/src/main/java/io/getstream/client/FileStorageClient.java b/src/main/java/io/getstream/client/FileStorageClient.java
new file mode 100644
index 00000000..b7696b22
--- /dev/null
+++ b/src/main/java/io/getstream/client/FileStorageClient.java
@@ -0,0 +1,37 @@
+package io.getstream.client;
+
+import io.getstream.core.StreamFiles;
+import io.getstream.core.exceptions.StreamException;
+import io.getstream.core.http.Token;
+import io.getstream.core.utils.Auth.TokenAction;
+
+import java.io.File;
+import java.net.URL;
+import java.util.concurrent.CompletableFuture;
+
+import static io.getstream.core.utils.Auth.buildFilesToken;
+
+public final class FileStorageClient {
+ private final String secret;
+ private final StreamFiles files;
+
+ FileStorageClient(String secret, StreamFiles files) {
+ this.secret = secret;
+ this.files = files;
+ }
+
+ public CompletableFuture upload(String fileName, byte[] content) throws StreamException {
+ final Token token = buildFilesToken(secret, TokenAction.WRITE);
+ return files.upload(token, fileName, content);
+ }
+
+ public CompletableFuture upload(File content) throws StreamException {
+ final Token token = buildFilesToken(secret, TokenAction.WRITE);
+ return files.upload(token, content);
+ }
+
+ public CompletableFuture delete(URL url) throws StreamException {
+ final Token token = buildFilesToken(secret, TokenAction.DELETE);
+ return files.delete(token, url);
+ }
+}
diff --git a/src/main/java/io/getstream/client/FlatFeed.java b/src/main/java/io/getstream/client/FlatFeed.java
new file mode 100644
index 00000000..7ed5ac70
--- /dev/null
+++ b/src/main/java/io/getstream/client/FlatFeed.java
@@ -0,0 +1,225 @@
+package io.getstream.client;
+
+import io.getstream.core.exceptions.StreamException;
+import io.getstream.core.models.Activity;
+import io.getstream.core.models.EnrichedActivity;
+import io.getstream.core.models.FeedID;
+import io.getstream.core.options.*;
+import io.getstream.core.utils.DefaultOptions;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+import static io.getstream.core.utils.Serialization.deserializeContainer;
+
+public final class FlatFeed extends Feed {
+ FlatFeed(Client client, FeedID id) {
+ super(client, id);
+ }
+
+ public CompletableFuture> getActivities() throws StreamException {
+ return getActivities(DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, null);
+ }
+
+ public CompletableFuture> getActivities(String ranking) throws StreamException {
+ return getActivities(DefaultOptions.DEFAULT_PAGINATION, DefaultOptions.DEFAULT_FILTER, ranking);
+ }
+
+ public CompletableFuture> getActivities(Filter filter) throws StreamException {
+ return getActivities(DefaultOptions.DEFAULT_PAGINATION, filter, null);
+ }
+
+ public CompletableFuture> getActivities(Pagination pagination) throws StreamException {
+ return getActivities(pagination, DefaultOptions.DEFAULT_FILTER, null);
+ }
+
+ public CompletableFuture