Skip to content

feat(MSDK-3261): add storage info and sdks field to ConsentDisclosure…#182

Merged
uc-brunosilva merged 2 commits intomasterfrom
feature/MSDK-3261
Feb 23, 2026
Merged

feat(MSDK-3261): add storage info and sdks field to ConsentDisclosure…#182
uc-brunosilva merged 2 commits intomasterfrom
feature/MSDK-3261

Conversation

@uc-brunosilva
Copy link
Collaborator

@uc-brunosilva uc-brunosilva commented Feb 19, 2026

CodeAnt-AI Description

Add SDK entries to device storage disclosures and make deviceStorage an object with disclosures and sdks

What Changed

  • deviceStorage is now an object with keys "disclosures" (list) and "sdks" (list) instead of a plain list of disclosures
  • Each SDK in "sdks" is exposed to consumers with "name" and "use" fields
  • Android, iOS and TypeScript models and test mocks updated so CMP data, serializers, and mocks include the new sdks field

Impact

✅ Clearer SDK disclosures in CMP data
✅ Consistent deviceStorage shape across Android, iOS and web
✅ Fewer missing fields in CMP mocks and tests

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • New Features

    • Consent disclosure data now includes SDK details alongside disclosures; serialization and data conversions updated across platforms.
  • Tests

    • Test mocks and fixtures updated to include the new SDK field in consent disclosure objects.

…Object

- Add storageInfo and sdks fields to ConsentDisclosureObject model
- Update ConsentDisclosureSerializer (Android) to map new fields
- Update UsercentricsCMPData+Dict (iOS) to include new fields
- Update mocks (Android/iOS) and TS test mocks to reflect new fields

Co-authored-by: Cursor <cursoragent@cursor.com>
@codeant-ai
Copy link

codeant-ai bot commented Feb 19, 2026

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@qodo-code-review
Copy link

Review Summary by Qodo

Add storage info and SDKs field to ConsentDisclosureObject

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add storageInfo and sdks fields to ConsentDisclosureObject model
• Restructure deviceStorage from flat list to nested object with disclosures and sdks
• Update serialization logic across Android, iOS, and TypeScript implementations
• Update all mock data to reflect new model structure

Grey Divider

File Changes

1. android/src/androidTest/java/com/usercentrics/reactnative/mock/GetCMPDataMock.kt 🧪 Tests +16/-12

Update Android mock data structure

• Add sdks field to ConsentDisclosureObject mock initialization
• Restructure deviceStorage mock from flat list to nested map with disclosures and sdks keys

android/src/androidTest/java/com/usercentrics/reactnative/mock/GetCMPDataMock.kt


2. android/src/main/java/com/usercentrics/reactnative/extensions/ConsentDisclosureSerializer.kt ✨ Enhancement +12/-1

Update serialization for new SDK structure

• Change serialize() return type from list to map with disclosures and sdks keys
• Add new ConsentDisclosureSDK.serialize() method to serialize SDK objects with name and use
 fields
• Import ConsentDisclosureSDK class

android/src/main/java/com/usercentrics/reactnative/extensions/ConsentDisclosureSerializer.kt


3. example/ios/exampleTests/Mock/CMPData+Mock.swift 🧪 Tests +1/-1

Update iOS example mock data

• Add sdks: [] parameter to ConsentDisclosureObject mock initialization

example/ios/exampleTests/Mock/CMPData+Mock.swift


View more (4)
4. ios/Extensions/UsercentricsCMPData+Dict.swift ✨ Enhancement +4/-1

Update iOS dictionary conversion logic

• Restructure toDictionary() return from flat list to map with disclosures and sdks keys
• Add mapping for sdks array with name and use fields

ios/Extensions/UsercentricsCMPData+Dict.swift


5. sample/ios/sampleTests/Mock/CMPData+Mock.swift 🧪 Tests +1/-1

Update iOS sample mock data

• Add sdks: [] parameter to ConsentDisclosureObject mock initialization

sample/ios/sampleTests/Mock/CMPData+Mock.swift


6. src/__tests__/mocks.ts 🧪 Tests +2/-1

Update TypeScript test mock data

• Add sdks: [] field to consentDisclosureObject mock

src/tests/mocks.ts


7. src/models/ConsentDisclosureObject.tsx ✨ Enhancement +13/-1

Add SDK model and update TypeScript types

