Skip to content

feat: resolve map of primitives#3044

Merged
dd-mergequeue[bot] merged 8 commits into
feature/flags-ofeatfrom
typo/flags-resolveMap-api
Dec 12, 2025
Merged

feat: resolve map of primitives#3044
dd-mergequeue[bot] merged 8 commits into
feature/flags-ofeatfrom
typo/flags-resolveMap-api

Conversation

@typotter

@typotter typotter commented Dec 9, 2025

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds a Map<String, Any?> overload for resolveStructureValue(), providing a cleaner Kotlin collections
API for structured flags without JSONObject types.

Motivation

The existing resolveStructureValue() returns JSONObject, which:

  • Requires JSON type handling in integrations (e.g., OpenFeature provider)
  • Is less idiomatic for Kotlin code
  • Complicates recursive structure handling

This overload enables direct Map-based flag resolution with automatic recursive conversion of nested structures (maps and lists).

Additional Notes

API (backward compatible):

// Existing - unchanged
fun resolveStructureValue(flagKey: String, defaultValue: JSONObject): JSONObject

// New overload
fun resolveStructureValue(flagKey: String, defaultValue: Map<String, Any?>): Map<String, Any?>

Key implementation:

  • JsonExtensions.kt: Bidirectional recursive Map↔JSON conversion
  • String.toMap(): Direct JSON string parsing to Map
  • FlagValueConverter: Recognizes Map::class for OBJECT type
  • Returned Map contains only primitives, null, nested Maps, and Lists

Benefits:

  • Non-breaking change
  • Type determines format (JSONObject→JSONObject, Map→Map)
  • Cleaner API for Kotlin-first code
  • Enables OpenFeature integration

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)

@typotter
typotter force-pushed the typo/flags-resolveMap-api branch from 31a2370 to 15d7d6e Compare December 9, 2025 22:19
@typotter
typotter changed the base branch from develop to feature/flags-ofeat December 9, 2025 22:21
Adds Map<String, Any?> overload for resolveStructureValue(), enabling cleaner
API for structured flags using Kotlin collections instead of JSON types.

API (backward compatible):
- resolveStructureValue(flagKey, JSONObject): JSONObject (existing)
- resolveStructureValue(flagKey, Map): Map (new overload)

The Map overload returns only primitives, null, nested Maps, and Lists - no
JSONObject or JSONArray types.

Implementation:
- JsonExtensions.kt: Recursive bidirectional Map↔JSON conversion
- String.toMap(): Direct JSON string → Map parsing
- FlagValueConverter: Recognizes Map::class for OBJECT type
- DatadogFlagsClient: Delegates to resolveValue with conversion
- NoOpFlagsClient: Returns default map

Benefits:
- Backward compatible - existing JSONObject API unchanged
- Type determines format - input type matches output type
- Enables OpenFeature integration without JSON conversion issues
Adds JSONObject and JSONArray method calls used in JsonExtensions.kt:
- JSONObject.constructor(String): Parse JSON strings
- JSONObject.get/put: Get and set values
- JSONArray.get/put: Get and add elements

These methods are used for recursive Map↔JSON conversion and are safe
within the context of the conversion logic.
@typotter typotter changed the title resolve map method feat: resolve map of primitives Dec 10, 2025
@typotter
typotter force-pushed the typo/flags-resolveMap-api branch from 45fdc80 to 03f969e Compare December 10, 2025 16:24
@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Dec 10, 2025

Copy link
Copy Markdown

🎯 Code Coverage
Patch Coverage: 100.00%
Overall Coverage: 71.18% (-0.09%)

View detailed report

This comment will be updated automatically if new data arrives.
* Fix with Cursor requires Datadog plugin ≥v2.17.0
🔗 Commit SHA: d4de1af | Docs | Datadog PR Page | Was this helpful? Give us feedback!

@codecov-commenter

codecov-commenter commented Dec 10, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.22222% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.17%. Comparing base (d065edf) to head (d4de1af).

Files with missing lines Patch % Lines
...tadog/android/flags/internal/FlagValueConverter.kt 11.11% 2 Missing and 6 partials ⚠️
Additional details and impacted files
@@                   Coverage Diff                   @@
##           feature/flags-ofeat    #3044      +/-   ##
=======================================================
+ Coverage                71.13%   71.17%   +0.04%     
=======================================================
  Files                      862      863       +1     
  Lines                    31361    31400      +39     
  Branches                  5277     5289      +12     
=======================================================
+ Hits                     22306    22347      +41     
+ Misses                    7557     7552       -5     
- Partials                  1498     1501       +3     
Files with missing lines Coverage Δ
...in/kotlin/com/datadog/android/flags/FlagsClient.kt 33.63% <ø> (-1.96%) ⬇️
...tadog/android/flags/internal/DatadogFlagsClient.kt 91.43% <100.00%> (+0.85%) ⬆️
...m/datadog/android/flags/internal/JsonExtensions.kt 100.00% <100.00%> (ø)
.../datadog/android/flags/internal/NoOpFlagsClient.kt 93.55% <100.00%> (+0.44%) ⬆️
...tadog/android/flags/internal/FlagValueConverter.kt 64.86% <11.11%> (-1.80%) ⬇️

