diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000..b2707f61
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,2 @@
+* @xernobyl
+* @JcMinarro
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..7bcc6037
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,41 @@
+name: build
+
+on: [pull_request]
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: 🧪 Test & lint
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+
+ - name: Commit message lint
+ uses: wagoid/commitlint-github-action@v4
+
+ - name: Restore cache
+ uses: actions/cache@v3
+ with:
+ path: ~/.gradle/caches
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*') }}
+ restore-keys: |
+ ${{ runner.os }}-gradle-
+
+ - name: Test
+ env:
+ STREAM_KEY: ${{ secrets.STREAM_KEY }}
+ STREAM_SECRET: ${{ secrets.STREAM_SECRET }}
+ STREAM_APP_ID: ${{ secrets.STREAM_APP_ID }}
+ run: |
+ ./gradlew spotlessCheck --no-daemon
+ ./gradlew test --no-daemon
diff --git a/.github/workflows/initiate_release.yml b/.github/workflows/initiate_release.yml
new file mode 100644
index 00000000..59c1c98c
--- /dev/null
+++ b/.github/workflows/initiate_release.yml
@@ -0,0 +1,51 @@
+name: Create release PR
+
+on:
+ workflow_dispatch:
+ inputs:
+ version:
+ description: "The new version number with a 'v' prefix. Example: v1.40.1"
+ required: true
+
+jobs:
+ init_release:
+ name: 🚀 Create release PR
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0 # gives the changelog generator access to all previous commits
+ - uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+
+ - name: Update CHANGELOG.md, build.gradle and push release branch
+ env:
+ VERSION: ${{ github.event.inputs.version }}
+ run: |
+ npx --yes standard-version@9.3.2 --release-as "$VERSION" --skip.tag --skip.commit --tag-prefix=v
+ git config --global user.name 'github-actions'
+ git config --global user.email 'release@getstream.io'
+ git checkout -q -b "release-$VERSION"
+ git commit -am "chore(release): $VERSION"
+ git push -q -u origin "release-$VERSION"
+
+ - name: Get changelog diff
+ uses: actions/github-script@v5
+ with:
+ script: |
+ const get_change_log_diff = require('./scripts/get_changelog_diff.js')
+ core.exportVariable('CHANGELOG', get_change_log_diff())
+
+ - name: Open pull request
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ gh pr create \
+ -t "Release ${{ github.event.inputs.version }}" \
+ -b "# :rocket: ${{ github.event.inputs.version }}
+ Make sure to use squash & merge when merging!
+ Once this is merged, another job will kick off automatically and publish the package.
+ # :memo: Changelog
+ ${{ env.CHANGELOG }}"
diff --git a/.github/workflows/javadoc.yml b/.github/workflows/javadoc.yml
new file mode 100644
index 00000000..30938502
--- /dev/null
+++ b/.github/workflows/javadoc.yml
@@ -0,0 +1,29 @@
+name: javadoc
+on:
+ push:
+ branches:
+ - main
+jobs:
+ javadoc:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ persist-credentials: false
+ - uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+ - name: Set up Node.js 16
+ uses: actions/setup-node@v2
+ with:
+ node-version: 16
+ - name: Generate doc
+ run: ./gradlew --no-daemon javadoc
+ - name: Deploy
+ uses: JamesIves/github-pages-deploy-action@releases/v3
+ with:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ BRANCH: gh-pages
+ FOLDER: build/docs/javadoc/
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..da5eba72
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,57 @@
+name: Release
+
+on:
+ pull_request:
+ types: [closed]
+ branches:
+ - main
+
+jobs:
+ Release:
+ name: 🚀 Release
+ if: github.event.pull_request.merged && startsWith(github.head_ref, 'release-')
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+ - uses: actions/github-script@v5
+ with:
+ script: |
+ const get_change_log_diff = require('./scripts/get_changelog_diff.js')
+ core.exportVariable('CHANGELOG', get_change_log_diff())
+
+ // Getting the release version from the PR source branch
+ // Source branch looks like this: release-1.0.0
+ const version = context.payload.pull_request.head.ref.split('-')[1]
+ core.exportVariable('VERSION', version)
+
+ - name: Build artifacts
+ run: ./gradlew jar javadocJar sourcesJar
+
+ - name: Publish to MavenCentral
+ run: |
+ sudo bash -c "echo '$GPG_KEY_CONTENTS' | base64 -d > '$SIGNING_SECRET_KEY_RING_FILE'"
+ ./gradlew publishReleasePublicationToSonatypeRepository --max-workers 1 closeAndReleaseSonatypeStagingRepository
+ env:
+ STREAM_KEY: ${{ secrets.STREAM_KEY }}
+ STREAM_SECRET: ${{ secrets.STREAM_SECRET }}
+ STREAM_APP_ID: ${{ secrets.STREAM_APP_ID }}
+ GPG_KEY_CONTENTS: ${{ secrets.GPG_KEY_CONTENTS }}
+ OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
+ OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
+ SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
+ SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
+ SIGNING_SECRET_KEY_RING_FILE: ${{ secrets.SIGNING_SECRET_KEY_RING_FILE }}
+ SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}
+
+ - name: Create release on GitHub
+ uses: ncipollo/release-action@v1
+ with:
+ body: ${{ env.CHANGELOG }}
+ tag: ${{ env.VERSION }}
+ token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index cd968238..f5614bdd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,25 +1,22 @@
*.class
-.idea
-*.iml
-
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
-
-# Package Files #
*.jar
*.war
*.ear
+build/
-# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
-hs_err_pid*
-
-# Maven
+.gradle/
+gradle.properties
+gradle/*
+!gradle/wrapper
+!gradle/wrapper/*.jar
+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
+bin/
+.classpath
+.project
+.settings/
+
+.idea/
+.vscode/
+.envrc
+/local.properties
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 32a3b469..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-language: java
-jdk:
-- oraclejdk8
-env:
- global:
- - secure: TmI+K6gSOfDy5oa5EgLCGijimKdR5cHAwM6RASDegTjlRQ/sWVf8BNF4IPJHnAYFWNCgyX6vLdfPS3IJNrYS9iI1Er2a1Ctcq1TbCZfZpn4eIj8mcUXxr2Fo6hLJh2KeuXj3OQwfUy/Jul1Own/9R5YoVDJYD0ntnt1HJgFqq+0=
- - secure: DethMgIykOufLLQn9Jykmg7rdvZp4ONXD4A1XVQNaEVQytv20Fb2QidEPmVEocuDmpmU6SjWNMhAeBdlWnyGB4lECMG9594HJpSnNkyFQzzsfSWdOwHixiUCuD+rMJlEPRJDM4ayxhilPnK1gkycKBAZ1gtutriC4O/BdEOCN6A=
- - secure: F6jGxeoyhD1tmC0ovRW2RqeEQDN5t5Kfqktmtcj0Lz3S4ddgzMwC6RYKyYiiu9T3FSfh/hEMrx2SrMK+Neu2ctfYflNibUjgZJVPZHR7DA3S+g30teyj10XWspb6+OLC7U0DnGbdLee/w0KkuugQHYNv1aM9oEPtLWi+3VpvIIU=
-after_success:
-- ".travis/publish.sh"
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/.versionrc.js b/.versionrc.js
new file mode 100644
index 00000000..2367c93c
--- /dev/null
+++ b/.versionrc.js
@@ -0,0 +1,16 @@
+const gradleUpdater = {
+ VERSION_REGEX: /version = '(.+)'/,
+
+ readVersion: function (contents) {
+ const version = this.VERSION_REGEX.exec(contents)[1];
+ return version;
+ },
+
+ writeVersion: function (contents, version) {
+ return contents.replace(this.VERSION_REGEX.exec(contents)[0], `version = '${version}'`);
+ }
+}
+
+module.exports = {
+ bumpFiles: [{ filename: './build.gradle', updater: gradleUpdater }],
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..31eb8b43
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,130 @@
+# Changelog
+
+All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+## [3.25.0](https://github.com/GetStream/stream-java/compare/v3.24.0...v3.25.0) (2026-01-16)
+
+## [3.24.0](https://github.com/GetStream/stream-java/compare/v3.23.0...v3.24.0) (2025-11-27)
+
+## [3.23.0](https://github.com/GetStream/stream-java/compare/v3.22.0...v3.23.0) (2025-10-03)
+
+## [3.22.0](https://github.com/GetStream/stream-java/compare/v3.20.0...v3.22.0) (2025-07-09)
+
+## [3.21.0](https://github.com/GetStream/stream-java/compare/v3.20.0...v3.21.0) (2025-07-02)
+
+## [3.20.0](https://github.com/GetStream/stream-java/compare/v3.19.0...v3.20.0) (2025-04-17)
+
+## [3.19.0](https://github.com/GetStream/stream-java/compare/v3.17.1...v3.19.0) (2025-04-14)
+
+### [3.17.1](https://github.com/GetStream/stream-java/compare/v3.17.0...v3.17.1) (2025-04-11)
+
+
+### Bug Fixes
+
+* update reaction should support moderation template ([08b282b](https://github.com/GetStream/stream-java/commit/08b282b64170961e3b2cc25f4cc434674651bc84))
+
+## [3.17.0](https://github.com/GetStream/stream-java/compare/v3.16.0...v3.17.0) (2025-03-21)
+
+## [3.16.0](https://github.com/GetStream/stream-java/compare/v3.15.2...v3.16.0) (2025-03-10)
+
+### [3.15.2](https://github.com/GetStream/stream-java/compare/v3.15.1...v3.15.2) (2025-03-07)
+
+
+### Bug Fixes
+
+* moderation response ([ec7db00](https://github.com/GetStream/stream-java/commit/ec7db0040062c2af6c9db9b3337d240594abf9b4))
+
+### [3.15.1](https://github.com/GetStream/stream-java/compare/v3.15.0...v3.15.1) (2024-12-27)
+
+
+### Bug Fixes
+
+* pass custom var to request ([99ea58e](https://github.com/GetStream/stream-java/commit/99ea58ef057d7a5eaa1f20aaceb6b28b819e825e))
+
+## [3.15.0](https://github.com/GetStream/stream-java/compare/v3.14.0...v3.15.0) (2024-11-21)
+
+## [3.14.0](https://github.com/GetStream/stream-java/compare/v3.13.0...v3.14.0) (2024-11-11)
+
+## [3.13.0](https://github.com/GetStream/stream-java/compare/v3.12.0...v3.13.0) (2024-11-06)
+
+## [3.12.0](https://github.com/GetStream/stream-java/compare/v3.11.0...v3.12.0) (2024-11-05)
+
+
+### Bug Fixes
+
+* fix tests ([4ae4d24](https://github.com/GetStream/stream-java/commit/4ae4d2434c81334c82115fd610caf507ce32ac5c))
+* fix tests, fix token moderation ([f4f6596](https://github.com/GetStream/stream-java/commit/f4f6596c240349c67f66094ccd5117a7fd3d60e6))
+* fix tests, fix token moderation ([1ea89d9](https://github.com/GetStream/stream-java/commit/1ea89d90e19c773dd120d96883d8bd08252e6d86))
+
+## [3.11.0](https://github.com/GetStream/stream-java/compare/v3.10.0...v3.11.0) (2024-10-08)
+
+## [3.10.0](https://github.com/GetStream/stream-java/compare/v3.9.2...v3.10.0) (2024-03-12)
+
+### [3.9.2](https://github.com/GetStream/stream-java/compare/v3.9.1...v3.9.2) (2024-02-19)
+* Ranking Variables in the Enriched Activities Endpoint
+
+### [3.9.1](https://github.com/GetStream/stream-java/compare/v3.8.2...v3.9.1) (2024-01-09)
+
+### [3.8.2](https://github.com/GetStream/stream-java/compare/v3.8.1...v3.8.2) (2024-01-03)
+
+### [3.8.1](https://github.com/GetStream/stream-java/compare/v3.8.0...v3.8.1) (2023-11-17)
+
+## [3.8.0](https://github.com/GetStream/stream-java/compare/v3.7.0...v3.8.0) (2023-10-23)
+* Added support to force refresh
+* Added support for soft deletions
+
+## [3.7.0](https://github.com/GetStream/stream-java/compare/v3.6.2...v3.7.0) (2023-08-16)
+
+
+### Features
+
+* add capability in batch client to get enrichment activities to use enrichment flags ([cfcc86a](https://github.com/GetStream/stream-java/commit/cfcc86ae3b62fd16cbf733912e7b484a91bb7d8b))
+
+
+### Bug Fixes
+
+* spotless ([dad14c7](https://github.com/GetStream/stream-java/commit/dad14c7abedd6ca6d9cef7ea4c1a0133cb648e72))
+
+### [3.6.2](https://github.com/GetStream/stream-java/compare/v3.5.0...v3.6.2) (2023-01-26)
+
+
+### Features
+
+* **faye:** add websocket client ([#110](https://github.com/GetStream/stream-java/issues/110)) ([c0b29e5](https://github.com/GetStream/stream-java/commit/c0b29e51708e424f44686e20d9b2b426da661b4c))
+
+### [3.6.1](https://github.com/GetStream/stream-java/compare/v3.5.0...v3.6.1) (2023-01-26)
+
+
+### Features
+
+* **faye:** add websocket client ([#110](https://github.com/GetStream/stream-java/issues/110)) ([c0b29e5](https://github.com/GetStream/stream-java/commit/c0b29e51708e424f44686e20d9b2b426da661b4c))
+
+## [3.6.0](https://github.com/GetStream/stream-java/compare/v3.5.0...v3.6.0) (2022-05-26)
+
+
+### Features
+
+* **faye:** add websocket client ([#110](https://github.com/GetStream/stream-java/issues/110)) ([c0b29e5](https://github.com/GetStream/stream-java/commit/c0b29e51708e424f44686e20d9b2b426da661b4c))
+
+## [3.5.0](https://github.com/GetStream/stream-java/compare/v3.4.1...v3.5.0) (2022-04-26)
+
+
+### Features
+
+* **stats:** add followstats ([#108](https://github.com/GetStream/stream-java/issues/108)) ([56afa90](https://github.com/GetStream/stream-java/commit/56afa9098d6d21eac5e6c0b75975b32c6684358b))
+
+## [3.4.1] - 2022-03-22
+
+- Fix unmarshal of empty custom data
+
+## [3.4.0] - 2022-02-10
+
+- Add unread/unseed counts into notification feed payloads
+
+## [3.3.0] - 2022-01-07
+
+- Relax id checks for custom data in collections
+- Add withOwnChildren support into reaction filtering
+- Move to GitHub actions and add release automation
+- Add proguard notes into readme and bump some deps due to security notices
+- Add changelog
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..3655d4e0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2016-2021, Stream.io Inc, and individual contributors.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted
+provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ 3. Neither the name of the copyright holder nor the names of its contributors may
+ be used to endorse or promote products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+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.
diff --git a/README.md b/README.md
index ddbee397..cd5b41d4 100644
--- a/README.md
+++ b/README.md
@@ -1,101 +1,92 @@
-stream-java
-===========
-[](https://travis-ci.org/GetStream/stream-java)
+# Official Java SDK for [Stream Feeds](https://getstream.io/activity-feeds/)
-stream-java is a Java client for [Stream](https://getstream.io/).
+[](https://github.com/GetStream/stream-java/actions)
+
+
+
+
+ Official Java API client for Stream Feeds, a web service for building scalable newsfeeds and activity streams.
+
+ Explore the docs »
+
+
+ JavaDoc
+ ·
+ Report Bug
+ ·
+ Request Feature
+
-### Installation
+## 📝 About Stream
-Download the latest JAR or grab via Maven:
+You can sign up for a Stream account at our [Get Started](https://getstream.io/activity-feeds/docs/java/?language=java) page.
+
+You can use this library to access feeds API endpoints server-side.
+
+For the client-side integrations (web and mobile) have a look at the JavaScript, iOS and Android SDK libraries ([docs](https://getstream.io/activity-feeds/)).
+
+> 💡 Note: this is a library for the **Feeds** product. The Chat SDKs can be found [here](https://getstream.io/chat/docs/).
+
+## ⚙️ Installation
+
+Add the following dependency to your `pom.xml`:
```xml
io.getstream.client
- stream-repo-okhttp
- 0.1-RC2
+ stream-java
+ ${stream_version}
```
-Snapshots of the development version are available in Sonatype's snapshots repository.
+or in your `build.gradle`:
-### Usage
-
-```java
-// Instantiate a new client to connect to us east API endpoint
-// Find your API keys here https://getstream.io/dashboard/
+```gradle
+implementation 'io.getstream.client:stream-java:$stream_version'
+```
-ClientConfiguration streamConfig = new ClientConfiguration().setRegion(StreamRegion.US_EAST);
-StreamClient streamClient = new StreamClientImpl(streamConfig, 'API_KEY', 'API_SECRET');
+In case you want to download the artifact and put it manually into your project,
+you can download it from [here](https://github.com/GetStream/stream-java/releases).
-// Instantiate a feed object
-Feed feed = streamClient.newFeed("user", "1");
+Snapshots of the development version are available in [Sonatype](https://oss.sonatype.org/content/repositories/snapshots/io/getstream/client/) snapshots repository.
-// Create an activity service
-FlatActivityServiceImpl flatActivityService = feed.newFlatActivityService(SimpleActivity.class);
+> 💡This API Client project requires Java SE 7.
-// 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();
+## 🙋 FAQ
-// 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();
+1. Is Android supported?
-// 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);
-```
+Yes. Use `client` for your backend and use `CloudClient` for your mobile application.
-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)
+2. Cannot construct an instance of `io.getstream.core.models.*`, a model object in android. What is the problem?
-```java
-// Remove an activity by its id
-feed.deleteActivity("e561de8f-00f1-11e4-b400-0cc47a024be0");
+If you're using proguard, ensure having following: `-keep class io.getstream.core.models.** { *; }`
-// Remove activities by their foreign_id
-feed.deleteActivityByForeignId("tweet:1");
+Additionally, we're using Jackson JSON processor and see [their definitions](https://github.com/FasterXML/jackson-docs/wiki/JacksonOnAndroid) too unless you're already using it.
-// Follow another feed
-feed.follow(flat", "42");
+## 📚 Full documentation
-// Stop following another feed
-feed.unfollow(flat", "42");
+Documentation for this Java client are available at the [Stream website](https://getstream.io/docs/?language=java).
-// Batch adding activities
-// This is not supported yet
+For examples have a look [here](./example/Example.java).
-// Batch following many feeds
-// This is not supported yet
+Docs are available on [GetStream.io](https://getstream.io/docs/?language=java).
-// Add an activity and push it to other feeds too using the `to` field
-// This is not supported yet
+JavaDoc is available [here](https://getstream.github.io/stream-java/).
-// Remove a feed and its content
-// This is not supported yet
+## 🧪 Building & Testing
-// Generating tokens for client side usage
-String token = feed.getToken();
+Run `gradlew test` to execute integration tests
-// Javascript client side feed initialization
-// user1 = client.feed('user', '1', '{{ token }}');
-// Retrieve first 10 followers of a feed
-FeedFilter filter = new FeedFilter.Builder().withLimit(10).build();
-List followingPaged = feed.getFollowing(filter);
+## ✍️ Contributing
-// Retrieve the first 10 followed feeds
-FeedFilter filter = new FeedFilter.Builder().withLimit(10).build();
-List followingPaged = feed.getFollowing(filter);
+We welcome code changes that improve this library or fix a problem, please make sure to follow all best practices and add tests if applicable before submitting a Pull Request on Github. We are very happy to merge your code in the official repository. Make sure to sign our [Contributor License Agreement (CLA)](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) first. See our [license file](./LICENSE) for more details.
-// Check if specific feeds are followed
-// This is not supported yet
+## 🧑💻 We are hiring!
-```
+We've recently closed a [$38 million Series B funding round](https://techcrunch.com/2021/03/04/stream-raises-38m-as-its-chat-and-activity-feed-apis-power-communications-for-1b-users/) and we keep actively growing.
+Our APIs are used by more than a billion end-users, and you'll have a chance to make a huge impact on the product within a team of the strongest engineers all over the world.
-Docs are available on [GetStream.io](http://getstream.io/docs/).
+Check out our current openings and apply via [Stream's website](https://getstream.io/team/#jobs).
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..4094801e
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,16 @@
+# Reporting a Vulnerability
+At Stream we are committed to the security of our Software. We appreciate your efforts in disclosing vulnerabilities responsibly and we will make every effort to acknowledge your contributions.
+
+Report security vulnerabilities at the following email address:
+```
+[security@getstream.io](mailto:security@getstream.io)
+```
+Alternatively it is also possible to open a new issue in the affected repository, tagging it with the `security` tag.
+
+A team member will acknowledge the vulnerability and will follow-up with more detailed information. A representative of the security team will be in touch if more information is needed.
+
+# Information to include in a report
+While we appreciate any information that you are willing to provide, please make sure to include the following:
+* Which repository is affected
+* Which branch, if relevant
+* Be as descriptive as possible, the team will replicate the vulnerability before working on a fix.
diff --git a/assets/logo.svg b/assets/logo.svg
new file mode 100644
index 00000000..1c68c5cc
--- /dev/null
+++ b/assets/logo.svg
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000..1b6fd533
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,89 @@
+plugins {
+ id 'java-library'
+ id 'io.github.gradle-nexus.publish-plugin' version '1.1.0'
+ id 'com.diffplug.spotless' version '5.14.0'
+}
+
+group 'io.getstream.client'
+version = '3.25.0'
+description = 'Stream Feeds official Java SDK'
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+ maven { url "https://plugins.gradle.org/m2/" }
+ maven { url uri('https://repo.maven.apache.org/maven2/') }
+}
+
+var jackson_version = '2.14.2'
+
+dependencies {
+ java {
+ sourceCompatibility = 1.8
+ targetCompatibility = 1.8
+ }
+
+ testImplementation 'junit:junit:4.13.1'
+ testImplementation 'com.pholser:junit-quickcheck-core:0.8.1'
+ testImplementation 'com.pholser:junit-quickcheck-generators:0.8.1'
+ testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.2.0'
+
+ implementation 'com.google.guava:guava:31.1-jre'
+ implementation 'com.squareup.okhttp3:okhttp:4.10.0'
+ implementation "com.fasterxml.jackson.core:jackson-core:$jackson_version"
+ implementation "com.fasterxml.jackson.core:jackson-core:$jackson_version"
+ implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
+ implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
+ implementation 'com.auth0:java-jwt:4.2.2'
+
+ api 'net.sourceforge.streamsupport:streamsupport:1.7.0'
+ api 'net.sourceforge.streamsupport:streamsupport-cfuture:1.7.0'
+}
+
+def localProperties = new Properties()
+def localPropertiesFile = project.rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ FileInputStream stream = new FileInputStream(localPropertiesFile)
+ localProperties.load(stream)
+}
+
+test {
+ useJUnitPlatform()
+
+ testLogging {
+ exceptionFormat = 'full'
+ events 'standard_out', 'standard_error', "passed", "skipped", "failed"
+ }
+
+ doFirst {
+ // Inject local properties into tests runtime system properties
+ for (String key : localProperties.stringPropertyNames) {
+ systemProperty key, localProperties.getProperty(key).toString()
+ }
+ }
+}
+
+def generatedVersionDir = "${buildDir}/generated-version"
+
+sourceSets {
+ main {
+ output.dir(generatedVersionDir, builtBy: 'generateVersionProperties')
+ }
+}
+/*spotless {
+ java {
+ googleJavaFormat()
+ }
+}*/
+task generateVersionProperties {
+ doLast {
+ def propertiesFile = file "$generatedVersionDir/version.properties"
+ propertiesFile.parentFile.mkdirs()
+ def properties = new Properties()
+ properties.setProperty("version", rootProject.version.toString())
+ propertiesFile.withWriter { properties.store(it, null) }
+ }
+}
+processResources.dependsOn generateVersionProperties
+
+apply from: "publish.gradle"
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..772aad5b
--- /dev/null
+++ b/example/Example.java
@@ -0,0 +1,523 @@
+package example;
+
+import static io.getstream.core.utils.Enrichment.createCollectionReference;
+import static io.getstream.core.utils.Enrichment.createUserReference;
+
+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;
+
+class Example {
+ private static final String apiKey = System.getenv("STREAM_KEY") != null ? System.getenv("STREAM_KEY")
+ : System.getProperty("STREAM_KEY");
+ private static final String secret = System.getenv("STREAM_SECRET") != null ? System.getenv("STREAM_SECRET")
+ : System.getProperty("STREAM_SECRET");
+
+ 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);
+
+ // Get activities sorted by the ranking method along with externalRankingVars
+ Map mp=new LinkedHashMap();
+
+ mp.put("boolVal",true);
+ mp.put("music",1);
+ mp.put("sports",2.1);
+ mp.put("string","str");
+ response = userFeed.feed.getActivities(
+ new Limit(69),
+ new Offset(13),
+ DefaultOptions.DEFAULT_FILTER,
+ "rank",
+ new RankingVars(mp)
+ );
+ /* -------------------------------------------------------- */
+
+ // 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:1", "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/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..033e24c4
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 00000000..9f4197d5
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 00000000..fcb6fca1
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,248 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ 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
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 00000000..6689b85b
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+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 execute
+
+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
+
+: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 %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 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!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/local.properties.example b/local.properties.example
new file mode 100644
index 00000000..fdfc7f1f
--- /dev/null
+++ b/local.properties.example
@@ -0,0 +1,6 @@
+ossrhUsername=
+ossrhPassword=
+signing.keyId=
+signing.password=
+signing.secretKeyRingFile=
+sonatypeStagingProfileId=
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 3cf263c7..00000000
--- a/pom.xml
+++ /dev/null
@@ -1,216 +0,0 @@
-
-
- 4.0.0
-
- io.getstream.client
- stream-java
- pom
- 0.1-RC6-SNAPSHOT
-
- stream-java
- stream-java is a Java client for http://getstream.io.
- https://github.com/GetStream/stream-java
-
-
-
- The New BSD License
- http://www.opensource.org/licenses/bsd-license.html
-
-
- 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
- 1.6.6
- 1.1.2
- 18.0
- 2.4.3
- 1.0
- 4.11
-
-
-
-
-
- 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}
-
-
-
-
- junit
- junit
- ${junit.version}
- test
-
-
-
-
-
-
- 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.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
-
-
-
-
-
-
- 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/publish.gradle b/publish.gradle
new file mode 100644
index 00000000..1d7563bf
--- /dev/null
+++ b/publish.gradle
@@ -0,0 +1,95 @@
+apply plugin: 'maven-publish'
+apply plugin: 'signing'
+
+// Create variables with empty default values
+ext["ossrhUsername"] = ''
+ext["ossrhPassword"] = ''
+ext["signing.keyId"] = ''
+ext["signing.password"] = ''
+ext["signing.secretKeyRingFile"] = ''
+ext["sonatypeStagingProfileId"] = ''
+
+File secretPropsFile = project.rootProject.file('local.properties')
+if (secretPropsFile.exists()) {
+ // Read local.properties file first if it exists
+ Properties p = new Properties()
+ new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) }
+ p.each { name, value -> ext[name] = value }
+} else {
+ // Use system environment variables
+ ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME')
+ ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD')
+ ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID')
+ ext["signing.password"] = System.getenv('SIGNING_PASSWORD')
+ ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE')
+ ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID')
+}
+
+nexusPublishing {
+ repositories {
+ sonatype {
+ nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
+ snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/"))
+ stagingProfileId = sonatypeStagingProfileId
+ username = ossrhUsername
+ password = ossrhPassword
+ }
+ }
+}
+
+task javadocJar(type: Jar) {
+ archiveClassifier = 'javadoc'
+ from javadoc
+}
+
+task sourcesJar(type: Jar) {
+ archiveClassifier = 'sources'
+ from sourceSets.main.allSource
+}
+
+artifacts {
+ archives javadocJar, sourcesJar
+}
+
+afterEvaluate {
+ publishing {
+ publications {
+ release(MavenPublication) {
+ from components.java
+ artifactId 'stream-java'
+
+ artifact sourcesJar
+ artifact javadocJar
+
+ pom {
+ name = "Stream Feeds official Java API Client"
+ description = "Stream Feeds Java Client for backend and android integrations"
+ url = 'https://github.com/getstream/stream-chat-java'
+ licenses {
+ license {
+ name = 'The 3-Clause BSD License'
+ url = 'https://opensource.org/licenses/BSD-3-Clause'
+ distribution = 'repo'
+ }
+ }
+ developers {
+ developer {
+ id = 'getstream-support'
+ name = 'Stream Support'
+ email = 'support@getstream.io'
+ }
+ }
+ scm {
+ connection = 'scm:git:github.com/getstream/stream-java.git'
+ developerConnection = 'scm:git:ssh://github.com/getstream/stream-java.git'
+ url = 'https://github.com/getstream/stream-java'
+ }
+ }
+ }
+ }
+ }
+}
+
+signing {
+ sign publishing.publications
+}
diff --git a/scripts/get_changelog_diff.js b/scripts/get_changelog_diff.js
new file mode 100644
index 00000000..ce034389
--- /dev/null
+++ b/scripts/get_changelog_diff.js
@@ -0,0 +1,26 @@
+/*
+Here we're trying to parse the latest changes from CHANGELOG.md file.
+The changelog looks like this:
+
+## 0.0.3
+- Something #3
+## 0.0.2
+- Something #2
+## 0.0.1
+- Something #1
+
+In this case we're trying to extract "- Something #3" since that's the latest change.
+*/
+module.exports = () => {
+ const fs = require('fs')
+
+ changelog = fs.readFileSync('CHANGELOG.md', 'utf8')
+ releases = changelog.match(/## [?[0-9](.+)/g)
+
+ current_release = changelog.indexOf(releases[0])
+ previous_release = changelog.indexOf(releases[1])
+
+ latest_changes = changelog.substr(current_release, previous_release - current_release)
+
+ return latest_changes
+}
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 00000000..d320843b
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,5 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ */
+
+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..107e7229
--- /dev/null
+++ b/src/main/java/io/getstream/client/AggregatedFeed.java
@@ -0,0 +1,731 @@
+package io.getstream.client;
+
+import static io.getstream.core.utils.Serialization.deserializeContainer;
+
+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.*;
+import io.getstream.core.utils.DefaultOptions;
+import java.io.IOException;
+import java.util.List;
+import java8.util.concurrent.CompletableFuture;
+import java8.util.concurrent.CompletionException;
+
+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_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(Limit limit)
+ throws StreamException {
+ return getActivities(
+ limit,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(Offset offset)
+ throws StreamException {
+ return getActivities(
+ DefaultOptions.DEFAULT_LIMIT,
+ offset,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(Filter filter)
+ throws StreamException {
+ return getActivities(
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ filter,
+ DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(
+ ActivityMarker marker) throws StreamException {
+ return getActivities(
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(
+ Limit limit, Offset offset) throws StreamException {
+ return getActivities(
+ limit, offset, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(
+ Limit limit, Filter filter) throws StreamException {
+ return getActivities(
+ limit, DefaultOptions.DEFAULT_OFFSET, filter, DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(
+ Limit limit, ActivityMarker marker) throws StreamException {
+ return getActivities(
+ limit, DefaultOptions.DEFAULT_OFFSET, DefaultOptions.DEFAULT_FILTER, marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(
+ Filter filter, ActivityMarker marker) throws StreamException {
+ return getActivities(
+ DefaultOptions.DEFAULT_LIMIT, DefaultOptions.DEFAULT_OFFSET, filter, marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(
+ Offset offset, ActivityMarker marker) throws StreamException {
+ return getActivities(
+ DefaultOptions.DEFAULT_LIMIT, offset, DefaultOptions.DEFAULT_FILTER, marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(
+ Limit limit, Filter filter, ActivityMarker marker) throws StreamException {
+ return getActivities(limit, DefaultOptions.DEFAULT_OFFSET, filter, marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getActivities(
+ Limit limit, Offset offset, ActivityMarker marker) throws StreamException {
+ return getActivities(limit, offset, DefaultOptions.DEFAULT_FILTER, marker);
+ }
+
+ CompletableFuture extends List extends Group>> getActivities(
+ Limit limit, Offset offset, Filter filter, ActivityMarker marker) throws StreamException {
+ return getClient()
+ .getActivities(getID(), limit, offset, 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_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(
+ Class type, Limit limit) throws StreamException {
+ return getCustomActivities(
+ type,
+ limit,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(
+ Class type, Offset offset) throws StreamException {
+ return getCustomActivities(
+ type,
+ DefaultOptions.DEFAULT_LIMIT,
+ offset,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(
+ Class type, Filter filter) throws StreamException {
+ return getCustomActivities(
+ type,
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ filter,
+ DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(
+ Class type, ActivityMarker marker) throws StreamException {
+ return getCustomActivities(
+ type,
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(
+ Class type, Limit limit, Offset offset) throws StreamException {
+ return getCustomActivities(
+ type, limit, offset, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(
+ Class type, Limit limit, Filter filter) throws StreamException {
+ return getCustomActivities(
+ type, limit, DefaultOptions.DEFAULT_OFFSET, filter, DefaultOptions.DEFAULT_MARKER);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(
+ Class type, Limit limit, ActivityMarker marker) throws StreamException {
+ return getCustomActivities(
+ type, limit, DefaultOptions.DEFAULT_OFFSET, DefaultOptions.DEFAULT_FILTER, marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(
+ Class type, Filter filter, ActivityMarker marker) throws StreamException {
+ return getCustomActivities(
+ type, DefaultOptions.DEFAULT_LIMIT, DefaultOptions.DEFAULT_OFFSET, filter, marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(
+ Class type, Offset offset, ActivityMarker marker) throws StreamException {
+ return getCustomActivities(
+ type, DefaultOptions.DEFAULT_LIMIT, offset, DefaultOptions.DEFAULT_FILTER, marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(
+ Class type, Limit limit, Filter filter, ActivityMarker marker) throws StreamException {
+ return getCustomActivities(type, limit, DefaultOptions.DEFAULT_OFFSET, filter, marker);
+ }
+
+ public CompletableFuture extends List extends Group>> getCustomActivities(
+ Class type, Limit limit, Offset offset, ActivityMarker marker) throws StreamException {
+ return getCustomActivities(type, limit, offset, DefaultOptions.DEFAULT_FILTER, marker);
+ }
+
+ CompletableFuture extends List extends Group>> getCustomActivities(
+ Class type, Limit limit, Offset offset, Filter filter, ActivityMarker marker)
+ throws StreamException {
+ return getClient()
+ .getActivities(getID(), limit, offset, 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_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit) throws StreamException {
+ return getEnrichedActivities(
+ limit,
+ DefaultOptions.DEFAULT_OFFSET,
+ 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_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER,
+ flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Offset offset) throws StreamException {
+ return getEnrichedActivities(
+ DefaultOptions.DEFAULT_LIMIT,
+ offset,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Filter filter) throws StreamException {
+ return getEnrichedActivities(
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ filter,
+ DefaultOptions.DEFAULT_MARKER,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ ActivityMarker marker) throws StreamException {
+ return getEnrichedActivities(
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ marker,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(
+ limit,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER,
+ flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit, Offset offset) throws StreamException {
+ return getEnrichedActivities(
+ limit,
+ offset,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit, Filter filter) throws StreamException {
+ return getEnrichedActivities(
+ limit,
+ DefaultOptions.DEFAULT_OFFSET,
+ filter,
+ DefaultOptions.DEFAULT_MARKER,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit, ActivityMarker marker) throws StreamException {
+ return getEnrichedActivities(
+ limit,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ marker,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Offset offset, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(
+ DefaultOptions.DEFAULT_LIMIT,
+ offset,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER,
+ flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Filter filter, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ filter,
+ DefaultOptions.DEFAULT_MARKER,
+ flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Filter filter, ActivityMarker marker) throws StreamException {
+ return getEnrichedActivities(
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ filter,
+ marker,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Offset offset, ActivityMarker marker) throws StreamException {
+ return getEnrichedActivities(
+ DefaultOptions.DEFAULT_LIMIT,
+ offset,
+ 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_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ marker,
+ flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit, Offset offset, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(
+ limit, offset, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit, Filter filter, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(
+ limit, DefaultOptions.DEFAULT_OFFSET, filter, DefaultOptions.DEFAULT_MARKER, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit, Filter filter, ActivityMarker marker) throws StreamException {
+ return getEnrichedActivities(
+ limit,
+ DefaultOptions.DEFAULT_OFFSET,
+ filter,
+ marker,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit, Offset offset, ActivityMarker marker) throws StreamException {
+ return getEnrichedActivities(
+ limit,
+ offset,
+ DefaultOptions.DEFAULT_FILTER,
+ marker,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit, ActivityMarker marker, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(
+ limit, DefaultOptions.DEFAULT_OFFSET, DefaultOptions.DEFAULT_FILTER, marker, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Filter filter, ActivityMarker marker, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(
+ DefaultOptions.DEFAULT_LIMIT, DefaultOptions.DEFAULT_OFFSET, filter, marker, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Offset offset, ActivityMarker marker, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedActivities(
+ DefaultOptions.DEFAULT_LIMIT, offset, DefaultOptions.DEFAULT_FILTER, marker, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit, Filter filter, ActivityMarker marker, EnrichmentFlags flags)
+ throws StreamException {
+ return getEnrichedActivities(limit, DefaultOptions.DEFAULT_OFFSET, filter, marker, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit, Offset offset, ActivityMarker marker, EnrichmentFlags flags)
+ throws StreamException {
+ return getEnrichedActivities(limit, offset, DefaultOptions.DEFAULT_FILTER, marker, flags);
+ }
+
+ CompletableFuture extends List extends Group>> getEnrichedActivities(
+ Limit limit, Offset offset, Filter filter, ActivityMarker marker, EnrichmentFlags flags)
+ throws StreamException {
+ return getClient()
+ .getEnrichedActivities(getID(), limit, offset, 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>> getEnrichedActivities(
+ RequestOption... options) throws StreamException {
+ // If no options provided, use defaults
+ if (options == null || options.length == 0) {
+ options = new RequestOption[] {
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS,
+ DefaultOptions.DEFAULT_MARKER
+ };
+ }
+
+ return getClient()
+ .getEnrichedActivities(getID(), options)
+ .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_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Limit limit) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ limit,
+ DefaultOptions.DEFAULT_OFFSET,
+ 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_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER,
+ flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Offset offset) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ DefaultOptions.DEFAULT_LIMIT,
+ offset,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Filter filter) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ filter,
+ DefaultOptions.DEFAULT_MARKER,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, ActivityMarker marker) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ marker,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Limit limit, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ limit,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER,
+ flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Limit limit, Offset offset) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ limit,
+ offset,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Limit limit, Filter filter) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ limit,
+ DefaultOptions.DEFAULT_OFFSET,
+ filter,
+ DefaultOptions.DEFAULT_MARKER,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Limit limit, ActivityMarker marker) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ limit,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ marker,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Offset offset, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ DefaultOptions.DEFAULT_LIMIT,
+ offset,
+ DefaultOptions.DEFAULT_FILTER,
+ DefaultOptions.DEFAULT_MARKER,
+ flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Filter filter, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ filter,
+ DefaultOptions.DEFAULT_MARKER,
+ flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Filter filter, ActivityMarker marker) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ DefaultOptions.DEFAULT_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ filter,
+ marker,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Offset offset, ActivityMarker marker) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ DefaultOptions.DEFAULT_LIMIT,
+ offset,
+ 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_LIMIT,
+ DefaultOptions.DEFAULT_OFFSET,
+ DefaultOptions.DEFAULT_FILTER,
+ marker,
+ flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Limit limit, Offset offset, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedCustomActivities(
+ type, limit, offset, DefaultOptions.DEFAULT_FILTER, DefaultOptions.DEFAULT_MARKER, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Limit limit, Filter filter, EnrichmentFlags flags) throws StreamException {
+ return getEnrichedCustomActivities(
+ type, limit, DefaultOptions.DEFAULT_OFFSET, filter, DefaultOptions.DEFAULT_MARKER, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Limit limit, Filter filter, ActivityMarker marker) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ limit,
+ DefaultOptions.DEFAULT_OFFSET,
+ filter,
+ marker,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Limit limit, Offset offset, ActivityMarker marker) throws StreamException {
+ return getEnrichedCustomActivities(
+ type,
+ limit,
+ offset,
+ DefaultOptions.DEFAULT_FILTER,
+ marker,
+ DefaultOptions.DEFAULT_ENRICHMENT_FLAGS);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Limit limit, ActivityMarker marker, EnrichmentFlags flags)
+ throws StreamException {
+ return getEnrichedCustomActivities(
+ type, limit, DefaultOptions.DEFAULT_OFFSET, DefaultOptions.DEFAULT_FILTER, marker, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Filter filter, ActivityMarker marker, EnrichmentFlags flags)
+ throws StreamException {
+ return getEnrichedCustomActivities(
+ type, DefaultOptions.DEFAULT_LIMIT, DefaultOptions.DEFAULT_OFFSET, filter, marker, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Offset offset, ActivityMarker marker, EnrichmentFlags flags)
+ throws StreamException {
+ return getEnrichedCustomActivities(
+ type, DefaultOptions.DEFAULT_LIMIT, offset, DefaultOptions.DEFAULT_FILTER, marker, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Limit limit, Filter filter, ActivityMarker marker, EnrichmentFlags flags)
+ throws StreamException {
+ return getEnrichedCustomActivities(
+ type, limit, DefaultOptions.DEFAULT_OFFSET, filter, marker, flags);
+ }
+
+ public CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type, Limit limit, Offset offset, ActivityMarker marker, EnrichmentFlags flags)
+ throws StreamException {
+ return getEnrichedCustomActivities(
+ type, limit, offset, DefaultOptions.DEFAULT_FILTER, marker, flags);
+ }
+
+ CompletableFuture extends List extends Group>> getEnrichedCustomActivities(
+ Class type,
+ Limit limit,
+ Offset offset,
+ Filter filter,
+ ActivityMarker marker,
+ EnrichmentFlags flags)
+ throws StreamException {
+ return getClient()
+ .getEnrichedActivities(getID(), limit, offset, 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..7c03cbd3
--- /dev/null
+++ b/src/main/java/io/getstream/client/AnalyticsClient.java
@@ -0,0 +1,62 @@
+package io.getstream.client;
+
+import static io.getstream.core.utils.Auth.buildAnalyticsRedirectToken;
+import static io.getstream.core.utils.Auth.buildAnalyticsToken;
+
+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 java8.util.concurrent.CompletableFuture;
+
+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/AuditLogsClient.java b/src/main/java/io/getstream/client/AuditLogsClient.java
new file mode 100644
index 00000000..18e592a0
--- /dev/null
+++ b/src/main/java/io/getstream/client/AuditLogsClient.java
@@ -0,0 +1,97 @@
+package io.getstream.client;
+
+import io.getstream.core.Stream;
+import io.getstream.core.http.Token;
+import io.getstream.core.exceptions.StreamException;
+import io.getstream.core.models.AuditLog;
+import io.getstream.core.options.RequestOption;
+import io.getstream.core.options.CustomQueryParameter;
+import io.getstream.core.utils.Auth.TokenAction;
+import java8.util.concurrent.CompletableFuture;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static io.getstream.core.utils.Auth.buildAuditLogsToken;
+
+/**
+ * Client for querying Stream audit logs.
+ * Audit logs record changes to various entities within your Stream app.
+ */
+public final class AuditLogsClient {
+ private final String secret;
+ private final Stream stream;
+
+ public AuditLogsClient(String secret, Stream stream) {
+ this.secret = secret;
+ this.stream = stream;
+ }
+
+ /**
+ * Query audit logs with the specified filters and default pagination.
+ *
+ * @param filters Filters to apply to the query (either entityType+entityID OR userID is required)
+ * @return CompletableFuture with the query response
+ * @throws StreamException if the filters are invalid or if there's an API error
+ */
+ public CompletableFuture queryAuditLogs(QueryAuditLogsFilters filters) throws StreamException {
+ return queryAuditLogs(filters, new QueryAuditLogsPager());
+ }
+
+ /**
+ * Query audit logs with the specified filters and pagination.
+ *
+ * @param filters Filters to apply to the query (either entityType+entityID OR userID is required)
+ * @param pager Pagination settings for the query
+ * @return CompletableFuture with the query response
+ * @throws StreamException if the filters are invalid or if there's an API error
+ */
+ public CompletableFuture queryAuditLogs(QueryAuditLogsFilters filters, QueryAuditLogsPager pager) throws StreamException {
+ // Validate filters before making the API call
+ if (filters == null) {
+ throw new StreamException("Filters cannot be null for audit logs queries");
+ }
+
+ final Token token = buildAuditLogsToken(secret, TokenAction.READ);
+
+ RequestOption[] options = buildRequestOptions(filters, pager);
+ return stream.queryAuditLogs(token, options);
+ }
+
+ /**
+ * Builds request options from filters and pagination settings.
+ *
+ * @param filters Filters to apply to the query
+ * @param pager Pagination settings
+ * @return Array of RequestOption for the API call
+ */
+ private RequestOption[] buildRequestOptions(QueryAuditLogsFilters filters, QueryAuditLogsPager pager) {
+ List options = new ArrayList<>();
+
+ if (filters.getEntityType() != null && !filters.getEntityType().isEmpty() &&
+ filters.getEntityID() != null && !filters.getEntityID().isEmpty()) {
+ options.add(new CustomQueryParameter("entity_type", filters.getEntityType()));
+ options.add(new CustomQueryParameter("entity_id", filters.getEntityID()));
+ }
+
+ if (filters.getUserID() != null && !filters.getUserID().isEmpty()) {
+ options.add(new CustomQueryParameter("user_id", filters.getUserID()));
+ }
+
+ if (pager != null) {
+ if (pager.getNext() != null && !pager.getNext().isEmpty()) {
+ options.add(new CustomQueryParameter("next", pager.getNext()));
+ }
+
+ if (pager.getPrev() != null && !pager.getPrev().isEmpty()) {
+ options.add(new CustomQueryParameter("prev", pager.getPrev()));
+ }
+
+ if (pager.getLimit() > 0) {
+ options.add(new CustomQueryParameter("limit", Integer.toString(pager.getLimit())));
+ }
+ }
+
+ return options.toArray(new RequestOption[0]);
+ }
+}
\ No newline at end of file
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..e9cd30be
--- /dev/null
+++ b/src/main/java/io/getstream/client/BatchClient.java
@@ -0,0 +1,137 @@
+package io.getstream.client;
+
+import static io.getstream.core.utils.Auth.*;
+
+import com.google.common.collect.Iterables;
+import io.getstream.core.KeepHistory;
+import io.getstream.core.StreamBatch;
+import io.getstream.core.exceptions.StreamException;
+import io.getstream.core.http.Token;
+import io.getstream.core.models.*;
+import io.getstream.core.options.EnrichmentFlags;
+import io.getstream.core.utils.DefaultOptions;
+import java.util.List;
+import java8.util.J8Arrays;
+import java8.util.concurrent.CompletableFuture;
+
+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.WRITE);
+ final UnfollowOperation[] ops =
+ J8Arrays.stream(follows)
+ .map(follow -> new UnfollowOperation(follow, io.getstream.core.KeepHistory.YES))
+ .toArray(UnfollowOperation[]::new);
+ return batch.unfollowMany(token, ops);
+ }
+
+ public CompletableFuture unfollowMany(KeepHistory keepHistory, FollowRelation... follows)
+ throws StreamException {
+ final Token token = buildFollowToken(secret, TokenAction.WRITE);
+ final UnfollowOperation[] ops =
+ J8Arrays.stream(follows)
+ .map(follow -> new UnfollowOperation(follow, keepHistory))
+ .toArray(UnfollowOperation[]::new);
+ return batch.unfollowMany(token, ops);
+ }
+
+ public CompletableFuture unfollowMany(UnfollowOperation... unfollows)
+ throws StreamException {
+ final Token token = buildFollowToken(secret, TokenAction.WRITE);
+ return batch.unfollowMany(token, unfollows);
+ }
+
+ 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> getEnrichedActivitiesByID(
+ Iterable activityIDs) throws StreamException {
+ return getEnrichedActivitiesByID(Iterables.toArray(activityIDs, String.class));
+ }
+
+ public CompletableFuture> getEnrichedActivitiesByID(String... activityIDs)
+ throws StreamException {
+ return getEnrichedActivitiesByID(DefaultOptions.DEFAULT_ENRICHMENT_FLAGS, activityIDs);
+ }
+
+ public CompletableFuture> getEnrichedActivitiesByID(
+ EnrichmentFlags flags, String... activityIDs) throws StreamException {
+ final Token token = buildActivityToken(secret, TokenAction.READ);
+ return batch.getEnrichedActivitiesByID(token, flags, 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> getEnrichedActivitiesByForeignID(
+ Iterable activityIDTimePairs) throws StreamException {
+ return getEnrichedActivitiesByForeignID(
+ Iterables.toArray(activityIDTimePairs, ForeignIDTimePair.class));
+ }
+
+ public CompletableFuture> getEnrichedActivitiesByForeignID(
+ ForeignIDTimePair... activityIDTimePairs) throws StreamException {
+ final Token token = buildActivityToken(secret, TokenAction.READ);
+ return batch.getEnrichedActivitiesByForeignID(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..49fba80c
--- /dev/null
+++ b/src/main/java/io/getstream/client/Client.java
@@ -0,0 +1,387 @@
+package io.getstream.client;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static io.getstream.core.utils.Auth.*;
+
+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.List;
+import java.util.Map;
+
+import io.getstream.core.utils.Auth;
+import java8.util.concurrent.CompletableFuture;
+
+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(ActivityUpdate update)
+ throws StreamException {
+ return updateActivityByID(update.getID(), update.getSet(), update.getUnset());
+ }
+
+ public CompletableFuture updateActivityByID(
+ String id, Map set, String[] unset) throws StreamException {
+ final Token token = buildActivityToken(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(ActivityUpdate update)
+ throws StreamException {
+ return updateActivityByForeignID(
+ update.getForeignID(), update.getTime(), update.getSet(), update.getUnset());
+ }
+
+ 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 CompletableFuture> updateActivitiesByID(Iterable updates)
+ throws StreamException {
+ return updateActivitiesByID(Iterables.toArray(updates, ActivityUpdate.class));
+ }
+
+ public CompletableFuture> updateActivitiesByID(ActivityUpdate... updates)
+ throws StreamException {
+ final Token token = buildActivityToken(secret, TokenAction.WRITE);
+ return stream.updateActivitiesByID(token, updates);
+ }
+
+ public CompletableFuture> updateActivitiesByForeignID(
+ Iterable updates) throws StreamException {
+ return updateActivitiesByForeignID(Iterables.toArray(updates, ActivityUpdate.class));
+ }
+
+ public CompletableFuture> updateActivitiesByForeignID(ActivityUpdate... updates)
+ throws StreamException {
+ final Token token = buildActivityToken(secret, TokenAction.WRITE);
+ return stream.updateActivitiesByForeignID(token, updates);
+ }
+
+ 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 Token frontendToken(String userID, Date expiresAt) {
+ return buildFrontendToken(secret, userID, expiresAt);
+ }
+
+ 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 ModerationClient moderation() {
+ return new ModerationClient(secret, stream.moderation());
+ }
+
+ public AuditLogsClient auditLogs() {
+ return new AuditLogsClient(secret, stream);
+ }
+
+ 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 getFollowStats(
+ FeedID feed, String[] followerSlugs, String[] followingSlugs) throws StreamException {
+ final Token token = buildFollowToken(secret, TokenAction.READ);
+ return stream.getFollowStats(token, feed, followerSlugs, followingSlugs);
+ }
+
+ 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