• Add new ConsentDisclosureSDK class with name and use properties
• Add sdks field to ConsentDisclosureObject class
• Update constructor to accept optional sdks parameter with default empty array

src/models/ConsentDisclosureObject.tsx


Grey Divider

Qodo Logo

@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

ConsentDisclosureObject gains a new sdks field across Android, iOS, and web. Serialization and mock data are updated to include both "disclosures" and "sdks" (empty lists where applicable), and a ConsentDisclosureSDK type/class and serializer were added.

Changes

Cohort / File(s) Summary
Android Data Mock
android/src/androidTest/java/com/usercentrics/reactnative/mock/GetCMPDataMock.kt
Added sdks field to ConsentDisclosureObject; changed deviceStorage from a list to a map with disclosures and sdks keys.
Android Serialization
android/src/main/java/com/usercentrics/reactnative/extensions/ConsentDisclosureSerializer.kt
Serialize now returns both "disclosures" and "sdks"; added private ConsentDisclosureSDK.serialize() extension producing "name" and "use".
iOS Test Mocks
example/ios/exampleTests/Mock/CMPData+Mock.swift, sample/ios/sampleTests/Mock/CMPData+Mock.swift
Mock initializers updated to call ConsentDisclosureObject with sdks: [].
iOS Serialization
ios/Extensions/UsercentricsCMPData+Dict.swift
toDictionary() now returns a dictionary with "disclosures" and "sdks" (SDKs mapped to name/use).
Web Models
src/models/ConsentDisclosureObject.tsx
Added exported ConsentDisclosureSDK class (name, use); ConsentDisclosureObject extended with sdks: ConsentDisclosureSDK[] and constructor updated to accept optional sdks.
Web Test Mocks
src/__tests__/mocks.ts
Added sdks: [] to the ConsentDisclosureObject mock instances.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through code, both far and near,

Added sdks so all is clear,
Disclosures kept, new names in view,
Serializers tidy, tests say "woo!"
A tiny hop for consistent cheer 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding an sdks field to ConsentDisclosure and updating the deviceStorage structure. It is specific, concise, and directly reflects the primary objectives of the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/MSDK-3261

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: test-rn

Failed stage: Install Dependencies [❌]

Failed test name: ""

Failure summary:

The action failed during the TypeScript compile step (yarn compile) because tsc reported a type
error in src/models/ConsentDisclosureObject.tsx at line 18, column 9:
- TS2322: A value typed as
[ConsentDisclosureSDK] | [] (a union that can be an empty array) was assigned to a tuple type
[ConsentDisclosureSDK] (which requires exactly 1 element).
- This caused tsc -p . to fail, and yarn
compile exited with code 1, which failed the GitHub Action.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

655:  warning react-native-codegen > jscodeshift > temp > rimraf@2.6.3: Rimraf versions prior to v4 are no longer supported
656:  warning react-native-codegen > jscodeshift > temp > rimraf > glob@7.2.3: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
657:  warning react-native-codegen > jscodeshift > micromatch > snapdragon > source-map-resolve@0.5.3: See https://github.com/lydell/source-map-resolve#deprecated
658:  warning react-native-codegen > jscodeshift > micromatch > snapdragon > source-map-resolve > resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
659:  warning react-native-codegen > jscodeshift > micromatch > snapdragon > source-map-resolve > urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
660:  warning react-native-codegen > jscodeshift > micromatch > snapdragon > source-map-resolve > source-map-url@0.4.1: See https://github.com/lydell/source-map-url#deprecated
661:  [2/4] Fetching packages...
662:  [3/4] Linking dependencies...
663:  warning " > @react-native-community/eslint-config@3.2.0" has unmet peer dependency "prettier@>=2".
664:  warning "@react-native-community/eslint-config > eslint-plugin-prettier@4.2.5" has unmet peer dependency "prettier@>=2.0.0".
665:  warning "react-native-codegen > jscodeshift@0.11.0" has unmet peer dependency "@babel/preset-env@^7.1.6".
666:  [4/4] Building fresh packages...
667:  $ yarn compile
668:  yarn run v1.22.22
669:  $ rm -rf lib && tsc -p .
670:  ##[error]src/models/ConsentDisclosureObject.tsx(18,9): error TS2322: Type '[ConsentDisclosureSDK] | []' is not assignable to type '[ConsentDisclosureSDK]'.
671:  Type '[]' is not assignable to type '[ConsentDisclosureSDK]'.
672:  Source has 0 element(s) but target requires 1.
673:  error Command failed with exit code 1.
674:  info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
675:  error Command failed with exit code 1.
676:  info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
677:  ##[error]Process completed with exit code 1.
678:  ##[group]Run actions/upload-artifact@v4