... and 39 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@typotter
typotter force-pushed the typo/flags-resolveMap-api branch from 03f969e to 70829d1 Compare December 10, 2025 18:40
@typotter
typotter marked this pull request as ready for review December 10, 2025 18:53
@typotter
typotter requested a review from a team as a code owner December 10, 2025 18:53
@typotter
typotter force-pushed the typo/flags-resolveMap-api branch from 70829d1 to d6c35fd Compare December 10, 2025 20:29
0xnm
0xnm previously approved these changes Dec 11, 2025
null -> JSONObject.NULL
is Map<*, *> -> {
// Convert keys to String (supports non-String keys via toString())
@Suppress("UNCHECKED_CAST")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any cast here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}
is List<*> -> {
@Suppress("UNCHECKED_CAST")
(value as List<Any?>).toJSONArray()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can change receiver type from List<Any?>.toJSONArray to List<*>.toJSONArray at the function declaration and then cast is not needed here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

Comment on lines +506 to +510
val fakeKey1Value = forge.anAlphabeticalString()
val fakeKey2Value = forge.anInt()
val fakeFlagValue = JSONObject().apply {
put("key1", forge.anAlphabeticalString())
put("key2", forge.anInt())
put("key1", fakeKey1Value)
put("key2", fakeKey2Value)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: looks like a redundant change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed

Comment on lines +49 to +52
assertThat(result[key1]).isEqualTo(value1)
assertThat(result["number"]).isEqualTo(value2)
assertThat(result["bool"]).isEqualTo(boolVal)
assertThat(result["double"]).isEqualTo(doubleVal)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you may want to use com.datadog.tools.unit.assertj.JsonObjectAssert for such assertions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

@typotter typotter left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, Nikita. ptal with the changes.

Comment on lines +506 to +510
val fakeKey1Value = forge.anAlphabeticalString()
val fakeKey2Value = forge.anInt()
val fakeFlagValue = JSONObject().apply {
put("key1", forge.anAlphabeticalString())
put("key2", forge.anInt())
put("key1", fakeKey1Value)
put("key2", fakeKey2Value)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed

Comment on lines +49 to +52
assertThat(result[key1]).isEqualTo(value1)
assertThat(result["number"]).isEqualTo(value2)
assertThat(result["bool"]).isEqualTo(boolVal)
assertThat(result["double"]).isEqualTo(doubleVal)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

0xnm
0xnm previously approved these changes Dec 12, 2025
@typotter

Copy link
Copy Markdown
Contributor Author

/merge

@dd-devflow-routing-codex

dd-devflow-routing-codex Bot commented Dec 12, 2025

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2025-12-12 12:54:17 UTC ℹ️ Start processing command /merge


2025-12-12 12:54:22 UTC ℹ️ MergeQueue: waiting for PR to be ready

This pull request is not mergeable according to GitHub. Common reasons include pending required checks, missing approvals, or merge conflicts — but it could also be blocked by other repository rules or settings.
It will be added to the queue as soon as checks pass and/or get approvals.
Note: if you pushed new commits since the last approval, you may need additional approval.
You can remove it from the waiting list with /remove command.


2025-12-12 14:09:22 UTC ℹ️ MergeQueue: merge request added to the queue

The expected merge time in feature/flags-ofeat is approximately 0s (p90).


2025-12-12 15:55:47 UTCMergeQueue: The checks failed on this merge request

Tests failed on this commit 5abf301:

What to do next?

  • Investigate the failures and when ready, re-add your pull request to the queue!
  • If your PR checks are green, try to rebase/merge. It might be because the CI run is a bit old.
  • Any question, go check the FAQ.

@typotter

Copy link
Copy Markdown
Contributor Author

/remove

@dd-devflow-routing-codex

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2025-12-12 15:55:49 UTC ℹ️ Start processing command /remove
If you need support, contact us on Slack #devflow!


2025-12-12 15:55:52 UTCDevflow: /remove

This merge request was already processed and can't be unqueued anymore.

To get help about command usage, write /remove --help

If you need support, contact us on Slack #devflow with those details!

@typotter

Copy link
Copy Markdown
Contributor Author

/merge

@dd-devflow-routing-codex

dd-devflow-routing-codex Bot commented Dec 12, 2025

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2025-12-12 16:10:51 UTC ℹ️ Start processing command /merge


2025-12-12 16:10:56 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in feature/flags-ofeat is approximately 0s (p90).


2025-12-12 17:07:51 UTC ℹ️ MergeQueue: This merge request was merged

@dd-mergequeue
dd-mergequeue Bot merged commit 8410eca into feature/flags-ofeat Dec 12, 2025
27 checks passed
@dd-mergequeue
dd-mergequeue Bot deleted the typo/flags-resolveMap-api branch December 12, 2025 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants