From 47a51005e1e143fdd6007abd23b8056f2ea50682 Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Tue, 7 Apr 2026 12:41:32 +0530 Subject: [PATCH 1/6] Add agent guide and skills documentation --- .cursor/rules/README.md | 5 +++ AGENTS.md | 51 +++++++++++++++++++++++ skills/README.md | 16 +++++++ skills/code-review/SKILL.md | 42 +++++++++++++++++++ skills/contentstack-java-cma-sdk/SKILL.md | 35 ++++++++++++++++ skills/dev-workflow/SKILL.md | 41 ++++++++++++++++++ skills/http-client-stack/SKILL.md | 42 +++++++++++++++++++ skills/java/SKILL.md | 37 ++++++++++++++++ skills/testing/SKILL.md | 37 ++++++++++++++++ 9 files changed, 306 insertions(+) create mode 100644 .cursor/rules/README.md create mode 100644 AGENTS.md create mode 100644 skills/README.md create mode 100644 skills/code-review/SKILL.md create mode 100644 skills/contentstack-java-cma-sdk/SKILL.md create mode 100644 skills/dev-workflow/SKILL.md create mode 100644 skills/http-client-stack/SKILL.md create mode 100644 skills/java/SKILL.md create mode 100644 skills/testing/SKILL.md diff --git a/.cursor/rules/README.md b/.cursor/rules/README.md new file mode 100644 index 00000000..d8f35453 --- /dev/null +++ b/.cursor/rules/README.md @@ -0,0 +1,5 @@ +# Cursor (optional) + +*Cursor* users: start at *[AGENTS.md](../../AGENTS.md)*. All conventions live in **skills/*/SKILL.md**. + +This folder only points contributors to *AGENTS.md* so editor-specific config does not duplicate the canonical docs. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..d4a23aee --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,51 @@ +# Contentstack Management Java SDK – Agent guide + +*Universal entry point* for contributors and AI agents. Detailed conventions live in **skills/*/SKILL.md**. + +## What this repo is + +| Field | Detail | +| --- | --- | +| *Name:* | [contentstack-management-java](https://github.com/contentstack/contentstack-management-java) | +| *Purpose:* | Java client library for the Contentstack Content Management API (CMA): create, update, delete, and fetch account content and configuration from JVM applications. | +| *Out of scope (if any):* | Content Delivery API (CDA) and delivery-only use cases belong in the Delivery SDK, not this package. This SDK is read/write against CMA; do not treat it as the primary way to serve content to end users. | + +## Tech stack (at a glance) + +| Area | Details | +| --- | --- | +| Language | Java 8 (source/target in `pom.xml`); README suggests Java 8+ for consumers. | +| Build | Apache Maven; `pom.xml` at repo root. | +| Tests | JUnit 5 / Vintage, Mockito, OkHttp MockWebServer; tests under `src/test/java/com/contentstack/cms/`. | +| Lint / coverage | Compiler: `-Xlint:all` (see `maven-compiler-plugin`). Coverage: JaCoCo (`jacoco-maven-plugin`); HTML report under `target/site/jacoco` after tests + `jacoco:report`. No separate Checkstyle/SpotBugs in this `pom.xml`. | +| Other | HTTP: Retrofit 3, OkHttp 5, Gson; RxJava 3; Lombok for generated code. | + +## Commands (quick reference) + +| Command type | Command | +| --- | --- | +| Build | `mvn clean compile` | +| Test | `mvn clean test -DskipTests=false` | +| Lint | `mvn compile` (compiler warnings; see Tech stack) | +| Coverage | `mvn clean test -DskipTests=false jacoco:report` (open `target/site/jacoco/index.html`) | + +**Note:** `maven-surefire-plugin` sets `skipTests` to `true` by default in this repo, so you must pass `-DskipTests=false` (or override in your IDE) to run unit tests locally. + +**CI:** [.github/workflows/coverage.yml](.github/workflows/coverage.yml) runs `mvn clean package` and JaCoCo steps on push. Other workflows under [.github/workflows/](.github/workflows/) handle publish, scans, and branch checks. + +## Where the documentation lives: skills + +| Skill | Path | What it covers | +| --- | --- | --- | +| Dev workflow | [skills/dev-workflow/SKILL.md](skills/dev-workflow/SKILL.md) | Branches, Maven lifecycle, CI expectations, PR flow. | +| Contentstack Java CMA SDK | [skills/contentstack-java-cma-sdk/SKILL.md](skills/contentstack-java-cma-sdk/SKILL.md) | Public API, `Contentstack` entry point, auth, versioning boundaries. | +| Java (repo conventions) | [skills/java/SKILL.md](skills/java/SKILL.md) | Package layout, language conventions, Lombok usage. | +| Testing | [skills/testing/SKILL.md](skills/testing/SKILL.md) | Test layout, naming, skipping policy, credentials. | +| Code review | [skills/code-review/SKILL.md](skills/code-review/SKILL.md) | PR checklist and severity guidance. | +| HTTP client stack | [skills/http-client-stack/SKILL.md](skills/http-client-stack/SKILL.md) | Retrofit, OkHttp, retries, logging—what this repo actually wires. | + +An index with “when to use” hints is in [skills/README.md](skills/README.md). + +## Using Cursor (optional) + +If you use *Cursor*, [.cursor/rules/README.md](.cursor/rules/README.md) only points to *AGENTS.md*—same docs as everyone else. diff --git a/skills/README.md b/skills/README.md new file mode 100644 index 00000000..5d34656a --- /dev/null +++ b/skills/README.md @@ -0,0 +1,16 @@ +# Skills – Contentstack Management Java SDK + +Source of truth for detailed guidance. Read [AGENTS.md](../AGENTS.md) first, then open the skill that matches your task. + +## When to use which skill + +| Skill folder | Use when | +| --- | --- | +| [dev-workflow](dev-workflow/SKILL.md) | Branching against `master`/`staging`, running Maven/CI commands, release or publish touchpoints. | +| [contentstack-java-cma-sdk](contentstack-java-cma-sdk/SKILL.md) | Changing public API, `Contentstack` / `Stack` flows, auth tokens, or SDK surface exposed to integrators. | +| [java](java/SKILL.md) | Package structure under `com.contentstack.cms`, Java 8 compatibility, Lombok, imports, and code style in this repo. | +| [testing](testing/SKILL.md) | Adding or fixing tests, Surefire `skipTests` behavior, MockWebServer or live API tests, env/credentials. | +| [code-review](code-review/SKILL.md) | Preparing or reviewing a PR: scope, tests, API compatibility, and security around tokens. | +| [http-client-stack](http-client-stack/SKILL.md) | Retrofit services, OkHttp client/interceptors, retries, proxies, or HTTP logging. | + +Each folder contains `SKILL.md` with YAML frontmatter (`name`, `description`). diff --git a/skills/code-review/SKILL.md b/skills/code-review/SKILL.md new file mode 100644 index 00000000..906a50f3 --- /dev/null +++ b/skills/code-review/SKILL.md @@ -0,0 +1,42 @@ +--- +name: code-review +description: Use when reviewing or preparing a PR—API safety, tests, auth handling, and compatibility checklist. +--- + +# Code review – Contentstack Management Java SDK + +## When to use + +- You are reviewing a pull request or self-reviewing before request. +- You need a quick severity rubric (blocker / major / minor) aligned with this SDK. + +## Instructions + +### Blocker + +- Real credentials, tokens, or stack keys committed in source or fixtures. +- Breaking public API changes without version strategy or without team agreement. +- New network behavior that bypasses existing interceptors, retry policy, or auth without explicit design. + +### Major + +- Missing or insufficient tests for new CMA behavior or error paths. +- Behavior change for `Contentstack` / `Stack` builders or defaults without README or Javadoc updates where integrators would notice. +- Regressions for Java 8 compatibility or new compiler warnings left unaddressed. + +### Minor + +- Style inconsistencies fixable in follow-up, or internal refactors with no API impact. +- Javadoc typos or non-user-facing comment cleanup. + +### PR checklist (short) + +- [ ] `mvn clean test -DskipTests=false` passes locally. +- [ ] Public changes documented (Javadoc / README / changelog per team process). +- [ ] No secrets in repo; test config uses env or mocks. +- [ ] HTTP changes reviewed against [http-client-stack/SKILL.md](../http-client-stack/SKILL.md). + +## References + +- [dev-workflow/SKILL.md](../dev-workflow/SKILL.md) +- [contentstack-java-cma-sdk/SKILL.md](../contentstack-java-cma-sdk/SKILL.md) diff --git a/skills/contentstack-java-cma-sdk/SKILL.md b/skills/contentstack-java-cma-sdk/SKILL.md new file mode 100644 index 00000000..15501d47 --- /dev/null +++ b/skills/contentstack-java-cma-sdk/SKILL.md @@ -0,0 +1,35 @@ +--- +name: contentstack-java-cma-sdk +description: Use when changing the Management SDK surface—Contentstack entry, Stack, auth, or CMA integration boundaries. +--- + +# Contentstack Java CMA SDK – Contentstack Management Java SDK + +## When to use + +- You add or change public methods on `Contentstack`, `Stack`, organization, user, or other API-facing types under `com.contentstack.cms`. +- You need to document how integrators authenticate (authtoken, management token, OAuth, login). +- You are deciding what belongs in this SDK vs the Delivery API or other Contentstack clients. + +## Instructions + +### Purpose and boundaries + +- This library targets the [Content Management API](https://www.contentstack.com/docs/developers/apis/content-management-api/) (CMA). It is **not** the recommended way to deliver content to websites or apps at scale; the README directs readers to the [Content Delivery API](https://www.contentstack.com/docs/developers/apis/content-delivery-api/) for that. +- The main entry point is [`Contentstack`](../../src/main/java/com/contentstack/cms/Contentstack.java): builders set host, tokens, timeouts, proxy, OAuth, and retry behavior before obtaining `Stack`, `User`, `Organization`, etc. + +### Public API and compatibility + +- Treat additions to public classes as **semver-sensitive**: new optional builder methods are easier to ship than breaking signature changes. +- Errors and responses should stay consistent with existing patterns (`retrofit2.Response`, model types under `com.contentstack.cms.models`, etc.). +- Document behavior in Javadoc where integrators rely on contracts (auth order, token precedence, branch/early-access headers). + +### Versioning + +- Artifact coordinates: `com.contentstack.sdk:cms` — version in root [`pom.xml`](../../pom.xml). Follow org release process for bumping version and tagging. + +## References + +- [java/SKILL.md](../java/SKILL.md) +- [http-client-stack/SKILL.md](../http-client-stack/SKILL.md) +- [Content Management API – Authentication](https://www.contentstack.com/docs/developers/apis/content-management-api/#authentication) (official) diff --git a/skills/dev-workflow/SKILL.md b/skills/dev-workflow/SKILL.md new file mode 100644 index 00000000..3bfe3cc9 --- /dev/null +++ b/skills/dev-workflow/SKILL.md @@ -0,0 +1,41 @@ +--- +name: dev-workflow +description: Use when branching, building with Maven, CI, or release/publish workflows for this repo. +--- + +# Dev workflow – Contentstack Management Java SDK + +## When to use + +- You need the canonical build/test commands or CI expectations. +- You are opening a PR and need branch rules (`master` vs `staging`). +- You are changing `pom.xml`, plugins, or publishing configuration. + +## Instructions + +### Repository and branches + +- Default collaboration flow is documented in [.github/workflows/check-branch.yml](../../.github/workflows/check-branch.yml): PRs targeting `master` from branches other than `staging` may be blocked; prefer the documented Contentstack branching policy for your team. + +### Maven + +- **Compile:** `mvn clean compile` +- **Tests:** `mvn clean test -DskipTests=false` (Surefire is configured with `skipTests` true by default in [`pom.xml`](../../pom.xml); always pass `-DskipTests=false` for real test runs unless you intentionally skip.) +- **Package:** `mvn clean package` — note tests may be skipped unless you add `-DskipTests=false`. +- **Coverage report:** after tests with skip disabled, `mvn jacoco:report` and open `target/site/jacoco/index.html`. + +### CI + +- On push, [.github/workflows/coverage.yml](../../.github/workflows/coverage.yml) uses Java 11 on Ubuntu and runs Maven + JaCoCo. Align local verification with those steps when debugging CI-only failures. +- Publishing and security scans live under [.github/workflows/](../../.github/workflows/); read the relevant workflow before changing release or scan behavior. + +### PR expectations + +- Run tests locally with `-DskipTests=false` before pushing. +- Update [changelog.md](../../changelog.md) or version metadata when your team’s release process requires it. + +## References + +- [AGENTS.md](../../AGENTS.md) +- [testing/SKILL.md](../testing/SKILL.md) +- [code-review/SKILL.md](../code-review/SKILL.md) diff --git a/skills/http-client-stack/SKILL.md b/skills/http-client-stack/SKILL.md new file mode 100644 index 00000000..db8717ba --- /dev/null +++ b/skills/http-client-stack/SKILL.md @@ -0,0 +1,42 @@ +--- +name: http-client-stack +description: Use when changing Retrofit, OkHttp, interceptors, retries, proxies, or HTTP logging in this SDK. +--- + +# HTTP client stack – Contentstack Management Java SDK + +## When to use + +- You modify [`Contentstack`](../../src/main/java/com/contentstack/cms/Contentstack.java) or related code that builds `Retrofit`, `OkHttpClient`, or interceptors. +- You touch `AuthInterceptor`, OAuth interceptors, logging, connection pool, timeouts, or `RetryConfig` / retry utilities under `com.contentstack.cms.core`. + +## Instructions + +### Stack in this repo + +- **Retrofit 3** + **Gson** converter for API interfaces and JSON binding. +- **OkHttp 5** for transport; **logging-interceptor** for debug logging when enabled. +- **RxJava 3** is a dependency for async patterns where used; keep consistency with existing call patterns. + +### Auth and headers + +- `AuthInterceptor` and OAuth-related types attach tokens and headers expected by CMA. Changes here affect every call—coordinate with tests in `core/` (e.g. `AuthInterceptorTest`). + +### Retries and resilience + +- Retry behavior is configurable via `RetryConfig` and related utilities (`core/Retry*.java`). Preserve backward-compatible defaults when adjusting retry conditions or backoff. + +### Proxies and TLS + +- `Contentstack` supports `Proxy` configuration; verify behavior if you change client building or connection pool settings. + +### Testing + +- Prefer **MockWebServer** for deterministic HTTP tests; see existing stack and core tests for patterns. + +## References + +- [contentstack-java-cma-sdk/SKILL.md](../contentstack-java-cma-sdk/SKILL.md) +- [testing/SKILL.md](../testing/SKILL.md) +- [Retrofit](https://square.github.io/retrofit/) (official) +- [OkHttp](https://square.github.io/okhttp/) (official) diff --git a/skills/java/SKILL.md b/skills/java/SKILL.md new file mode 100644 index 00000000..59dfab06 --- /dev/null +++ b/skills/java/SKILL.md @@ -0,0 +1,37 @@ +--- +name: java +description: Use for Java package layout, Java 8 constraints, Lombok, and coding conventions in this repository. +--- + +# Java – Contentstack Management Java SDK + +## When to use + +- You add new types under `com.contentstack.cms` or reorganize packages. +- You need to stay compatible with Java 8 (`maven.compiler.source` / `target` = 1.8). +- You use or configure Lombok and need to match existing patterns. + +## Instructions + +### Layout + +- **Main code:** `src/main/java/com/contentstack/cms/` — core client in `Contentstack`, stack features under `stack/`, HTTP helpers in `core/`, OAuth under `oauth/`, models under `models/`. +- **Tests:** mirror packages under `src/test/java/com/contentstack/cms/`; Surefire includes `**/Test*.java`, `**/*Test.java`, `**/*Tests.java`, and related patterns per [`pom.xml`](../../pom.xml). + +### Language level + +- Do not use language or library features that require a JVM newer than 8 for **source** compatibility unless the project explicitly upgrades (check `maven-compiler-plugin` and `` in [`pom.xml`](../../pom.xml)). + +### Lombok and tooling + +- Lombok is on the annotation processor path; prefer the same style as neighboring classes (`@Getter`, `@Builder`, etc.) instead of mixing hand-written boilerplate inconsistently. +- JetBrains `@NotNull` / nullable annotations appear in places like [`Contentstack`](../../src/main/java/com/contentstack/cms/Contentstack.java); follow existing nullability hints for new APIs. + +### Compiler + +- The compiler enables broad `-Xlint` checks; fix new warnings rather than suppressing without cause. + +## References + +- [contentstack-java-cma-sdk/SKILL.md](../contentstack-java-cma-sdk/SKILL.md) +- [testing/SKILL.md](../testing/SKILL.md) diff --git a/skills/testing/SKILL.md b/skills/testing/SKILL.md new file mode 100644 index 00000000..d345a1f3 --- /dev/null +++ b/skills/testing/SKILL.md @@ -0,0 +1,37 @@ +--- +name: testing +description: Use when writing or running tests—Surefire skip flag, test naming, mocks, MockWebServer, and secrets policy. +--- + +# Testing – Contentstack Management Java SDK + +## When to use + +- You add or change unit or integration tests under `src/test/java`. +- Tests “do not run” locally or in CI — usually `skipTests`. +- You need to mock HTTP or use OkHttp `MockWebServer` like existing tests. + +## Instructions + +### Running tests + +- **Always** use `mvn test -DskipTests=false` (or `mvn clean test -DskipTests=false`) unless you are only compiling. The Surefire plugin in [`pom.xml`](../../pom.xml) sets `true` by default. + +### Layout and naming + +- Tests live under `src/test/java/com/contentstack/cms/` with class names matching Surefire includes (`*Test`, `*Tests`, `Test*`, suites like `UnitTestSuite`). +- Reuse helpers such as [`TestClient`](../../src/test/java/com/contentstack/cms/TestClient.java), [`TestUtils`](../../src/test/java/com/contentstack/cms/core/TestUtils.java), or patterns in existing `*APITest` classes when appropriate. + +### HTTP and API tests + +- **MockWebServer** is available for stubbing HTTP; align with tests in `core/` and `stack/` packages. +- Tests that hit real APIs may require environment variables or `.env` (see `java-dotenv` test dependency in [`pom.xml`](../../pom.xml)). **Never commit** real API keys, management tokens, or passwords; use CI secrets or local env only. + +### Coverage + +- JaCoCo attaches via `prepare-agent`; generate HTML with `mvn jacoco:report` after a test run with skips disabled. + +## References + +- [dev-workflow/SKILL.md](../dev-workflow/SKILL.md) +- [http-client-stack/SKILL.md](../http-client-stack/SKILL.md) From a4cbc18e29ac058067394e88fa6617419672119a Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Tue, 7 Apr 2026 12:52:55 +0530 Subject: [PATCH 2/6] snyk fixes --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 2feb459a..c227e9ba 100644 --- a/pom.xml +++ b/pom.xml @@ -93,7 +93,7 @@ 3.0.0 5.3.2 0.8.13 - 1.18.42 + 1.18.44 5.11.4 5.10.1 2.13.2 @@ -148,7 +148,7 @@ org.jetbrains annotations - 26.0.2 + 26.1.0 provided @@ -214,12 +214,12 @@ org.jsoup jsoup - 1.21.2 + 1.22.1 org.jetbrains.kotlin kotlin-stdlib - 2.2.21 + 2.3.20 com.warrenstrange @@ -254,7 +254,7 @@ ${project.build.directory}/surefire-reports - true + true From 63f2a7ab8ffa8e85317382c78be85d0422757207 Mon Sep 17 00:00:00 2001 From: harshitha-cstk Date: Thu, 23 Apr 2026 17:12:04 +0530 Subject: [PATCH 3/6] =?UTF-8?q?enh:=20adopt=20development=E2=86=92main=20r?= =?UTF-8?q?elease=20flow,=20back-merge,=20and=20version=20checks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/back-merge-pr.yml | 59 ++++++++++++ .github/workflows/check-branch.yml | 20 ---- .github/workflows/check-version-bump.yml | 111 +++++++++++++++++++++++ .github/workflows/maven-publish.yml | 2 + skills/README.md | 2 +- skills/dev-workflow/SKILL.md | 11 +-- 6 files changed, 176 insertions(+), 29 deletions(-) create mode 100644 .github/workflows/back-merge-pr.yml delete mode 100644 .github/workflows/check-branch.yml create mode 100644 .github/workflows/check-version-bump.yml diff --git a/.github/workflows/back-merge-pr.yml b/.github/workflows/back-merge-pr.yml new file mode 100644 index 00000000..cec0f269 --- /dev/null +++ b/.github/workflows/back-merge-pr.yml @@ -0,0 +1,59 @@ +# Opens a PR from master → development after changes land on master (back-merge). +# +# Org/repo Settings → Actions → General → Workflow permissions: read and write +# (so GITHUB_TOKEN can create pull requests). Or use a PAT in secret GH_TOKEN. + +name: Back-merge master to development + +on: + push: + branches: [master] + workflow_dispatch: + +permissions: + contents: read + pull-requests: write + +jobs: + open-back-merge-pr: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Open back-merge PR if needed + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + git fetch origin development master + + MASTER_SHA=$(git rev-parse origin/master) + DEV_SHA=$(git rev-parse origin/development) + + if [ "$MASTER_SHA" = "$DEV_SHA" ]; then + echo "master and development are at the same commit; nothing to back-merge." + exit 0 + fi + + EXISTING=$(gh pr list --repo "${{ github.repository }}" \ + --base development \ + --head master \ + --state open \ + --json number \ + --jq 'length') + + if [ "$EXISTING" -gt 0 ]; then + echo "An open PR from master to development already exists; skipping." + exit 0 + fi + + gh pr create --repo "${{ github.repository }}" \ + --base development \ + --head master \ + --title "chore: back-merge master into development" \ + --body "Automated back-merge after changes landed on \`master\`. Review and merge to keep \`development\` in sync." + + echo "Created back-merge PR master → development." diff --git a/.github/workflows/check-branch.yml b/.github/workflows/check-branch.yml deleted file mode 100644 index 8a3a32ab..00000000 --- a/.github/workflows/check-branch.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: 'Check Branch' - -on: - pull_request: - -jobs: - check_branch: - runs-on: ubuntu-latest - steps: - - name: Comment PR - if: github.base_ref == 'master' && github.head_ref != 'staging' - uses: thollander/actions-comment-pull-request@v2 - with: - message: | - We regret to inform you that you are currently not able to merge your changes into the master branch due to restrictions applied by our SRE team. To proceed with merging your changes, we kindly request that you create a pull request from the staging branch. Our team will then review the changes and work with you to ensure a successful merge into the master branch. - - name: Check branch - if: github.base_ref == 'master' && github.head_ref != 'staging' - run: | - echo "ERROR: We regret to inform you that you are currently not able to merge your changes into the master branch due to restrictions applied by our SRE team. To proceed with merging your changes, we kindly request that you create a pull request from the staging branch. Our team will then review the changes and work with you to ensure a successful merge into the master branch." - exit 1 \ No newline at end of file diff --git a/.github/workflows/check-version-bump.yml b/.github/workflows/check-version-bump.yml new file mode 100644 index 00000000..9c7d3136 --- /dev/null +++ b/.github/workflows/check-version-bump.yml @@ -0,0 +1,111 @@ +# Release-affecting changes under src/main/ or pom.xml require pom.xml + changelog.md bumps +# aligned with the latest tag. Skips when only tests, .github, skills, or docs change. + +name: Check Version Bump + +on: + pull_request: + +jobs: + version-bump: + name: Version & changelog bump + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Detect changed files + id: detect + run: | + FILES=$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}") + echo "Changed files:" + echo "$FILES" + + CODE_CHANGED=false + while IFS= read -r f; do + [ -z "$f" ] && continue + if [[ "$f" == src/main/* ]] || [[ "$f" == "pom.xml" ]]; then + CODE_CHANGED=true + break + fi + done <<< "$FILES" + + POM_CHANGED=false + CHANGELOG_CHANGED=false + echo "$FILES" | grep -qx 'pom.xml' && POM_CHANGED=true + echo "$FILES" | grep -qx 'changelog.md' && CHANGELOG_CHANGED=true + + VERSION_FILES_OK=false + if [ "$POM_CHANGED" = true ] && [ "$CHANGELOG_CHANGED" = true ]; then + VERSION_FILES_OK=true + fi + + echo "code_changed=$CODE_CHANGED" >> "$GITHUB_OUTPUT" + echo "version_files_ok=$VERSION_FILES_OK" >> "$GITHUB_OUTPUT" + + - name: Skip when no release-affecting code changed + if: steps.detect.outputs.code_changed != 'true' + run: | + echo "No src/main or pom-only release path triggered (e.g. tests/docs/.github only). Skipping version-bump check." + exit 0 + + - name: Fail when version bump files were not both updated + if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_ok != 'true' + run: | + echo "::error::This PR changes release-affecting code but pom.xml and/or changelog.md were not both updated. Bump in pom.xml and add a ## vX.Y.Z section in changelog.md." + exit 1 + + - name: Validate version vs latest tag and changelog header + if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_ok == 'true' + run: | + set -euo pipefail + POM_VERSION=$(python3 <<'PY' + import xml.etree.ElementTree as ET + root = ET.parse("pom.xml").getroot() + ns = {"m": "http://maven.apache.org/POM/4.0.0"} + el = root.find("m:version", ns) + if el is None or not (el.text or "").strip(): + raise SystemExit("Could not read project version from pom.xml") + print(el.text.strip()) + PY + ) + + git fetch --tags --force 2>/dev/null || true + LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true) + if [ -z "$LATEST_TAG" ]; then + echo "No existing tags found. Skipping semver vs tag check (first release)." + CHANGELOG_HEAD=$(sed -nE 's/^## v?([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' changelog.md | head -1) + if [ -z "$CHANGELOG_HEAD" ]; then + echo "::error::Could not find a ## vX.Y.Z entry at the top of changelog.md." + exit 1 + fi + if [ "$CHANGELOG_HEAD" != "$POM_VERSION" ]; then + echo "::error::changelog.md top version ($CHANGELOG_HEAD) does not match pom.xml version ($POM_VERSION)." + exit 1 + fi + exit 0 + fi + + LATEST_VERSION="${LATEST_TAG#v}" + LATEST_VERSION="${LATEST_VERSION%%-*}" + if [ "$(printf '%s\n' "$LATEST_VERSION" "$POM_VERSION" | sort -V | tail -1)" != "$POM_VERSION" ]; then + echo "::error::pom.xml version ($POM_VERSION) must be greater than latest tag ($LATEST_TAG)." + exit 1 + fi + if [ "$POM_VERSION" = "$LATEST_VERSION" ]; then + echo "::error::pom.xml version ($POM_VERSION) must be strictly greater than latest tag version ($LATEST_VERSION)." + exit 1 + fi + + CHANGELOG_HEAD=$(sed -nE 's/^## v?([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' changelog.md | head -1) + if [ -z "$CHANGELOG_HEAD" ]; then + echo "::error::Could not find a ## vX.Y.Z entry at the top of changelog.md." + exit 1 + fi + if [ "$CHANGELOG_HEAD" != "$POM_VERSION" ]; then + echo "::error::changelog.md top version ($CHANGELOG_HEAD) does not match pom.xml version ($POM_VERSION)." + exit 1 + fi + echo "Version bump check passed: pom.xml and changelog.md at $POM_VERSION (latest tag: $LATEST_TAG)." diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index 4c5577e1..e0bc9053 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -1,9 +1,11 @@ name: Publish package to the Maven Central Repository +# Publishes when a GitHub Release is created (same pattern as before tag-based experiments). on: release: types: - created + jobs: publish-maven: runs-on: ubuntu-latest diff --git a/skills/README.md b/skills/README.md index 5d34656a..0a5b2cbc 100644 --- a/skills/README.md +++ b/skills/README.md @@ -6,7 +6,7 @@ Source of truth for detailed guidance. Read [AGENTS.md](../AGENTS.md) first, the | Skill folder | Use when | | --- | --- | -| [dev-workflow](dev-workflow/SKILL.md) | Branching against `master`/`staging`, running Maven/CI commands, release or publish touchpoints. | +| [dev-workflow](dev-workflow/SKILL.md) | Branching (`development` → `master`, back-merge, GitHub Release publish), Maven/CI, publish touchpoints. | | [contentstack-java-cma-sdk](contentstack-java-cma-sdk/SKILL.md) | Changing public API, `Contentstack` / `Stack` flows, auth tokens, or SDK surface exposed to integrators. | | [java](java/SKILL.md) | Package structure under `com.contentstack.cms`, Java 8 compatibility, Lombok, imports, and code style in this repo. | | [testing](testing/SKILL.md) | Adding or fixing tests, Surefire `skipTests` behavior, MockWebServer or live API tests, env/credentials. | diff --git a/skills/dev-workflow/SKILL.md b/skills/dev-workflow/SKILL.md index 3bfe3cc9..66a5194c 100644 --- a/skills/dev-workflow/SKILL.md +++ b/skills/dev-workflow/SKILL.md @@ -8,14 +8,15 @@ description: Use when branching, building with Maven, CI, or release/publish wor ## When to use - You need the canonical build/test commands or CI expectations. -- You are opening a PR and need branch rules (`master` vs `staging`). +- You are opening a PR and need branch rules (`development` → `master`, GitHub Release publishing). - You are changing `pom.xml`, plugins, or publishing configuration. ## Instructions ### Repository and branches -- Default collaboration flow is documented in [.github/workflows/check-branch.yml](../../.github/workflows/check-branch.yml): PRs targeting `master` from branches other than `staging` may be blocked; prefer the documented Contentstack branching policy for your team. +- **Flow:** work merges to **`development`**; **release PRs** go **`development` → `master`** (no `staging`). After `master` moves, [.github/workflows/back-merge-pr.yml](../../.github/workflows/back-merge-pr.yml) can open a PR **`master` → `development`** to stay aligned. +- **Releases:** create a **GitHub Release** (triggers [.github/workflows/maven-publish.yml](../../.github/workflows/maven-publish.yml) on **`release: created`**). PRs that change `src/main` or `pom.xml` are checked by [.github/workflows/check-version-bump.yml](../../.github/workflows/check-version-bump.yml) (version + `changelog.md`). ### Maven @@ -33,9 +34,3 @@ description: Use when branching, building with Maven, CI, or release/publish wor - Run tests locally with `-DskipTests=false` before pushing. - Update [changelog.md](../../changelog.md) or version metadata when your team’s release process requires it. - -## References - -- [AGENTS.md](../../AGENTS.md) -- [testing/SKILL.md](../testing/SKILL.md) -- [code-review/SKILL.md](../code-review/SKILL.md) From e22648e625284e9ea9cce90de7aa68c3115f2fae Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Tue, 26 May 2026 17:55:33 +0530 Subject: [PATCH 4/6] fix: enhance retry mechanism for network-level timeouts in AuthInterceptor and OAuthInterceptor; update version to 1.11.2 --- changelog.md | 6 + pom.xml | 2 +- .../cms/core/AuthInterceptor.java | 41 ++++-- .../cms/oauth/OAuthInterceptor.java | 18 ++- .../cms/core/AuthInterceptorTest.java | 84 +++++++++++++ .../cms/oauth/OAuthInterceptorTest.java | 118 ++++++++++++++++++ 6 files changed, 254 insertions(+), 15 deletions(-) create mode 100644 src/test/java/com/contentstack/cms/oauth/OAuthInterceptorTest.java diff --git a/changelog.md b/changelog.md index 80f1373b..b69d012c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # Changelog +## v1.11.2 + +### Jun 01, 2026 + +- Fix: `SocketTimeoutException` now correctly triggers the retry mechanism in `AuthInterceptor` and `OAuthInterceptor`. Previously, network-level timeouts bypassed retry logic entirely, causing `.setRetry(true)` to have no effect on timeout errors. + ## v1.11.1 ### Apr 06, 2026 diff --git a/pom.xml b/pom.xml index c227e9ba..1ff53c24 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ cms jar contentstack-management-java - 1.11.1 + 1.11.2 Contentstack Java Management SDK for Content Management API, Contentstack is a headless CMS with an API-first approach diff --git a/src/main/java/com/contentstack/cms/core/AuthInterceptor.java b/src/main/java/com/contentstack/cms/core/AuthInterceptor.java index 80ee0f97..873f5912 100644 --- a/src/main/java/com/contentstack/cms/core/AuthInterceptor.java +++ b/src/main/java/com/contentstack/cms/core/AuthInterceptor.java @@ -1,6 +1,7 @@ package com.contentstack.cms.core; import java.io.IOException; +import java.net.SocketTimeoutException; import org.jetbrains.annotations.NotNull; @@ -116,21 +117,35 @@ public void setRetryConfig(RetryConfig retryConfig) { this.retryConfig = retryConfig != null ? retryConfig : RetryConfig.defaultConfig(); } - private Response executeRequest(Chain chain, Request request, int retryCount) throws IOException{ - Response response = chain.proceed(request); - int code = response.code(); - if(retryCount < retryConfig.getRetryLimit() && retryConfig.getRetryCondition().shouldRetry(code, null)){ - response.close(); - long delay = RetryUtil.calculateDelay(retryConfig, retryCount+1, code); - try { - Thread.sleep(delay); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - throw new IOException("Retry interrupted", ex); + private Response executeRequest(Chain chain, Request request, int retryCount) throws IOException { + try { + Response response = chain.proceed(request); + int code = response.code(); + if (retryCount < retryConfig.getRetryLimit() && retryConfig.getRetryCondition().shouldRetry(code, null)) { + response.close(); + long delay = RetryUtil.calculateDelay(retryConfig, retryCount + 1, code); + try { + Thread.sleep(delay); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new IOException("Retry interrupted", ex); + } + return executeRequest(chain, request, retryCount + 1); } - return executeRequest(chain, request, retryCount + 1); + return response; + } catch (SocketTimeoutException e) { + if (retryCount < retryConfig.getRetryLimit() && retryConfig.getRetryCondition().shouldRetry(0, e)) { + long delay = RetryUtil.calculateDelay(retryConfig, retryCount + 1, 0); + try { + Thread.sleep(delay); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new IOException("Retry interrupted", ex); + } + return executeRequest(chain, request, retryCount + 1); + } + throw e; } - return response; } } diff --git a/src/main/java/com/contentstack/cms/oauth/OAuthInterceptor.java b/src/main/java/com/contentstack/cms/oauth/OAuthInterceptor.java index 56f110a9..8feb5c63 100644 --- a/src/main/java/com/contentstack/cms/oauth/OAuthInterceptor.java +++ b/src/main/java/com/contentstack/cms/oauth/OAuthInterceptor.java @@ -1,6 +1,7 @@ package com.contentstack.cms.oauth; import java.io.IOException; +import java.net.SocketTimeoutException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -112,7 +113,22 @@ private Response executeRequest(Chain chain, Request request, int retryCount) th } // Execute request - Response response = chain.proceed(request); + Response response; + try { + response = chain.proceed(request); + } catch (SocketTimeoutException e) { + if (retryCount < retryConfig.getRetryLimit() && retryConfig.getRetryCondition().shouldRetry(0, e)) { + long delay = RetryUtil.calculateDelay(retryConfig, retryCount + 1, 0); + try { + Thread.sleep(delay); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new IOException("Retry interrupted", ex); + } + return executeRequest(chain, request, retryCount + 1); + } + throw e; + } // Handle error responses if (!response.isSuccessful() && retryCount < retryConfig.getRetryLimit()) { diff --git a/src/test/java/com/contentstack/cms/core/AuthInterceptorTest.java b/src/test/java/com/contentstack/cms/core/AuthInterceptorTest.java index 310f964d..efe1b867 100644 --- a/src/test/java/com/contentstack/cms/core/AuthInterceptorTest.java +++ b/src/test/java/com/contentstack/cms/core/AuthInterceptorTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import java.io.IOException; +import java.net.SocketTimeoutException; public class AuthInterceptorTest { @@ -166,6 +167,89 @@ public Call call() { } } + @Test + @Tag("unit") + public void testRetry_onSocketTimeout_thenSuccess_retriesAndReturnsSuccess() throws IOException { + authInterceptor.setRetryConfig(RetryConfig.builder().retryLimit(3).retryDelay(10).build()); + Request request = new Request.Builder() + .url("https://api.contentstack.io/v3/user") + .get() + .build(); + TimeoutTestChain chain = new TimeoutTestChain(request, 1, 200); + try (Response response = authInterceptor.intercept(chain)) { + Assertions.assertEquals(200, response.code()); + Assertions.assertEquals(2, chain.getProceedCount()); + } + } + + @Test + @Tag("unit") + public void testRetry_onSocketTimeout_exhaustsRetries_throws() { + authInterceptor.setRetryConfig(RetryConfig.builder().retryLimit(2).retryDelay(10).build()); + Request request = new Request.Builder() + .url("https://api.contentstack.io/v3/user") + .get() + .build(); + TimeoutTestChain chain = new TimeoutTestChain(request, 5, 200); + Assertions.assertThrows(SocketTimeoutException.class, () -> authInterceptor.intercept(chain)); + Assertions.assertEquals(3, chain.getProceedCount()); + } + + @Test + @Tag("unit") + public void testRetry_onSocketTimeout_zeroRetryLimit_throwsImmediately() { + authInterceptor.setRetryConfig(RetryConfig.builder().retryLimit(0).retryDelay(10).build()); + Request request = new Request.Builder() + .url("https://api.contentstack.io/v3/user") + .get() + .build(); + TimeoutTestChain chain = new TimeoutTestChain(request, 5, 200); + Assertions.assertThrows(SocketTimeoutException.class, () -> authInterceptor.intercept(chain)); + Assertions.assertEquals(1, chain.getProceedCount()); + } + + private static class TimeoutTestChain implements Interceptor.Chain { + private final Request originalRequest; + private final int timeoutCount; + private final int successCode; + private int proceedCount = 0; + + TimeoutTestChain(Request request, int timeoutCount, int successCode) { + this.originalRequest = request; + this.timeoutCount = timeoutCount; + this.successCode = successCode; + } + + int getProceedCount() { return proceedCount; } + + @Override + public Request request() { return originalRequest; } + + @Override + public Response proceed(Request request) throws IOException { + proceedCount++; + if (proceedCount <= timeoutCount) { + throw new SocketTimeoutException("timeout"); + } + return new Response.Builder() + .request(request) + .protocol(Protocol.HTTP_1_1) + .code(successCode) + .message("OK") + .body(ResponseBody.create("{}", MediaType.parse("application/json"))) + .build(); + } + + @Override public Connection connection() { return null; } + @Override public int connectTimeoutMillis() { return 0; } + @Override public Interceptor.Chain withConnectTimeout(int timeout, java.util.concurrent.TimeUnit unit) { return this; } + @Override public int readTimeoutMillis() { return 0; } + @Override public Interceptor.Chain withReadTimeout(int timeout, java.util.concurrent.TimeUnit unit) { return this; } + @Override public int writeTimeoutMillis() { return 0; } + @Override public Interceptor.Chain withWriteTimeout(int timeout, java.util.concurrent.TimeUnit unit) { return this; } + @Override public Call call() { return null; } + } + @Test public void AuthInterceptor() { AuthInterceptor expected = new AuthInterceptor("abc"); diff --git a/src/test/java/com/contentstack/cms/oauth/OAuthInterceptorTest.java b/src/test/java/com/contentstack/cms/oauth/OAuthInterceptorTest.java new file mode 100644 index 00000000..580f9268 --- /dev/null +++ b/src/test/java/com/contentstack/cms/oauth/OAuthInterceptorTest.java @@ -0,0 +1,118 @@ +package com.contentstack.cms.oauth; + +import com.contentstack.cms.core.RetryConfig; +import com.contentstack.cms.models.OAuthTokens; +import okhttp3.*; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.IOException; +import java.net.SocketTimeoutException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +@RunWith(MockitoJUnitRunner.class) +public class OAuthInterceptorTest { + + private OAuthInterceptor interceptor; + + @Mock + private OAuthHandler mockHandler; + + @Mock + private OAuthTokens mockTokens; + + @Before + public void setup() { + Mockito.lenient().when(mockTokens.isExpired()).thenReturn(false); + Mockito.lenient().when(mockTokens.hasAccessToken()).thenReturn(true); + Mockito.lenient().when(mockHandler.getTokens()).thenReturn(mockTokens); + Mockito.lenient().when(mockHandler.getAccessToken()).thenReturn("test-access-token"); + + interceptor = new OAuthInterceptor(mockHandler); + interceptor.setRetryConfig(RetryConfig.builder().retryLimit(3).retryDelay(10).build()); + } + + @Test + public void testRetry_onSocketTimeout_thenSuccess_retriesAndReturnsSuccess() throws IOException { + Request request = new Request.Builder() + .url("https://api.contentstack.io/v3/content_types") + .get() + .build(); + TimeoutTestChain chain = new TimeoutTestChain(request, 1, 200); + try (Response response = interceptor.intercept(chain)) { + assertEquals(200, response.code()); + assertEquals(2, chain.getProceedCount()); + } + } + + @Test + public void testRetry_onSocketTimeout_exhaustsRetries_throws() { + Request request = new Request.Builder() + .url("https://api.contentstack.io/v3/content_types") + .get() + .build(); + TimeoutTestChain chain = new TimeoutTestChain(request, 5, 200); + assertThrows(SocketTimeoutException.class, () -> interceptor.intercept(chain)); + assertEquals(4, chain.getProceedCount()); // 1 initial + 3 retries + } + + @Test + public void testRetry_onSocketTimeout_zeroRetryLimit_throwsImmediately() { + interceptor.setRetryConfig(RetryConfig.builder().retryLimit(0).retryDelay(10).build()); + Request request = new Request.Builder() + .url("https://api.contentstack.io/v3/content_types") + .get() + .build(); + TimeoutTestChain chain = new TimeoutTestChain(request, 5, 200); + assertThrows(SocketTimeoutException.class, () -> interceptor.intercept(chain)); + assertEquals(1, chain.getProceedCount()); + } + + private static class TimeoutTestChain implements Interceptor.Chain { + private final Request originalRequest; + private final int timeoutCount; + private final int successCode; + private int proceedCount = 0; + + TimeoutTestChain(Request request, int timeoutCount, int successCode) { + this.originalRequest = request; + this.timeoutCount = timeoutCount; + this.successCode = successCode; + } + + int getProceedCount() { return proceedCount; } + + @Override + public Request request() { return originalRequest; } + + @Override + public Response proceed(Request request) throws IOException { + proceedCount++; + if (proceedCount <= timeoutCount) { + throw new SocketTimeoutException("timeout"); + } + return new Response.Builder() + .request(request) + .protocol(Protocol.HTTP_1_1) + .code(successCode) + .message("OK") + .body(ResponseBody.create("{}", MediaType.parse("application/json"))) + .build(); + } + + @Override public Connection connection() { return null; } + @Override public int connectTimeoutMillis() { return 0; } + @Override public Interceptor.Chain withConnectTimeout(int timeout, java.util.concurrent.TimeUnit unit) { return this; } + @Override public int readTimeoutMillis() { return 0; } + @Override public Interceptor.Chain withReadTimeout(int timeout, java.util.concurrent.TimeUnit unit) { return this; } + @Override public int writeTimeoutMillis() { return 0; } + @Override public Interceptor.Chain withWriteTimeout(int timeout, java.util.concurrent.TimeUnit unit) { return this; } + @Override public Call call() { return null; } + } +} From c02bb73688f1424bc1d90d96ca1cb44a9414645c Mon Sep 17 00:00:00 2001 From: Aravind Kumar Date: Tue, 26 May 2026 18:25:07 +0530 Subject: [PATCH 5/6] Update README.md From 606752a13f9278aae04ed06dbb0c8a9a9a43c586 Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Thu, 28 May 2026 12:51:42 +0530 Subject: [PATCH 6/6] fix: enhance retry mechanism for network-level timeouts in AuthInterceptor and OAuthInterceptor; update version to 1.11.2 --- changelog.md | 1 + .../com/contentstack/cms/Contentstack.java | 12 +++++++++++ .../cms/ContentstackUnitTest.java | 21 +++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/changelog.md b/changelog.md index b69d012c..74e2cc78 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ ### Jun 01, 2026 - Fix: `SocketTimeoutException` now correctly triggers the retry mechanism in `AuthInterceptor` and `OAuthInterceptor`. Previously, network-level timeouts bypassed retry logic entirely, causing `.setRetry(true)` to have no effect on timeout errors. +- Enhancement: Added `setProtocols(List)` to the Builder, allowing callers to restrict the HTTP protocol (e.g. force HTTP/1.1 via `Collections.singletonList(Protocol.HTTP_1_1)`) for environments where proxies or intermediaries have issues with HTTP/2. ## v1.11.1 diff --git a/src/main/java/com/contentstack/cms/Contentstack.java b/src/main/java/com/contentstack/cms/Contentstack.java index ebe847b3..9c497a9e 100644 --- a/src/main/java/com/contentstack/cms/Contentstack.java +++ b/src/main/java/com/contentstack/cms/Contentstack.java @@ -3,7 +3,9 @@ import java.io.IOException; import java.net.Proxy; import java.time.Duration; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -30,6 +32,7 @@ import com.contentstack.cms.core.RetryConfig; import okhttp3.ConnectionPool; import okhttp3.OkHttpClient; +import okhttp3.Protocol; import okhttp3.ResponseBody; import okhttp3.logging.HttpLoggingInterceptor; import retrofit2.Response; @@ -609,6 +612,7 @@ public static class Builder { * evicted after 5 minutes of inactivity. */ private ConnectionPool connectionPool = new ConnectionPool(); // Connection + private List protocols = null; /** * Instantiates a new Builder. @@ -713,6 +717,11 @@ public Builder setConnectTimeout(int connectTimeoutSeconds) { return this; } + public Builder setProtocols(@NotNull List protocols) { + this.protocols = protocols; + return this; + } + private static void validateTimeoutSeconds(int seconds, String name) { if (seconds <= 0) { throw new IllegalArgumentException(name + " must be positive."); @@ -881,6 +890,9 @@ private OkHttpClient httpClient(Contentstack contentstack, Boolean retryOnFailur .readTimeout(Duration.ofSeconds(readSec)) .writeTimeout(Duration.ofSeconds(writeSec)) .retryOnConnectionFailure(retryOnFailure); + if (this.protocols != null && !this.protocols.isEmpty()) { + builder.protocols(this.protocols); + } // Add either OAuth or traditional auth interceptor if (this.oauthConfig != null) { diff --git a/src/test/java/com/contentstack/cms/ContentstackUnitTest.java b/src/test/java/com/contentstack/cms/ContentstackUnitTest.java index 9e84c9b8..1da49474 100644 --- a/src/test/java/com/contentstack/cms/ContentstackUnitTest.java +++ b/src/test/java/com/contentstack/cms/ContentstackUnitTest.java @@ -5,6 +5,7 @@ import com.contentstack.cms.stack.Stack; import okhttp3.Headers; import okhttp3.OkHttpClient; +import okhttp3.Protocol; import okhttp3.Request; import okhttp3.ResponseBody; import okhttp3.mockwebserver.MockResponse; @@ -17,7 +18,9 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.net.Proxy; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -160,6 +163,24 @@ void setVersion() { Assertions.assertEquals("v8", contentstack.version); } + @Test + void setProtocols_http1Only_buildsSuccessfully() { + List protocols = Collections.singletonList(Protocol.HTTP_1_1); + Contentstack contentstack = new Contentstack.Builder() + .setProtocols(protocols) + .build(); + Assertions.assertNotNull(contentstack); + } + + @Test + void setProtocols_http2AndHttp1_buildsSuccessfully() { + List protocols = List.of(Protocol.HTTP_2, Protocol.HTTP_1_1); + Contentstack contentstack = new Contentstack.Builder() + .setProtocols(protocols) + .build(); + Assertions.assertNotNull(contentstack); + } + @Test void setTimeout() { Contentstack contentstack = new Contentstack.Builder()