@codeant-ai codeant-ai bot added the size:M This PR changes 30-99 lines, ignoring generated files label Feb 19, 2026
@pantoaibot
Copy link

pantoaibot bot commented Feb 19, 2026

PR Summary:

Add sdks field to ConsentDisclosureObject and turn deviceStorage from a list into an object {disclosures, sdks} across Android, iOS, and JS/TS layers.

  • Overview: Introduces ConsentDisclosureSDK and adds sdks: [] to ConsentDisclosureObject. deviceStorage (aka ConsentDisclosureObject) representation changed from an array of disclosures to an object containing "disclosures" and "sdks".
  • Android:
    • ConsentDisclosureSerializer now serializes ConsentDisclosureObject as { "disclosures": [...], "sdks": [...] }.
    • Added serialization for ConsentDisclosureSDK.
    • Updated android test mock GetCMPDataMock to use the new deviceStorage object shape and include sdks: [].
  • iOS:
    • UsercentricsCMPData+Dict mapping updated: ConsentDisclosureObject.toDictionary() now returns { "disclosures": [...], "sdks": [...] } and sdks serialized as { "name": , "use": }.
    • Example and sample test mocks updated to pass sdks: [] when creating ConsentDisclosureObject.
  • JS/TS:
    • Added ConsentDisclosureSDK class and sdks property to ConsentDisclosureObject model; constructors updated to accept (and default) sdks.
    • Updated test mocks (src/tests/mocks.ts) and consentDisclosureObject to include sdks: [].
  • Tests/mocks: Multiple platform mocks updated to account for the new sdks field and new deviceStorage structure.
  • Breaking change: deviceStorage JSON shape changed from a list of disclosure objects to an object with keys "disclosures" (list) and "sdks" (list). Consumers/parsers expecting the old array shape must be adjusted.
  • No dependency or performance changes.

Reviewed by Panto AI

@codeant-ai
Copy link

codeant-ai bot commented Feb 19, 2026

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Null-safety
    The serializer calls disclosures.map { ... } and sdks.map { ... } without guarding the lists. If disclosures or sdks can be null in the SDK model, these calls will throw a NullPointerException at runtime. Verify the nullability of these properties and make the mapping resilient to null values.

  • Optional handling
    ConsentDisclosureObject.toDictionary() maps disclosures and sdks directly. If either property is optional (nil) this will crash or produce unexpected output. Ensure you use safe optional chaining or default empty containers to avoid runtime crashes and to keep consistent shapes in the produced dictionaries.

  • Class vs plain object usage
    The code defines classes with constructors that require some fields (e.g. cookieRefresh, purposes) but test code and other parts create plain object literals typed as ConsentDisclosure. This can lead to mismatches between class instances and plain objects (missing prototype methods, different initialization). Decide if the model should be an interface/type (for plain objects) or keep classes and instantiate them everywhere.

  • Type annotation bug
    The new type annotations use tuple types (e.g. [ConsentDisclosure], [ConsentDisclosureSDK], [number]) which declare fixed-length tuples, not variable-length arrays. This will cause incorrect typing and runtime assumptions when multiple items are expected. Replace with proper array types (e.g. ConsentDisclosure[], ConsentDisclosureSDK[], number[]) and update constructor signatures accordingly.

  • Mock / Expected shape mismatch
    The Android test/mock data changes deviceStorage from an array to a map with keys disclosures and sdks. Ensure all places that consume the mock (tests, expected maps) were updated to the new structure and that the empty SDK list shape matches the serializer's output (list of maps). Validate tests assert the new nested structure consistently.

Comment on lines 14 to 15
sdks: [ConsentDisclosureSDK]

Copy link

Choose a reason for hiding this comment

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

Suggestion: The sdks property and constructor parameter are typed as a single-element tuple ([ConsentDisclosureSDK]), but both the constructor default (sdks ?? []) and the test mocks assign an empty array, which is not compatible with a non-empty tuple type and also misrepresents the intended "zero or more SDKs" contract; this can lead to type errors and incorrect modeling of the data. [type error]

