Releasing v4.11.0#107
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
7b1a491 to
0ef3c51
Compare
WalkthroughThe PR updates JSON serialization for Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/main/java/com/chargebee/v4/internal/JsonUtil.java`:
- Around line 40-41: The Timestamp serializer in JsonUtil is still emitting
epoch milliseconds via the registered TypeAdapter, but the contract now expects
Unix seconds. Update the Timestamp serialization used by the Gson builder in
JsonUtil so it converts the value from src.getTime() to seconds before creating
the JsonPrimitive, and make sure the behavior matches getTimestamp(...) and the
regression tests for expires_at.
- Around line 45-46: The enum JSON serializer in JsonUtil currently lowercases
names using the JVM default locale, which can change wire-format values on
locale-sensitive systems. Update the serializer registered in JsonUtil’s type
hierarchy adapter to use Locale.ROOT when calling toLowerCase so enum
serialization stays stable regardless of default locale.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3acd0fdb-16e5-4475-a148-af91e86b79a0
📒 Files selected for processing (6)
CHANGELOG.mdVERSIONbuild.gradle.ktssrc/main/java/com/chargebee/v4/internal/JsonUtil.javasrc/test/java/com/chargebee/v4/internal/JsonUtilTest.javasrc/test/java/com/chargebee/v4/models/promotionalGrant/params/PromotionalGrantsParamsJsonTest.java
| .registerTypeAdapter(Timestamp.class, | ||
| (JsonSerializer<Timestamp>) (src, t, c) -> new JsonPrimitive(src.getTime())) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Serialize Timestamp in Unix seconds, not milliseconds.
Line 41 currently writes src.getTime(), which is epoch milliseconds. The new contract, getTimestamp(...), and the added regression tests all expect epoch seconds, so JSON requests will still send expires_at 1000x too large and miss the release’s bug fix.
Proposed fix
.registerTypeAdapter(Timestamp.class,
- (JsonSerializer<Timestamp>) (src, t, c) -> new JsonPrimitive(src.getTime()))
+ (JsonSerializer<Timestamp>) (src, t, c) -> new JsonPrimitive(src.getTime() / 1000L))This contradicts the PR objective and the added regression assertions.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .registerTypeAdapter(Timestamp.class, | |
| (JsonSerializer<Timestamp>) (src, t, c) -> new JsonPrimitive(src.getTime())) | |
| .registerTypeAdapter(Timestamp.class, | |
| (JsonSerializer<Timestamp>) (src, t, c) -> new JsonPrimitive(src.getTime() / 1000L)) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/chargebee/v4/internal/JsonUtil.java` around lines 40 - 41,
The Timestamp serializer in JsonUtil is still emitting epoch milliseconds via
the registered TypeAdapter, but the contract now expects Unix seconds. Update
the Timestamp serialization used by the Gson builder in JsonUtil so it converts
the value from src.getTime() to seconds before creating the JsonPrimitive, and
make sure the behavior matches getTimestamp(...) and the regression tests for
expires_at.
| .registerTypeHierarchyAdapter(Enum.class, | ||
| (JsonSerializer<Enum<?>>) (src, t, c) -> new JsonPrimitive(src.name().toLowerCase())) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major
🧩 Analysis chain
🌐 Web query:
Does Java String.toLowerCase() use the default locale, and is Locale.ROOT recommended for protocol/API values?
💡 Result:
Yes, the parameterless String.toLowerCase method uses the default locale of the Java Virtual Machine (JVM), which is determined by Locale.getDefault [1][2][3]. Because the default locale depends on the environment where the code runs, using the parameterless toLowerCase or toUpperCase methods for strings that are intended to be interpreted in a locale-independent manner—such as protocol keys, programming language identifiers, filenames, or HTML tags—is strongly discouraged [2][3][4]. These methods can produce unexpected results depending on the host system's configuration. A classic example is the Turkish locale, where the character 'I' is converted to a lowercase dotless 'ı' instead of the standard 'i' [2][3][5][4]. For protocol/API values and other machine-facing identifiers, using Locale.ROOT is the recommended practice [2][3][6]. Locale.ROOT is the language- and country-neutral locale, ensuring that case conversion behaves deterministically regardless of the JVM's default locale settings [7][6][8][9]. Using Locale.ROOT (e.g., string.toLowerCase(Locale.ROOT)) provides a stable, predictable outcome suitable for canonicalization, storage, and comparison of technical identifiers [6][10][11].
Citations:
- 1: https://stackoverflow.com/questions/21385024/purpose-of-string-tolowercase-with-default-locale
- 2: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html
- 3: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html
- 4: https://learn.microsoft.com/en-us/dotnet/api/java.lang.string.tolowercase?view=net-android-35.0
- 5: https://stackoverflow.com/questions/10336730/which-locale-should-i-specify-when-i-call-stringtolowercase
- 6: https://thelinuxcode.com/java-string-tolowercase-method-locale-unicode-and-practical-examples-2026-guide/
- 7: https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/Locale.html
- 8: https://www.exchangetuts.com/java-15-stringformattedargs-safe-for-locale-aware-usage-1766929802777461
- 9: https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/Locale.html
- 10: Try to unify usage of Locale.ROOT and Locale.ENGLISH to just one apache/iceberg#3045
- 11: 7065228: To interpret case-insensitive string locale independently openjdk/jdk#14006
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- JsonUtil.java ---\n'
sed -n '1,140p' src/main/java/com/chargebee/v4/internal/JsonUtil.java | nl -ba
printf '\n--- Search toLowerCase/Locale in repo ---\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' 'toLowerCase\(|Locale\.ROOT|Locale\.getDefault|registerTypeHierarchyAdapter\(Enum\.class' src test .Repository: chargebee/chargebee-java
Length of output: 253
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- JsonUtil.java ---\n'
sed -n '1,140p' src/main/java/com/chargebee/v4/internal/JsonUtil.java | nl -ba
printf '\n--- Search toLowerCase/Locale in repo ---\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' 'toLowerCase\(|Locale\.ROOT|Locale\.getDefault|registerTypeHierarchyAdapter\(Enum\.class' src test .Repository: chargebee/chargebee-java
Length of output: 253
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- JsonUtil.java ---'
sed -n '1,140p' src/main/java/com/chargebee/v4/internal/JsonUtil.java | nl -ba
echo
echo '--- Search toLowerCase/Locale in repo ---'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' 'toLowerCase\(|Locale\.ROOT|Locale\.getDefault|registerTypeHierarchyAdapter\(Enum\.class' src test .Repository: chargebee/chargebee-java
Length of output: 224
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- JsonUtil.java ---'
sed -n '1,140p' src/main/java/com/chargebee/v4/internal/JsonUtil.java | nl -ba
echo
echo '--- Search toLowerCase/Locale in repo ---'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' 'toLowerCase\(|Locale\.ROOT|Locale\.getDefault|registerTypeHierarchyAdapter\(Enum\.class' src test .Repository: chargebee/chargebee-java
Length of output: 224
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- JsonUtil.java ---'
awk 'NR>=1 && NR<=140 { printf("%4d %s\n", NR, $0) }' src/main/java/com/chargebee/v4/internal/JsonUtil.java
echo
echo '--- Search toLowerCase/Locale in repo ---'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' 'toLowerCase\(|Locale\.ROOT|Locale\.getDefault|registerTypeHierarchyAdapter\(Enum\.class' src test .Repository: chargebee/chargebee-java
Length of output: 8062
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- JsonUtil.java ---'
awk 'NR>=1 && NR<=140 { printf("%4d %s\n", NR, $0) }' src/main/java/com/chargebee/v4/internal/JsonUtil.java
echo
echo '--- Search toLowerCase/Locale in repo ---'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' 'toLowerCase\(|Locale\.ROOT|Locale\.getDefault|registerTypeHierarchyAdapter\(Enum\.class' src test .Repository: chargebee/chargebee-java
Length of output: 8062
Use Locale.ROOT for enum JSON casing. src.name().toLowerCase() here follows the JVM default locale, so wire-format values can change on Turkish/default-locale systems (IN_TRIAL → ın_trial). Use src.name().toLowerCase(Locale.ROOT) instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/chargebee/v4/internal/JsonUtil.java` around lines 45 - 46,
The enum JSON serializer in JsonUtil currently lowercases names using the JVM
default locale, which can change wire-format values on locale-sensitive systems.
Update the serializer registered in JsonUtil’s type hierarchy adapter to use
Locale.ROOT when calling toLowerCase so enum serialization stays stable
regardless of default locale.
Replace the hand-rolled `instanceof`-chain inside `toJsonElement(...)` with a single static `Gson` instance configured via `GsonBuilder`: - `Timestamp` → Unix-seconds JSON number (`JsonSerializer<Timestamp>`) - `Date` → `yyyy-MM-dd` JSON string (`JsonSerializer<Date>`) - `Enum` → lowercase `name()` string (`registerTypeHierarchyAdapter`) `toJson(Map)` and `toJson(List)` now delegate to `GSON.toJson(...)`; Gson natively walks nested maps, lists, and `Object[]` and dispatches each value through the registered adapters via its `ObjectTypeAdapter`. The builder also calls `disableHtmlEscaping()` and `serializeNulls()` to preserve the prior behavior (`JsonElement.toString()` semantics and the explicit `"key":null` shape). No behavior change for the Chargebee API contract; the full JsonUtil and PromotionalGrantsParams regression suites pass. Co-authored-by: Cursor <cursoragent@cursor.com>
0ef3c51 to
e8fe429
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (2)
src/main/java/com/chargebee/v4/internal/JsonUtil.java (2)
40-41: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winExisting blocker still applies: serialize
Timestampas Unix seconds.Line 41 emits epoch milliseconds, while the PR objective and the local comment require Unix seconds. This keeps JSON
expires_atvalues 1000x too large; update this serializer and the new millisecond-based test expectations.Proposed fix
.registerTypeAdapter(Timestamp.class, - (JsonSerializer<Timestamp>) (src, t, c) -> new JsonPrimitive(src.getTime())) + (JsonSerializer<Timestamp>) (src, t, c) -> new JsonPrimitive(src.getTime() / 1000L))As per path instructions, this is limited to a merge-blocking correctness issue.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/chargebee/v4/internal/JsonUtil.java` around lines 40 - 41, The Timestamp serializer in JsonUtil currently emits epoch milliseconds via the JsonSerializer registered in JsonUtil, which breaks the expected Unix-seconds format. Update the Timestamp type adapter to divide the timestamp value by 1000 before serializing, and adjust the related tests that currently assert millisecond-based expires_at values so they expect Unix seconds instead.Source: Path instructions
45-46: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winExisting blocker still applies: make enum casing locale-independent.
src.name().toLowerCase()uses the JVM default locale, so API enum values can change on Turkish/default-locale systems. UseLocale.ROOT.Proposed fix
.registerTypeHierarchyAdapter(Enum.class, - (JsonSerializer<Enum<?>>) (src, t, c) -> new JsonPrimitive(src.name().toLowerCase())) + (JsonSerializer<Enum<?>>) (src, t, c) -> new JsonPrimitive(src.name().toLowerCase(java.util.Locale.ROOT)))As per path instructions, this is limited to a merge-blocking correctness issue.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/chargebee/v4/internal/JsonUtil.java` around lines 45 - 46, The enum serialization in JsonUtil’s type hierarchy adapter still depends on the JVM default locale, so update the Enum.class serializer to convert names to lowercase using Locale.ROOT instead of the locale-sensitive default. Keep the change local to the existing JsonUtil registration logic so the JSON output from enum values remains stable across all locales.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@src/main/java/com/chargebee/v4/internal/JsonUtil.java`:
- Around line 40-41: The Timestamp serializer in JsonUtil currently emits epoch
milliseconds via the JsonSerializer registered in JsonUtil, which breaks the
expected Unix-seconds format. Update the Timestamp type adapter to divide the
timestamp value by 1000 before serializing, and adjust the related tests that
currently assert millisecond-based expires_at values so they expect Unix seconds
instead.
- Around line 45-46: The enum serialization in JsonUtil’s type hierarchy adapter
still depends on the JVM default locale, so update the Enum.class serializer to
convert names to lowercase using Locale.ROOT instead of the locale-sensitive
default. Keep the change local to the existing JsonUtil registration logic so
the JSON output from enum values remains stable across all locales.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5b23ee97-b5db-45de-9ac1-13a735e52354
📒 Files selected for processing (3)
src/main/java/com/chargebee/v4/internal/JsonUtil.javasrc/test/java/com/chargebee/v4/internal/JsonUtilTest.javasrc/test/java/com/chargebee/v4/models/promotionalGrant/params/PromotionalGrantsParamsJsonTest.java
v4.11.0 (2026-06-23)
Bug Fixes:
Timestampparameters were sent in a human-readable format (e.g."expires_at":"2026-06-23 09:54:44.513") instead of Unix seconds. They are now serialized as numeric Unix seconds (e.g."expires_at":1782189229), matching the form-url-encoded path and the format expected by the API. This affects all JSON content-type endpoints, such ascreate_promotional_grantinPromotionalGrant.Released v4.11.0 with a JSON serialization bug fix:
Timestampfields inapplication/jsonrequest bodies now serialize as numeric Unix-second values (not human-readable strings), aligning with form-url-encoded behavior. Updated changelog/version and added regression tests covering JSON and promotional grant payloads (including nested structures).