Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .bito.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
suggestion_mode: comprehensive
post_description: true
post_changelist: true
exclude_files: 'pom.xml.versionsBackup'
exclude_draft_pr: false
secret_scanner_feedback: true
linters_feedback: true
repo_level_guidelines_enabled: true
sequence_diagram_enabled: true
custom_guidelines:
general:
- name: 'Review Posture'
path: './.bito/guidelines/review-posture.txt'
- name: 'Repo Truth And Alignment'
path: './.bito/guidelines/repo-truth-and-boundaries.txt'
- name: 'Domain Invariants'
path: './.bito/guidelines/domain-invariants.txt'

# Generated by seed-golden-context | Last updated: 2026-05-11
21 changes: 21 additions & 0 deletions .bito/guidelines/domain-invariants.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Critical domain invariants for contentful-management.java. Violations of these rules will cause runtime failures, silent data loss, or consumer-facing breakage.

1. ENVIRONMENT SCOPING: The modules apiKeys, previewApiKeys, roles, spaceMemberships, uploads, and webhooks do NOT support non-master environments. If a module that extends AbsModule operates in a space-only context, it must call throwIfEnvironmentIdIsSet() in its constructor or before any operation. Failing to do this causes CMANotWithEnvironmentsException at runtime.

2. PUBLIC API STABILITY: All CMA* model classes, CMAClient builder methods, and Module* public methods are part of the published public API. Removing or renaming them is a breaking change. Adding required parameters to existing methods is a breaking change. Check the CHANGELOG and semver implications before modifying these.

3. JAVA 8 TARGET: Production code in src/main/java/ must compile and run on Java 8 and Android API 21+. Do not use Java 9+ APIs (var, List.of(), Map.of(), Optional.ifPresentOrElse(), etc.) or any class not present on Android API 21. Tests in src/test/kotlin/ are exempt.

4. GSON TYPE ADAPTERS: All CMA model types with non-trivial wire formats have custom Gson type adapters registered in CMAClient. If you add a new model type that needs custom serialization, register its adapter in CMAClient — it will not be picked up automatically.

5. RICH TEXT HIERARCHY: The CMA enforces a strict rich text node hierarchy. The SDK models it but does NOT validate at write time. Document cannot nest Document; Links must be inside Paragraphs; Lists must be inside Document; ListItems must contain at least one Paragraph. Violations produce a 422 from the API, not an SDK exception.

6. CALLBACK DEFAULT: CMACallback.onFailure() has an empty default implementation. Callers who do not override it will silently swallow errors. When reviewing async code, check that failures are handled.

7. CHECKSTYLE: checkstyle.xml at the repo root is the enforced style config. All Java production code must pass it. Do not disable checkstyle rules without a strong documented reason.

8. MODULE PATTERN: Every new CMA resource type needs both a Module*.java (public API) and a Service*.java (Retrofit interface). The module must be registered in CMAClient. Do not bypass AbsModule.

9. SENSITIVE LOGGING: The logSensitiveData flag in CMAClient controls whether Authorization headers appear in logs. Default is false. Never change this default or log auth tokens unconditionally.

# Generated by seed-golden-context | Last updated: 2026-05-11
11 changes: 11 additions & 0 deletions .bito/guidelines/repo-truth-and-boundaries.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Use the repository's written documentation as review context and check whether the change matches the documented intent.

- Start from README.md, ARCHITECTURE.md, AGENTS.md, CONTRIBUTING.md, and docs/ADRs/ for architectural context.
- Check whether code, tests, and documentation all tell the same story. Flag mismatches between implementation and the documented architecture or ADRs.
- Treat AGENTS.md as the authoritative guide for sharp edges and invariants. If a change violates an invariant documented there, flag it.
- If CI or another required check already enforces a merge rule, do not ask for duplicate PR template sections or manual checklists.
- Ask for an ADR update when a change is architecture-significant: new module, new runtime dependency, new async pattern, new publishing target, changes to environment scoping rules.
- Distinguish the current public Java API from internal implementation details. Changes to public `CMA*` model classes, `CMAClient` builder options, or `Module*` method signatures require extra scrutiny — this is a published library with downstream consumers.
- The `Service*.java` interfaces are Retrofit annotations and define the exact HTTP contract with the CMA. Changes here must match the actual CMA API behavior.

# Generated by seed-golden-context | Last updated: 2026-05-11
11 changes: 11 additions & 0 deletions .bito/guidelines/review-posture.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Review this pull request like the tech lead of the contentful-management.java project — a Java SDK for Contentful's Content Management API, owned by the Developer Experience team.

- Prefer a few high-signal findings to a long list of minor or style-only comments.
- Prefer behavior, contract, runtime, and documentation issues over process-only suggestions. Do not ask for duplicate PR template sections or manual validation acknowledgements when CI or required checks already enforce that policy.
- Keep feedback actionable: explain why it matters, how it would surface in practice, and the clearest next step.
- If a concern is only a risk or assumption rather than a confirmed bug, say that clearly and explain what evidence would confirm it.
- If you find no issues, say so explicitly and call out any residual uncertainty that still deserves human attention.
- Pay special attention to public API surface changes — this is a published library. Breaking changes require strong justification and must be clearly called out.
- Checkstyle enforcement is automatic — do not comment on code style if the CI checkstyle gate already catches it.

# Generated by seed-golden-context | Last updated: 2026-05-11
59 changes: 59 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Agent Guide

<!-- Generated by seed-golden-context | Last updated: 2026-05-11 -->

Read this file first. It tells you where to find context in this repo.

## Quick Reference

| What you need | Where to look |
|---|---|
| How this repo is structured | [ARCHITECTURE.md](./ARCHITECTURE.md) |
| How to build/test/run | [CONTRIBUTING.md](./CONTRIBUTING.md) |
| Why decisions were made | [docs/ADRs/](./docs/ADRs/) |
| What this repo does | [README.md](./README.md) |
| PR review rules | [.bito/guidelines/](./.bito/guidelines/) |
| Active specs/work | [docs/specs/](./docs/specs/) |

## Sharp Edges & Invariants

- **Never call environment-scoped operations on the wrong modules.** The modules `apiKeys`, `previewApiKeys`, `roles`, `spaceMemberships`, `uploads`, and `webhooks` throw `CMANotWithEnvironmentsException` if the client is configured with a non-`master` `environmentId`. Always use a separate client (no `environmentId`) for those modules.
- **Java 8 is the minimum target — do not use Java 9+ APIs in production code.** The Android dependency (`com.google.android:android`) is marked `optional` but must remain compilable on API 21+. Tests may use newer JVM features.
- **Do not add new runtime dependencies without an ADR.** The dependency footprint is deliberately small to keep Android app size predictable.
- **Every Module must extend `AbsModule<T>`.** The base class handles argument validation, executor injection, and space/environment ID pre-configuration. Never bypass it.
- **Custom Gson adapters must be registered in `CMAClient`.** The Gson instance is built once in `CMAClient` and shared across all modules. New model types with non-trivial wire formats need a type adapter registered there.
- **RichText hierarchy rules are enforced by the API** — the SDK models them but does not validate at write time. Violating the hierarchy (Document in Document, Link outside Paragraph, etc.) will produce a 422 from the CMA. See `ARCHITECTURE.md` for the full rule set.
- **The `CMACallback.onFailure()` default implementation is empty.** Code that needs to handle failures must override it explicitly. This is a common source of silently dropped errors.
- **Test files are Kotlin; production code is Java.** Keep this split. Do not introduce Kotlin into `src/main/java/`.
- **Public API surface is stable — treat changes as breaking.** All `CMA*` model classes, `CMAClient` builder methods, and `Module*` public methods are part of the published API. Removing, renaming, or adding required parameters to any of these is a breaking change and requires a semver major bump. Review CHANGELOG implications before modifying them.
- **Never log auth tokens unconditionally.** The `logSensitiveData` flag in `CMAClient` controls whether `Authorization` headers appear in logs. Its default is `false`. Do not change that default or add unconditional logging of auth/credentials anywhere in the SDK.
- **Checkstyle runs during `./mvnw verify` (not `./mvnw test`).** CI only runs `./mvnw -B test`, so checkstyle is not enforced in CI — run `./mvnw -B verify` locally before pushing to catch violations. The config is in `checkstyle.xml` at the repo root.

## Key Conventions

- **Commit format:** Conventional Commits (`feat:`, `fix:`, `chore:`, `docs:`, etc.) — not enforced by a hook, but followed by convention
- **Branch strategy:** `master` is the main branch; feature/fix branches merged via PR; releases use `release/X.Y.Z` branches
- **Test location:** `src/test/kotlin/com/contentful/java/cma/` (unit) and `src/test/kotlin/com/contentful/java/cma/e2e/` (integration)
- **Build system:** Maven Wrapper (`./mvnw`) — always use the wrapper, never a system Maven install
- **Module pattern:** One `Module*.java` + one `Service*.java` per CMA resource type
- **Model naming:** All resource model classes are prefixed `CMA` (e.g., `CMAEntry`, `CMASpace`)

## Integration Points

**Upstream (this repo consumes):**
- Contentful CMA (`api.contentful.com`) — all content management operations
- Contentful Upload API (`upload.contentful.com`) — binary asset upload

**Downstream (consumes this repo):**
- Java and Android applications that manage Contentful content
- Published to Maven Central as `com.contentful.java:cma-sdk`

## Build & Quality

```bash
# Run tests (does NOT include checkstyle)
./mvnw -B test

# Full verification (tests + checkstyle) inside devcontainer
./mvnw -B verify
```
108 changes: 108 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Architecture

<!-- Generated by seed-golden-context | Last updated: 2026-05-11 -->

## Overview

`contentful-management.java` is the official Java SDK for Contentful's [Content Management API (CMA)](https://www.contentful.com/developers/docs/references/content-management-api/). It provides a strongly-typed, fluent Java client that wraps every CMA endpoint, supports both synchronous and asynchronous (RxJava2-backed) call patterns, and runs on Java 8+ including Android (API 21+).

## System Context

```mermaid
graph TD
App["Java / Android Application"] --> CMAClient["CMAClient (this SDK)"]
CMAClient --> Retrofit["Retrofit2 + OkHttp"]
Retrofit --> CMA["Contentful CMA\n(api.contentful.com)"]
CMAClient --> Gson["Gson (de/serialization)"]
CMAClient --> RxJava2["RxJava2 (async adapter)"]
```

**Upstream:** Contentful Content Management API — authenticated via Personal Access Token (PAT) or OAuth token passed as `Authorization: Bearer <token>`.

**Downstream consumers:** Any Java or Android application that needs to programmatically create, read, update, or delete Contentful content, spaces, environments, or configuration resources.

## Internal Structure

| Directory / Package | Purpose |
|---|---|
| `src/main/java/com/contentful/java/cma/` | Core SDK: `CMAClient`, all `Module*` and `Service*` classes, interceptors, Gson adapters |
| `src/main/java/com/contentful/java/cma/model/` | All `CMA*` resource model POJOs (entries, assets, content types, etc.) |
| `src/main/java/com/contentful/java/cma/model/rich/` | Rich Text node hierarchy models |
| `src/main/java/com/contentful/java/cma/gson/` | Custom Gson serializers and deserializers for CMA-specific wire formats |
| `src/main/java/com/contentful/java/cma/interceptor/` | OkHttp interceptors: auth header, content-type, rate-limit, error, logging, user-agent |
| `src/main/resources/com/contentful/java/cma/build/` | Build-time generated `GeneratedBuildParameters.java` containing `PROJECT_VERSION` |
| `src/test/kotlin/com/contentful/java/cma/` | Unit tests (Kotlin + JUnit 4 + OkHttp MockWebServer) |
| `src/test/kotlin/com/contentful/java/cma/e2e/` | End-to-end integration tests (require live CMA credentials) |
| `src/test/resources/` | Mock JSON response fixtures for unit tests |
| `.devcontainer/` | Devcontainer config (Temurin JDK 8, multi-arch) — used by both local dev and CI |
| `.github/workflows/` | GitHub Actions CI: runs `./mvnw -B test` inside devcontainer |

## Module / Service Pattern

Each CMA resource type has two classes:

- **`Module<Resource>.java`** — public-facing API: validates arguments, wraps synchronous `service.*` calls, and exposes an `.async()` accessor returning a callback-based counterpart.
- **`Service<Resource>.java`** — Retrofit2 `@interface` defining the raw HTTP endpoints for that resource.

All modules extend `AbsModule<T>` which holds references to the Retrofit service, callback executor, and optionally pre-configured `spaceId` / `environmentId`.

```
CMAClient
└── builds Retrofit instance (with interceptor chain)
└── instantiates one Module per resource type
└── Module delegates to Service (Retrofit interface)
└── results wrapped via RxJava2 Observable
```

## Data Flow

1. **Client creation** — `CMAClient.Builder` assembles an OkHttp client with auth, user-agent, content-type, rate-limit, error, and logging interceptors, then builds a Retrofit instance backed by Gson + RxJava2.
2. **Synchronous call** — `client.entries().fetchAll(spaceId, environmentId)` → Module validates args → calls Retrofit service → blocks via `SynchronousExecutor` → returns typed result.
3. **Asynchronous call** — `client.entries().async().fetchAll(...)` → same service call → emits via RxJava2 Observable → marshalled onto the provided callback `Executor` → fires `CMACallback.onSuccess` / `onFailure`.
4. **Deserialization** — Gson with custom type adapters (`CMASystemDeserializer`, `EntrySerializer`, `FieldTypeAdapter`, etc.) converts CMA JSON wire format into `CMA*` POJOs.
5. **Error handling** — `ErrorInterceptor` converts non-2xx HTTP responses into `CMAHttpException`; `RateLimitInterceptor` surfaces rate-limit headers via `RateLimitsListener`.

## Key Dependencies

| Dependency | Why it's here |
|---|---|
| `retrofit2` (2.9.0) | Declarative HTTP client; supports interceptor chain and multiple call adapters. See [ADR-0002](./docs/ADRs/0002-retrofit2-as-http-client.md) |
| `retrofit2:adapter-rxjava2` | Enables async Observable-based responses bridging to CMACallback. See [ADR-0003](./docs/ADRs/0003-rxjava2-async-pattern.md) |
| `retrofit2:converter-gson` | JSON serialization; Gson chosen for Android compatibility and custom type adapter support |
| `com.google.code.gson` (2.8.9) | Custom de/serialization for CMA-specific types (rich text, snapshots, webhooks) |
| `io.reactivex.rxjava2:rxjava` (2.2.5) | Reactive foundation for async call pattern |
| `kotlin-stdlib` | Used in production code (minimal); tests are written entirely in Kotlin |
| `okhttp3` (test scope) | MockWebServer for unit tests; OkHttp itself is an optional runtime dep |
| `com.google.android:android` (optional) | Android API stubs — makes SDK compatible with Android API 21+ without requiring it |
| `junit` (4.13.1) | Test runner for unit tests |
| `org.jetbrains.kotlin:kotlin-test-junit` | Kotlin test assertions in unit test suite |

## Configuration

| Parameter | Purpose | Default |
|---|---|---|
| `accessToken` | CMA Personal Access Token or OAuth token (required) | — |
| `spaceId` | Pre-configure space ID so modules omit it per-call | `null` (pass per-call) |
| `environmentId` | Pre-configure environment ID | `null` (defaults to `master` for most endpoints) |
| `callbackExecutor` | `Executor` for async callback delivery | `Executors.newCachedThreadPool()` |
| `coreEndpoint` | Override CMA base URL (e.g., for proxying) | `https://api.contentful.com` |
| `uploadEndpoint` | Override upload endpoint | `https://upload.contentful.com` |
| `logLevel` | `Logger.Level` — `NONE`, `BASIC`, `HEADERS`, `FULL` | `NONE` |
| `logSensitiveData` | Whether to include auth headers in logs | `false` |
| `httpClient` | Custom `okhttp3.Call.Factory` | OkHttp if on classpath, else `HttpURLConnection` |

## Integration Points

### Upstream (this repo consumes)

- **Contentful CMA** (`api.contentful.com`) — all REST operations
- **Contentful Upload API** (`upload.contentful.com`) — binary asset uploads

### Downstream (consumes this repo)

- Any Java/Android application that needs to manage Contentful content programmatically
- Distributed via Maven Central (`com.contentful.java:cma-sdk`) and Sonatype snapshots

## Environment Scoping Rules (Sharp Edge)

The modules `apiKeys`, `environments`, `roles`, `spaceMemberships`, `uiExtensions`, `uploads`, and `webhooks` **do not support non-`master` environments**. If `CMAClient` is configured with a non-master `environmentId`, these modules throw `CMANotWithEnvironmentsException`. Always create a separate client without an `environmentId` for these modules.
65 changes: 65 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,68 @@ devcontainer exec --workspace-folder . bash
1. Fork the repository and create a branch for your change.
2. Run the relevant checks from the dev container.
3. Open a pull request with a short summary of the change and any follow-up context.

<!-- Generated by seed-golden-context | Last updated: 2026-05-11 -->

## Testing

- **Framework:** JUnit 4 (runner), Kotlin test assertions (`kotlin-test-junit`), OkHttp `MockWebServer`
- **Unit tests:** `src/test/kotlin/com/contentful/java/cma/` — each resource module has a corresponding `*Tests.kt` file
- **E2E / integration tests:** `src/test/kotlin/com/contentful/java/cma/e2e/` — require live CMA credentials (`CONTENTFUL_ACCESS_TOKEN`, `CONTENTFUL_SPACE_ID`)
- **Run all tests:** `./mvnw -B test`
- **Run specific test class:** `./mvnw -B -Dtest=EntryTests test`
- **Test fixtures:** JSON response mocks are in `src/test/resources/`

## Code Style

Checkstyle is enforced on every `verify` run:

```bash
./mvnw -B verify
```

The configuration lives in `checkstyle.xml` at the repo root. Violations will fail the build. Run `./mvnw -B verify` locally before pushing to catch checkstyle violations — CI only runs `./mvnw -B test`, which skips the checkstyle phase.

## Commit Convention

This repo follows [Conventional Commits](https://www.conventionalcommits.org/) by convention:

```
type(scope): description
```

Valid types: `feat`, `fix`, `chore`, `docs`, `refactor`, `test`, `perf`, `ci`, `build`

Examples:
```
feat(entries): add PATCH support for partial updates
fix(rich-text): correct nodeType deserialization for embedded entries
chore: update OkHttp to 4.12.0
```

## Branch Strategy

- `master` — the main branch; all PRs target here
- Feature/fix branches — use descriptive names, e.g., `feat/add-taxonomy-module`, `fix/rate-limit-retry`
- Release branches — `release/X.Y.Z` used during the release process

## Release Process

Releases are managed via `maven-release-plugin`:

```bash
# Prepare a release (updates pom.xml versions, creates release tag)
./mvnw release:prepare

# Perform the release (builds, signs, and publishes to Sonatype Central)
./mvnw release:perform
```

Artifacts are published to Maven Central under `com.contentful.java:cma-sdk`. Pre-release snapshots are available via Sonatype snapshots repository and [jitpack.io](https://jitpack.io/#contentful/contentful-management.java/master-SNAPSHOT).

## CI/CD

| Job | Trigger | What it does |
|---|---|---|
| `CI / Test (Java 8)` | Push to `master`, PR to `master` | Spins up devcontainer, runs `./mvnw -B test` |
| `CodeQL` | Push / PR / weekly schedule | Static analysis for security vulnerabilities |
Loading
Loading