Severity Level: Critical 🚨
- ❌ TypeScript compilation fails at src/__tests__/mocks.ts:270.
- ⚠️ Cannot represent ConsentDisclosureObject with zero SDKs.
- ⚠️ TS model diverges from native list-based sdks field.
Suggested change
sdks: [ConsentDisclosureSDK]
sdks: ConsentDisclosureSDK[]
constructor(disclosures: [ConsentDisclosure], sdks?: ConsentDisclosureSDK[]) {
Steps of Reproduction ✅
1. Run the TypeScript type checker or test suite for this repo (e.g., `tsc` or `yarn
test`) so that all files under `src/` are type-checked.

2. During type-checking, `src/models/ConsentDisclosureObject.tsx:11-20` is loaded, where
`ConsentDisclosureObject` defines `sdks: [ConsentDisclosureSDK]` and its constructor
parameter as `sdks?: [ConsentDisclosureSDK]`, and assigns `this.sdks = sdks ?? []`.

3. The compiler then loads `src/__tests__/mocks.ts:259-273`, where `const
consentDisclosureObject: ConsentDisclosureObject = { disclosures: [disclosure], sdks: [],
}` attempts to assign an empty array `[]` to the `sdks` property.

4. TypeScript reports a type error because `sdks` is declared as a single-element tuple
`[ConsentDisclosureSDK]` (non-empty) while both `this.sdks = sdks ?? []` in
`ConsentDisclosureObject` and `sdks: []` in `mocks.ts` provide `[]`, which is not
assignable to `[ConsentDisclosureSDK]`, causing the build/type-check to fail.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/models/ConsentDisclosureObject.tsx
**Line:** 14:15
**Comment:**
	*Type Error: The `sdks` property and constructor parameter are typed as a single-element tuple (`[ConsentDisclosureSDK]`), but both the constructor default (`sdks ?? []`) and the test mocks assign an empty array, which is not compatible with a non-empty tuple type and also misrepresents the intended "zero or more SDKs" contract; this can lead to type errors and incorrect modeling of the data.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

@codeant-ai
Copy link

codeant-ai bot commented Feb 19, 2026

CodeAnt AI finished reviewing your PR.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
example/ios/exampleTests/Mock/CMPData+Mock.swift (1)

234-238: Consider adding a ConsentDisclosureSDK.mock() helper to mirror the existing ConsentDisclosure.mock().

The sdks: [] initialisation is correct and consistent with the sample/ counterpart. However, the file already provides a fully-populated ConsentDisclosure.mock() (lines 241–253) to enable tests to work with real-looking disclosure data. There is no equivalent ConsentDisclosureSDK.mock() extension here, which means any future test that needs to assert on SDK-level fields will have to construct instances inline or silently run against an empty list.

✨ Suggested addition (optional)
extension ConsentDisclosureSDK {

  static func mock() -> ConsentDisclosureSDK {
    return .init(/* populate with representative values once the type's initializer is known */)
  }
}

And update the ConsentDisclosureObject mock to use it:

-    return .init(disclosures: [.mock()], sdks: [])
+    return .init(disclosures: [.mock()], sdks: [.mock()])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/ios/exampleTests/Mock/CMPData`+Mock.swift around lines 234 - 238, Add
a ConsentDisclosureSDK.mock() helper and use it from
ConsentDisclosureObject.mock(): implement an extension on ConsentDisclosureSDK
with a static func mock() -> ConsentDisclosureSDK that returns a representative,
fully-populated instance (fill fields with realistic test values), then change
ConsentDisclosureObject.mock() to initialize sdks using [.mock()] (or an
appropriate array of mocks) instead of the empty array so tests can assert
SDK-level fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/models/ConsentDisclosureObject.tsx`:
- Around line 14-18: The tuple types like sdks: [ConsentDisclosureSDK],
disclosures: [ConsentDisclosure], and purposes: [number] are incorrect — change
them to array types ConsentDisclosureSDK[], ConsentDisclosure[], and number[]
respectively; update the ConsentDisclosureObject constructor
(constructor(disclosures: [ConsentDisclosure], sdks?: [ConsentDisclosureSDK]))
to accept disclosures: ConsentDisclosure[] and sdks?: ConsentDisclosureSDK[] and
set this.sdks = sdks ?? [] (and similarly ensure this.disclosures and any
purposes defaults use [] where appropriate) so empty arrays are allowed and the
types match runtime usage.

---

Nitpick comments:
In `@example/ios/exampleTests/Mock/CMPData`+Mock.swift:
- Around line 234-238: Add a ConsentDisclosureSDK.mock() helper and use it from
ConsentDisclosureObject.mock(): implement an extension on ConsentDisclosureSDK
with a static func mock() -> ConsentDisclosureSDK that returns a representative,
fully-populated instance (fill fields with realistic test values), then change
ConsentDisclosureObject.mock() to initialize sdks using [.mock()] (or an
appropriate array of mocks) instead of the empty array so tests can assert
SDK-level fields.

Comment on lines 14 to 18
sdks: [ConsentDisclosureSDK]

constructor(disclosures: [ConsentDisclosure]) {
constructor(disclosures: [ConsentDisclosure], sdks?: [ConsentDisclosureSDK]) {
this.disclosures = disclosures
this.sdks = sdks ?? []
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

[ConsentDisclosureSDK] is a tuple (exactly 1 element), not an array — this is the root cause of the pipeline failure.

In TypeScript, [T] defines a tuple with exactly one element of type T. Assigning [] to it fails because an empty array doesn't satisfy the single-element requirement. The field type should be ConsentDisclosureSDK[] (a variable-length array).

Note: the same tuple-vs-array issue exists for pre-existing fields (disclosures: [ConsentDisclosure], purposes: [number]), which happen not to break only because they're never assigned an empty array. Consider fixing those as well.

🐛 Proposed fix for the type and constructor
-    sdks: [ConsentDisclosureSDK]
+    sdks: ConsentDisclosureSDK[]

-    constructor(disclosures: [ConsentDisclosure], sdks?: [ConsentDisclosureSDK]) {
+    constructor(disclosures: [ConsentDisclosure], sdks?: ConsentDisclosureSDK[]) {

Ideally also fix the pre-existing tuple types:

-    disclosures: [ConsentDisclosure]
+    disclosures: ConsentDisclosure[]

-    purposes: [number]
+    purposes: number[]
📝 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.

Suggested change
sdks: [ConsentDisclosureSDK]
constructor(disclosures: [ConsentDisclosure]) {
constructor(disclosures: [ConsentDisclosure], sdks?: [ConsentDisclosureSDK]) {
this.disclosures = disclosures
this.sdks = sdks ?? []
sdks: ConsentDisclosureSDK[]
constructor(disclosures: [ConsentDisclosure], sdks?: ConsentDisclosureSDK[]) {
this.disclosures = disclosures
this.sdks = sdks ?? []
🧰 Tools
🪛 GitHub Actions: CI/CD

[error] 18-18: TS2322: Type '[ConsentDisclosureSDK] | []' is not assignable to type '[ConsentDisclosureSDK]'.

🪛 GitHub Check: test-rn

[failure] 18-18:
Type '[ConsentDisclosureSDK] | []' is not assignable to type '[ConsentDisclosureSDK]'.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/models/ConsentDisclosureObject.tsx` around lines 14 - 18, The tuple types
like sdks: [ConsentDisclosureSDK], disclosures: [ConsentDisclosure], and
purposes: [number] are incorrect — change them to array types
ConsentDisclosureSDK[], ConsentDisclosure[], and number[] respectively; update
the ConsentDisclosureObject constructor (constructor(disclosures:
[ConsentDisclosure], sdks?: [ConsentDisclosureSDK])) to accept disclosures:
ConsentDisclosure[] and sdks?: ConsentDisclosureSDK[] and set this.sdks = sdks
?? [] (and similarly ensure this.disclosures and any purposes defaults use []
where appropriate) so empty arrays are allowed and the types match runtime
usage.

Comment on lines 1 to 22
export class ConsentDisclosureSDK {
name: string
use: string

constructor(name: string, use: string) {
this.name = name
this.use = use
}
}

export class ConsentDisclosureObject {

disclosures: [ConsentDisclosure]
sdks: [ConsentDisclosureSDK]

constructor(disclosures: [ConsentDisclosure]) {
constructor(disclosures: [ConsentDisclosure], sdks?: [ConsentDisclosureSDK]) {
this.disclosures = disclosures
this.sdks = sdks ?? []
}
}

export class ConsentDisclosure {
Copy link

Choose a reason for hiding this comment

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

[CRITICAL_BUG] The new TypeScript types use tuple syntax ([T]) instead of array syntax (T[]). This will cause incorrect typing/compile errors and unexpected runtime shapes. Change: - disclosures: [ConsentDisclosure] -> disclosures: ConsentDisclosure[] - sdks: [ConsentDisclosureSDK] -> sdks: ConsentDisclosureSDK[] - purposes: [number] -> purposes: number[] Also update constructor signature to accept sdks?: ConsentDisclosureSDK[] and purposes: number[]. Example corrections: constructor(disclosures: ConsentDisclosure[], sdks?: ConsentDisclosureSDK[]) { ... } and class fields disclosures: ConsentDisclosure[]; sdks: ConsentDisclosureSDK[]; purposes: number[].

export class ConsentDisclosureSDK {
    name: string
    use: string

    constructor(name: string, use: string) {
        this.name = name
        this.use = use
    }
}

export class ConsentDisclosureObject {

    disclosures: ConsentDisclosure[]
    sdks: ConsentDisclosureSDK[]

    constructor(disclosures: ConsentDisclosure[], sdks?: ConsentDisclosureSDK[]) {
        this.disclosures = disclosures
        this.sdks = sdks ?? []
    }
}

export class ConsentDisclosure {

    identifier?: string
    type?: ConsentDisclosureType
    name?: string
    maxAgeSeconds?: number
    cookieRefresh: boolean
    purposes: number[]
    domain?: string
    description?: string

    constructor(
        cookieRefresh: boolean,
        purposes: number[],
        identifier?: string,
        type?: ConsentDisclosureType,
        name?: string,
        maxAgeSeconds?: number,
        domain?: string,
        description?: string,
    ) {
        this.identifier = identifier
        this.type = type
        this.name = name
        this.maxAgeSeconds = maxAgeSeconds
        this.cookieRefresh = cookieRefresh
        this.purposes = purposes
    }
}

Comment on lines +11 to +21
return mapOf(
"disclosures" to disclosures.map { it.serialize() },
"sdks" to sdks.map { it.serialize() }
)
}

private fun ConsentDisclosureSDK.serialize(): Map<String, Any> {
return mapOf(
"name" to name,
"use" to use
)
Copy link

Choose a reason for hiding this comment

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

[VALIDATION] serialize() now serializes sdks unguarded: sdks.map { it.serialize() }. If ConsentDisclosureObject.sdks can be null, this will throw an NPE. Make this null-safe and preserve an empty list if absent: "sdks" to (sdks?.map { it.serialize() } ?: emptyList()). Also consider whether the nested structures need conversion to WritableMap at this level or are handled by the caller.

import com.usercentrics.sdk.v2.settings.data.ConsentDisclosure
import com.usercentrics.sdk.v2.settings.data.ConsentDisclosureObject
import com.usercentrics.sdk.v2.settings.data.ConsentDisclosureSDK

internal fun ConsentDisclosureObject?.serialize(): Any? {
    if (this == null) return null

    return mapOf(
        "disclosures" to disclosures.map { it.serialize() },
        "sdks" to (sdks?.map { it.serialize() } ?: emptyList())
    )
}

private fun ConsentDisclosureSDK.serialize(): Map<String, Any> {
    return mapOf(
        "name" to name,
        "use" to use
    )
}

Comment on lines +367 to +370
return [
"disclosures": self.disclosures.map { $0.toDictionary() },
"sdks": self.sdks.map { ["name": $0.name, "use": $0.use] }
]
Copy link

Choose a reason for hiding this comment

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

[CRITICAL_BUG] You changed ConsentDisclosureObject.toDictionary() from returning an array of disclosures to returning a Dictionary with keys "disclosures" and "sdks". This is a breaking shape change for any consumers that previously expected an array. Either: 1) Ensure all call sites / JS/native bridge logic were updated to expect an object with keys {disclosures, sdks}; or 2) keep backward compatible shape (e.g. return the array for old callers and add a separate field or version). Additionally guard sdks for nil to avoid crashes: "sdks": self.sdks?.map { ["name": $0.name, "use": $0.use] } ?? []

extension ConsentDisclosureObject {
    func toDictionary() -> Any {
        return [
            "disclosures": self.disclosures.map { $0.toDictionary() },
            "sdks": (self.sdks ?? []).map { [
                "name": $0.name as Any,
                "use": $0.use as Any
            ]]
        ]
    }
}

Comment on lines +91 to +92
),
sdks = listOf()
Copy link

Choose a reason for hiding this comment

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

[VALIDATION] Mocks were updated to add sdks = listOf() inside ConsentDisclosureObject and the expected map for "deviceStorage" changed from a list to a map with keys disclosures/sdks. Ensure all test assertions and any other mock consumers are updated to the new structure. Also verify the real SDK's ConsentDisclosureObject constructor supports the new sdks parameter; otherwise tests will not compile.

Comment on lines +632 to 646
"deviceStorage" to mapOf(
"disclosures" to listOf(
mapOf(
"identifier" to "identifier",
"type" to 2,
"name" to "name",
"maxAgeSeconds" to 123123L,
"cookieRefresh" to true,
"purposes" to listOf(1, 2, 3),
"domain" to "domain",
"description" to "description",
)
),
"sdks" to emptyList<Map<String, Any>>()
),
Copy link

Choose a reason for hiding this comment

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

[REFACTORING] In the expected map you changed "deviceStorage" from a list of maps to a single map with keys "disclosures" and "sdks". Confirm the code that consumes this expected map (test verification code) is updated accordingly. If many tests expect the old list shape, consider adding a small migration helper in tests to transform the new object into the older shape for backward-compatible assertions while the rest of the codebase is updated.

Comment on lines +271 to 273
disclosures: [disclosure],
sdks: [],
}
Copy link

Choose a reason for hiding this comment

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

[REFACTORING] You added sdks: [] to the test ConsentDisclosureObject fixture. After fixing the model types (see src/models/ConsentDisclosureObject.tsx), update this fixture's typing (if typings are enforced) to match ConsentDisclosureSDK[] and, if useful, add a minimal SDK entry to exercise serialization paths in tests (e.g. { name: 'sdkName', use: 'purpose' }).

const consentDisclosureObject: ConsentDisclosureObject = {
    disclosures: [disclosure],
    sdks: [
        {
            name: "sdkName",
            use: "purpose",
        },
    ],
}

@pantoaibot
Copy link

pantoaibot bot commented Feb 19, 2026

Reviewed up to commit:516fa6fe2f954d9f4d18777b5f7b43f46ef1d64b

Reviewed by Panto AI

@qodo-code-review
Copy link

qodo-code-review bot commented Feb 19, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

✅ 1. TS sdks typed as tuple 🐞 Bug ✓ Correctness
Description
ConsentDisclosureObject.sdks is typed as a 1-element tuple ([ConsentDisclosureSDK]) even though the
native payload is a variable-length list (including empty). This makes the published TypeScript API
incorrect and can break consumers that have 0 or multiple SDK entries.
Code

src/models/ConsentDisclosureObject.tsx[R11-19]

export class ConsentDisclosureObject {

   disclosures: [ConsentDisclosure]
+    sdks: [ConsentDisclosureSDK]

-    constructor(disclosures: [ConsentDisclosure]) {
+    constructor(disclosures: [ConsentDisclosure], sdks?: [ConsentDisclosureSDK]) {
       this.disclosures = disclosures
+        this.sdks = sdks ?? []
   }
Evidence
TypeScript declares sdks as a tuple type, but both Android and iOS serialize sdks via collection
mapping (implying 0..n items). Android test expectations also include an empty sdks list, proving
emptiness is valid.

src/models/ConsentDisclosureObject.tsx[11-19]
android/src/main/java/com/usercentrics/reactnative/extensions/ConsentDisclosureSerializer.kt[7-15]
ios/Extensions/UsercentricsCMPData+Dict.swift[365-371]
android/src/androidTest/java/com/usercentrics/reactnative/mock/GetCMPDataMock.kt[632-646]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ConsentDisclosureObject.sdks` is declared as a tuple type (`[ConsentDisclosureSDK]`), but native returns a variable-length array (including empty). This makes the TS API contract incorrect.
### Issue Context
Android/iOS both serialize `sdks` via `.map { ... }` and Android test mocks include an empty `sdks` list.
### Fix Focus Areas
- src/models/ConsentDisclosureObject.tsx[11-19]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Android drops Long fields 🐞 Bug ✓ Correctness
Description
Android’s Map/List -> WritableMap/WritableArray conversion doesn’t handle Long values. Since
ConsentDisclosure.serialize() includes maxAgeSeconds and tests expect it as a Long, this field can
be silently omitted from the JS payload.
Code

android/src/main/java/com/usercentrics/reactnative/extensions/ConsentDisclosureSerializer.kt[R24-34]

internal fun ConsentDisclosure.serialize(): Any {
Evidence
ConsentDisclosureSerializer emits maxAgeSeconds into a Map. The conversion layer
(toWritableMap/serialize) only supports Boolean/Int/Double/String/Map/List/etc.; it has no Long
branch, so Long values are not written to the resulting React Native map/array. Android test
expectations include maxAgeSeconds as a Long literal, indicating this scenario occurs.

android/src/main/java/com/usercentrics/reactnative/extensions/ConsentDisclosureSerializer.kt[24-35]
android/src/main/java/com/usercentrics/reactnative/extensions/ReadableMapExtensions.kt[32-72]
android/src/main/java/com/usercentrics/reactnative/extensions/ReadableMapExtensions.kt[74-116]
android/src/androidTest/java/com/usercentrics/reactnative/mock/GetCMPDataMock.kt[632-646]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Android conversion from Kotlin `Map`/`List` to React Native `WritableMap`/`WritableArray` ignores `Long` values, which can silently drop fields like `maxAgeSeconds` from the JS payload.
### Issue Context
`ConsentDisclosure.serialize()` includes `maxAgeSeconds`, and Android tests expect it to be a `Long` (`123123L`). The conversion layer currently handles `Int`/`Double` but not `Long`.
### Fix Focus Areas
- android/src/main/java/com/usercentrics/reactnative/extensions/ReadableMapExtensions.kt[32-72]
- android/src/main/java/com/usercentrics/reactnative/extensions/ReadableMapExtensions.kt[74-116]
- android/src/main/java/com/usercentrics/reactnative/extensions/ConsentDisclosureSerializer.kt[24-35]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines 11 to 19
export class ConsentDisclosureObject {

disclosures: [ConsentDisclosure]
sdks: [ConsentDisclosureSDK]

constructor(disclosures: [ConsentDisclosure]) {
constructor(disclosures: [ConsentDisclosure], sdks?: [ConsentDisclosureSDK]) {
this.disclosures = disclosures
this.sdks = sdks ?? []
}

Choose a reason for hiding this comment

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

Action required

1. Ts sdks typed as tuple 🐞 Bug ✓ Correctness

ConsentDisclosureObject.sdks is typed as a 1-element tuple ([ConsentDisclosureSDK]) even though the
native payload is a variable-length list (including empty). This makes the published TypeScript API
incorrect and can break consumers that have 0 or multiple SDK entries.
Agent Prompt
### Issue description
`ConsentDisclosureObject.sdks` is declared as a tuple type (`[ConsentDisclosureSDK]`), but native returns a variable-length array (including empty). This makes the TS API contract incorrect.

### Issue Context
Android/iOS both serialize `sdks` via `.map { ... }` and Android test mocks include an empty `sdks` list.

### Fix Focus Areas
- src/models/ConsentDisclosureObject.tsx[11-19]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines 24 to 34
internal fun ConsentDisclosure.serialize(): Any {
return mapOf(
"identifier" to identifier,
"type" to type?.ordinal,
"name" to name,
"maxAgeSeconds" to maxAgeSeconds,
"cookieRefresh" to cookieRefresh,
"purposes" to purposes,
"domain" to domain,
"description" to description,
)

Choose a reason for hiding this comment

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

Action required

2. Android drops long fields 🐞 Bug ✓ Correctness

Android’s Map/List -> WritableMap/WritableArray conversion doesn’t handle Long values. Since
ConsentDisclosure.serialize() includes maxAgeSeconds and tests expect it as a Long, this field can
be silently omitted from the JS payload.
Agent Prompt
### Issue description
Android conversion from Kotlin `Map`/`List` to React Native `WritableMap`/`WritableArray` ignores `Long` values, which can silently drop fields like `maxAgeSeconds` from the JS payload.

### Issue Context
`ConsentDisclosure.serialize()` includes `maxAgeSeconds`, and Android tests expect it to be a `Long` (`123123L`). The conversion layer currently handles `Int`/`Double` but not `Long`.

### Fix Focus Areas
- android/src/main/java/com/usercentrics/reactnative/extensions/ReadableMapExtensions.kt[32-72]
- android/src/main/java/com/usercentrics/reactnative/extensions/ReadableMapExtensions.kt[74-116]
- android/src/main/java/com/usercentrics/reactnative/extensions/ConsentDisclosureSerializer.kt[24-35]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@uc-brunosilva uc-brunosilva merged commit 13a010c into master Feb 23, 2026
2 of 4 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Feb 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants