Skip to content

Commit ca23a72

Browse files
authored
Merge pull request #103 from contentstack/fix/DX-5361
Add agent guide and development workflow documentation
2 parents c59d64d + 512085d commit ca23a72

File tree

12 files changed

+488
-37
lines changed

12 files changed

+488
-37
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
description: Contentstack CDA patterns – Stack/Config, HTTP, retry, callbacks, Content Delivery API
3+
globs: "contentstack/src/main/java/com/contentstack/sdk/**/*.java", "contentstack/src/main/java/com/contentstack/sdk/**/*.kt"
4+
---
5+
6+
# Contentstack Android CDA – SDK Rules
7+
8+
Apply when editing the SDK core (`com.contentstack.sdk`). Keep behavior aligned with the [Content Delivery API](https://www.contentstack.com/docs/apis/content-delivery-api/).
9+
10+
## Stack and Config
11+
12+
- **Entry point:** `Contentstack.stack(Context, apiKey, deliveryToken, environment)` returns a `Stack`. Use `Config` for optional settings (host, version, region, branch, proxy, connection pool).
13+
- **Default host:** `cdn.contentstack.io`; **API version:** `v3` (see `Config`).
14+
- **Config options:** host, version, environment, branch, region (`ContentstackRegion`), proxy, connection pool, endpoint. Set these via `Config` before building the stack.
15+
- **Region/branch:** Support `Config.setRegion()` and `Config.setBranch()` for regional and branch-specific delivery.
16+
17+
## HTTP layer
18+
19+
- **Requests** use **`CSHttpConnection`** (Volley) and/or **Retrofit** + **OkHttp** (e.g. `Stack`, `APIService`). Do not bypass these for CDA calls.
20+
- **Headers:** Use the same headers and User-Agent as the rest of the SDK (see constants and request setup in `CSHttpConnection` and Stack/APIService).
21+
- **Errors:** Map API errors to the SDK **`Error`** class and pass to **`ResultCallBack`** or equivalent callback.
22+
23+
## Retry and resilience
24+
25+
- **Retry:** Volley uses `DefaultRetryPolicy` (e.g. in `CSHttpConnection`); constants in `SDKConstant` (e.g. `TimeOutDuration`, `NumRetry`, `BackOFMultiplier`). Keep retry/timeout behavior consistent when changing the HTTP layer.
26+
27+
## Callbacks and async
28+
29+
- Use existing callback types (e.g. **`ResultCallBack`**, **`EntryResultCallBack`**, **`QueryResultsCallBack`**) for async results. Do not introduce incompatible callback signatures without considering backward compatibility.
30+
- Pass **`Error`** and response data through the same callback patterns used elsewhere in the SDK.
31+
32+
## CDA concepts
33+
34+
- **Entry,** **Query,** **Asset,** **Content Type,** **Sync** – follow existing class and method names and the CDA API semantics (query params, response parsing). When adding new CDA features, align with the official Content Delivery API documentation.

.cursor/rules/dev-workflow.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Development Workflow – Contentstack Android CDA SDK
2+
3+
Use this as the standard workflow when contributing to the Android CDA SDK.
4+
5+
## Branches
6+
7+
- Use feature branches for changes (e.g. `feat/...`, `fix/...`).
8+
- Base work off the appropriate long-lived branch (e.g. `staging`, `development`) per team norms.
9+
10+
## Running tests
11+
12+
- **Unit tests:** `./gradlew :contentstack:testDebugUnitTest`
13+
- **Instrumented / connected tests:** `./gradlew :contentstack:connectedDebugAndroidTest` (device or emulator required)
14+
- **Full test pass:** `./gradlew :contentstack:testDebugUnitTest :contentstack:connectedDebugAndroidTest`
15+
- **Coverage report:** `./gradlew :contentstack:jacocoTestReport`
16+
17+
Run unit tests before opening a PR. Instrumented tests may require `local.properties` with stack credentials (see `contentstack/build.gradle` buildConfigField usage).
18+
19+
## Pull requests
20+
21+
- Ensure the build passes: `./gradlew :contentstack:assembleDebug :contentstack:testDebugUnitTest`
22+
- Follow the **code-review** rule (`.cursor/rules/code-review.mdc`) for the PR checklist.
23+
- Keep changes backward-compatible for public API; call out any breaking changes clearly.
24+
25+
## Optional: TDD
26+
27+
If the team uses TDD, follow RED–GREEN–REFACTOR when adding behavior. The **testing** rule and **skills/testing** skill describe test structure and naming.

.cursor/rules/java.mdc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
description: Java/Kotlin style and com.contentstack.sdk conventions for the Android CDA SDK
3+
globs: "**/*.java", "**/*.kt"
4+
---
5+
6+
# Java / Kotlin Standards – Contentstack Android CDA SDK
7+
8+
Apply these conventions when editing Java (or Kotlin) code in this repository.
9+
10+
## Language and runtime
11+
12+
- **Java:** Target **Java 8** compatibility for the library (see `contentstack/build.gradle` compileOptions). Avoid language or API features that require a higher version without updating the module.
13+
- **Kotlin:** If present, follow existing Kotlin style and interop with `com.contentstack.sdk`; prefer null-safety and idiomatic Kotlin where it does not break public API.
14+
- Avoid raw types; use proper generics where applicable.
15+
16+
## Package and layout
17+
18+
- All SDK code lives under **`com.contentstack.sdk`** (see `contentstack/src/main/java/com/contentstack/sdk/`).
19+
- Keep the existing package structure; do not introduce new top-level packages without alignment with the rest of the SDK.
20+
21+
## Naming
22+
23+
- **Classes:** PascalCase (e.g. `CSHttpConnection`, `Config`).
24+
- **Methods/variables:** camelCase.
25+
- **Constants:** UPPER_SNAKE_CASE (e.g. in `SDKConstant`, `ErrorMessages`).
26+
- **Test classes:** `Test*` for unit tests; instrumented tests in `androidTest` (see **testing.mdc**).
27+
28+
## Logging
29+
30+
- Use **Android `Log`** or project logging as in `CSHttpConnection` (TAG-based). Obtain loggers with a class-named TAG.
31+
- Log at appropriate levels (e.g. `Log.w` for recoverable issues, `Log.d` for debug).
32+
33+
## Null-safety and annotations
34+
35+
- Use **`@NonNull`** / **`@Nullable`** (Android or JetBrains) where the project already does, to document nullability for public API.
36+
- Validate or document parameters for public methods where NPEs would be surprising.
37+
38+
## General
39+
40+
- Prefer immutability where practical (e.g. final fields, defensive copies for collections).
41+
- Document public API with Javadoc; keep examples in Javadoc in sync with actual usage (e.g. `Contentstack.stack(...)`, `Config`).

.cursor/rules/testing.mdc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
description: JUnit 4, Robolectric, androidTest, test naming, JaCoCo
3+
globs: "contentstack/src/test/**/*.java", "contentstack/src/test/**/*.kt", "contentstack/src/androidTest/**/*.java", "contentstack/src/androidTest/**/*.kt"
4+
---
5+
6+
# Testing Rules – Contentstack Android CDA SDK
7+
8+
Apply when writing or editing tests. The project uses **JUnit 4**, **Robolectric** (unit), **AndroidX Test** / **Espresso** (instrumented), and **JaCoCo** (see `contentstack/build.gradle`).
9+
10+
## Test naming and layout
11+
12+
- **Unit tests:** Class name prefix **`Test`** (e.g. `TestEntry`, `TestStack`). Place in `contentstack/src/test/java/com/contentstack/sdk/`.
13+
- **Instrumented tests:** Place in `contentstack/src/androidTest/java/com/contentstack/sdk/`. Use **AndroidJUnitRunner**; naming may use `*TestCase` or `Test*` as in the project (e.g. `AssetTestCase`, `EntryTestCase`).
14+
15+
## JUnit 4 usage
16+
17+
- Use **JUnit 4** APIs: `@Test`, `@Before`, `@After`, `@BeforeClass`, `@AfterClass`.
18+
- Use **assertions** from `org.junit.Assert` (e.g. `assertEquals`, `assertNotNull`).
19+
- For unit tests on JVM, **Robolectric** provides Android context; use `Robolectric.buildService(...)` or equivalent where a Context is needed.
20+
21+
## Unit vs instrumented
22+
23+
- **Unit tests** (`src/test/`): Run on JVM with Robolectric; use **MockWebServer** (OkHttp) for HTTP when appropriate; mock dependencies with Mockito/PowerMock as needed.
24+
- **Instrumented tests** (`src/androidTest/`): Run on device/emulator; may use real stack credentials from `BuildConfig` / `local.properties`; avoid flakiness (timeouts, IdlingResource if using Espresso).
25+
26+
## Test data and credentials
27+
28+
- **Credentials:** Instrumented tests may use `BuildConfig` fields (APIKey, deliveryToken, environment, host) populated from `local.properties`. Do not commit real tokens; document required keys in README or test docs.
29+
- **Fixtures:** Use existing test assets and JSON under `src/test/` where applicable.
30+
31+
## Coverage
32+
33+
- **JaCoCo** is configured in `contentstack/build.gradle`. Run `./gradlew :contentstack:jacocoTestReport` for unit-test coverage (e.g. `contentstack/build/reports/jacoco/`). Maintain or improve coverage when adding or changing production code.

.github/workflows/sca-scan.yml

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ jobs:
77
runs-on: ubuntu-latest
88
steps:
99
- uses: actions/checkout@master
10+
11+
- name: Set up JDK 17
12+
uses: actions/setup-java@v4
13+
with:
14+
distribution: 'temurin'
15+
java-version: '17'
16+
cache: 'gradle'
17+
18+
- name: Set up Android SDK
19+
uses: android-actions/setup-android@v3
20+
with:
21+
packages: 'tools platform-tools platforms;android-34 build-tools;34.0.0'
22+
1023
- name: Setup local.properties
1124
run: |
1225
cat << EOF >> local.properties
@@ -18,12 +31,16 @@ jobs:
1831
contentType="${{ secrets.CONTENT_TYPE }}"
1932
assetUid="${{ secrets.ASSET_UID }}"
2033
EOF
21-
- name: Run Snyk to check for vulnerabilities
34+
35+
- name: Set up Snyk
2236
uses: snyk/actions/setup@master
37+
with:
38+
snyk-version: latest
39+
40+
- name: Run Snyk
2341
env:
2442
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
25-
with:
26-
args: --fail-on=all --all-sub-projects
27-
json: true
43+
run: snyk test --fail-on=all --all-sub-projects --json > snyk.json
2844
continue-on-error: true
45+
2946
- uses: contentstack/sca-policy@main

AGENTS.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Contentstack Android CDA SDK – Agent Guide
2+
3+
This document is the main entry point for AI agents working in this repository.
4+
5+
## Project
6+
7+
- **Name:** Contentstack Android CDA SDK (contentstack-android)
8+
- **Purpose:** Android client for the Contentstack **Content Delivery API (CDA)**. It fetches content (entries, assets, content types, sync, etc.) from Contentstack for Android apps.
9+
- **Repo:** [contentstack-android](https://github.com/contentstack/contentstack-android)
10+
11+
## Tech stack
12+
13+
- **Languages:** Java (primary SDK source); Kotlin may appear in tests or future code. Target **Java 8** compatibility for the library (see `contentstack/build.gradle`).
14+
- **Build:** Gradle (Android Gradle Plugin), single module **`contentstack`** (AAR).
15+
- **Testing:** JUnit 4, Robolectric (unit tests on JVM), Mockito / PowerMock where used; **androidTest** with AndroidX Test / Espresso for instrumented tests; JaCoCo for coverage (`jacocoTestReport`).
16+
- **HTTP:** **Volley** (`CSHttpConnection`) for much of the CDA traffic; **Retrofit 2 + OkHttp + Gson** for paths such as taxonomy (`Stack`, `APIService`). OkHttp **MockWebServer** in unit tests.
17+
18+
## Main entry points
19+
20+
- **`Contentstack`** – Factory: `Contentstack.stack(Context, apiKey, deliveryToken, environment)` (and overloads with `Config`) returns a **`Stack`**.
21+
- **`Stack`** – Main API: content types, entries, queries, assets, sync, etc.
22+
- **`Config`** – Optional configuration: host, version, region, branch, proxy, connection pool, endpoint.
23+
- **Paths:** `contentstack/src/main/java/com/contentstack/sdk/` (production), `contentstack/src/test/java/com/contentstack/sdk/` (unit tests), `contentstack/src/androidTest/java/` (instrumented tests).
24+
25+
## Commands
26+
27+
Run from the **repository root** (requires Android SDK / `local.properties` for connected tests).
28+
29+
| Goal | Command |
30+
|------|---------|
31+
| **Build library (debug)** | `./gradlew :contentstack:assembleDebug` |
32+
| **Run all unit tests** | `./gradlew :contentstack:testDebugUnitTest` |
33+
| **Run instrumented / connected tests** | `./gradlew :contentstack:connectedDebugAndroidTest` (device or emulator required) |
34+
| **Unit + connected (full local test pass)** | `./gradlew :contentstack:testDebugUnitTest :contentstack:connectedDebugAndroidTest` |
35+
| **Coverage report (unit)** | `./gradlew :contentstack:jacocoTestReport` |
36+
37+
Instrumented tests may need **`local.properties`** entries (e.g. `APIKey`, `deliveryToken`, `environment`, `host`) for stacks that hit a real CDA endpoint—see `contentstack/build.gradle` `buildConfigField` usage.
38+
39+
## Rules and skills
40+
41+
- **`.cursor/rules/`** – Cursor rules for this repo:
42+
- **README.md** – Index of all rules (globs / always-on).
43+
- **dev-workflow.md** – Branches, tests, PR expectations.
44+
- **java.mdc** – Applies to `**/*.java` and `**/*.kt`: language style, `com.contentstack.sdk` layout, logging, null-safety.
45+
- **contentstack-android-cda.mdc** – SDK core: CDA patterns, Stack/Config, host/version/region/branch, retry, callbacks, CDA alignment.
46+
- **testing.mdc**`contentstack/src/test/**` and `contentstack/src/androidTest/**`: naming, unit vs instrumented, JaCoCo.
47+
- **code-review.mdc** – Always applied: PR/review checklist (aligned with Java CDA SDK).
48+
- **`skills/`** – Reusable skill docs:
49+
- **contentstack-android-cda** – Implementing or changing CDA behavior (Stack/Config, entries, assets, sync, HTTP, callbacks).
50+
- **testing** – Writing or refactoring tests.
51+
- **code-review** – PR review / pre-PR checklist.
52+
- **framework** – Config, HTTP layer (Volley + Retrofit/OkHttp), retry/timeouts.
53+
54+
Refer to `.cursor/rules/README.md` and `skills/README.md` for details.

contentstack/build.gradle

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ android {
4444
includeAndroidResources = true
4545
returnDefaultValues = true
4646
all {
47+
testLogging {
48+
events 'passed', 'skipped', 'failed'
49+
}
4750
jacoco {
4851
includeNoLocationClasses = true
4952
excludes = ['jdk.internal.*']
@@ -126,61 +129,57 @@ dependencies {
126129
def junit = "4.13.2"
127130
def mockito = "5.2.0"
128131
def mockitoKotlin = "2.2.0"
132+
def okhttp = "5.3.2"
133+
def retrofit = "2.11.0"
129134
configurations.configureEach { resolutionStrategy.force 'com.android.support:support-annotations:23.1.0' }
135+
// androidx.test pulls an older kotlin-stdlib; align for Snyk SNYK-JAVA-ORGJETBRAINSKOTLIN-2393744 (fixed in Kotlin 2.1.0+).
136+
configurations.configureEach {
137+
resolutionStrategy.eachDependency { details ->
138+
if (details.requested.group == 'org.jetbrains.kotlin' &&
139+
(details.requested.name == 'kotlin-stdlib' ||
140+
details.requested.name == 'kotlin-stdlib-common' ||
141+
details.requested.name == 'kotlin-stdlib-jdk7' ||
142+
details.requested.name == 'kotlin-stdlib-jdk8')) {
143+
details.useVersion '2.1.21'
144+
}
145+
}
146+
}
130147
implementation fileTree(include: ['*.jar'], dir: 'libs')
131148
implementation "com.android.volley:volley:$volley"
132-
implementation "junit:junit:$junit"
133149

134150
// For AGP 7.4+
135151
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
136152

137153
// Unit Testing Dependencies
138-
testImplementation 'junit:junit:4.13.2'
154+
testImplementation "junit:junit:$junit"
139155
testImplementation "org.mockito:mockito-core:$mockito"
140156
testImplementation "org.mockito:mockito-inline:$mockito"
141157
testImplementation 'org.mockito:mockito-android:5.2.0'
142158
testImplementation 'org.robolectric:robolectric:4.15' // Updated to fix security vulnerabilities
143-
testImplementation 'androidx.test:core:1.5.0'
144-
testImplementation 'androidx.test:runner:1.5.2'
145-
testImplementation 'androidx.test.ext:junit:1.1.5'
146-
testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0'
159+
testImplementation 'androidx.test:core:1.6.1'
160+
testImplementation 'androidx.test:runner:1.6.1'
161+
testImplementation 'androidx.test.ext:junit:1.2.1'
162+
testImplementation "com.squareup.okhttp3:mockwebserver:$okhttp"
147163
testImplementation 'org.json:json:20231013'
148164
// PowerMock for advanced mocking
149165
testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
150166
testImplementation 'org.powermock:powermock-api-mockito2:2.0.9'
151167
testImplementation 'org.powermock:powermock-core:2.0.9'
152168

153169
// Android Test Dependencies
154-
androidTestImplementation 'androidx.test:core:1.5.0'
155-
androidTestImplementation 'androidx.test:runner:1.5.2'
156-
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
157-
androidTestImplementation('androidx.test.espresso:espresso-core:3.5.1', {
170+
androidTestImplementation 'androidx.test:core:1.6.1'
171+
androidTestImplementation 'androidx.test:runner:1.6.1'
172+
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
173+
androidTestImplementation('androidx.test.espresso:espresso-core:3.6.1', {
158174
exclude group: 'com.android.support', module: 'support-annotations'
159175
})
160176

161177
// implementation 'com.squareup.okio:okio:3.9.0'
162178
implementation 'com.github.rjeschke:txtmark:0.12'
163-
// // Retrofit
164-
implementation("com.squareup.retrofit2:retrofit:2.9.0")
165-
implementation 'com.squareup.retrofit2:converter-gson'
166-
// // OkHttp
167-
implementation 'com.squareup.okhttp3:okhttp'
179+
implementation "com.squareup.retrofit2:retrofit:$retrofit"
180+
implementation "com.squareup.retrofit2:converter-gson:$retrofit"
181+
implementation "com.squareup.okhttp3:okhttp:$okhttp"
168182
// implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3'
169-
170-
constraints {
171-
implementation('com.squareup.retrofit2:converter-gson:2.9.0') {
172-
because 'gson 2.8.5 used by retrofit has a vulnerability'
173-
}
174-
implementation('com.google.code.gson:gson@2.8.9') {
175-
because 'gson 2.8.5 used by retrofit has a vulnerability'
176-
}
177-
implementation('com.squareup.okhttp3:okhttp:4.9.3') {
178-
because 'kotlin stdlib 1.4.10 used by okhttp has a vulnerability'
179-
}
180-
implementation('org.jetbrains.kotlin:kotlin-stdlib@1.6.0') {
181-
because 'kotlin stdlib 1.4.10 used by okhttp has a vulnerability'
182-
}
183-
}
184183
}
185184
tasks.register('clearJar', Delete) { delete 'build/libs/contentstack.jar' }
186185
tasks.register('unzip', Copy) {
@@ -397,7 +396,18 @@ tasks.register('jacocoTestCoverageVerification', JacocoCoverageVerification) {
397396
]))
398397
}
399398

400-
// Make check task depend on coverage verification
401-
tasks.named('check') {
402-
dependsOn('jacocoTestReport', 'jacocoTestCoverageVerification')
399+
// Do not run JVM unit tests during `build`, `check`, etc. Enable only when you ask for them explicitly.
400+
// Examples: ./gradlew :contentstack:testDebugUnitTest | :contentstack:test | :contentstack:jacocoTestReport
401+
def requestedTaskBasenames = gradle.startParameter.taskNames.collect { it.split(':').last().toLowerCase() }
402+
def runJvmUnitTests = requestedTaskBasenames.any { t ->
403+
t == 'test' ||
404+
t ==~ /test.*unittest/ ||
405+
t in ['jacocotestreport', 'jacocotestcoveragewerification', 'jacococombinedreport']
406+
}
407+
gradle.projectsEvaluated {
408+
if (!runJvmUnitTests) {
409+
tasks.matching { it.name.matches('test.*UnitTest') }.configureEach { ut ->
410+
ut.enabled = false
411+
}
412+
}
403413
}

0 commit comments

Comments
 (0)