react-native-moengage-personalize 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/LICENSE.md +12 -0
  2. package/README.md +3 -0
  3. package/ReactNativeMoEngagePersonalize.podspec +30 -0
  4. package/android/build.gradle +69 -0
  5. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  6. package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  7. package/android/gradlew +185 -0
  8. package/android/gradlew.bat +89 -0
  9. package/android/src/main/AndroidManifest.xml +1 -0
  10. package/android/src/main/java/com/moengage/react/personalize/MoEngagePersonalizeHandler.kt +104 -0
  11. package/android/src/main/java/com/moengage/react/personalize/MoengagePersonalizePackage.kt +50 -0
  12. package/android/src/newarch/java/com/moengage/react/personalize/MoEReactPersonalize.kt +54 -0
  13. package/android/src/oldarch/java/com/moengage/react/personalize/MoEReactPersonalize.kt +63 -0
  14. package/ios/MoEReactNativePersonalizeHandler.h +26 -0
  15. package/ios/MoEReactNativePersonalizeHandler.m +118 -0
  16. package/ios/MoEngagePersonalizeBridge.h +15 -0
  17. package/ios/MoEngagePersonalizeBridge.mm +46 -0
  18. package/package.json +52 -0
  19. package/src/NativeMoEngagePersonalize.ts +51 -0
  20. package/src/index.ts +164 -0
  21. package/src/internal/Constants.ts +16 -0
  22. package/src/internal/MoEngagePersonalizeHandler.ts +97 -0
  23. package/src/internal/utils/PayloadBuilder.ts +91 -0
  24. package/src/internal/utils/PayloadParser.ts +117 -0
  25. package/src/model/DataSource.ts +12 -0
  26. package/src/model/ExperienceCampaign.ts +47 -0
  27. package/src/model/ExperienceCampaignFailure.ts +28 -0
  28. package/src/model/ExperienceCampaignMeta.ts +34 -0
  29. package/src/model/ExperienceCampaignsMetadata.ts +29 -0
  30. package/src/model/ExperienceCampaignsResult.ts +30 -0
  31. package/src/model/ExperienceFailureReason.ts +36 -0
  32. package/src/model/ExperienceStatus.ts +14 -0
@@ -0,0 +1,91 @@
1
+ import ExperienceCampaign from "../../model/ExperienceCampaign";
2
+ import { ExperienceStatus } from "../../model/ExperienceStatus";
3
+ import { MoEngageLogger } from "react-native-moengage";
4
+ import { MODULE_TAG } from "../Constants";
5
+
6
+ const TAG = `${MODULE_TAG}PayloadBuilder`;
7
+
8
+ function getAccountMetaPayload(appId: string) {
9
+ return { appId: appId };
10
+ }
11
+
12
+ function serializeCampaign(campaign: ExperienceCampaign) {
13
+ if (campaign.payload == null) {
14
+ MoEngageLogger.warn(`${TAG} serializeCampaign() : campaign.payload is null/undefined for "${campaign.experienceKey}"`);
15
+ }
16
+ if (campaign.experienceContext == null) {
17
+ MoEngageLogger.warn(`${TAG} serializeCampaign() : campaign.experienceContext is null/undefined for "${campaign.experienceKey}"`);
18
+ }
19
+ return {
20
+ experienceKey: campaign.experienceKey,
21
+ payload: campaign.payload,
22
+ experienceContext: campaign.experienceContext,
23
+ source: campaign.source
24
+ };
25
+ }
26
+
27
+ export function buildFetchExperiencesMetaPayload(appId: string, statuses: ExperienceStatus[]): string {
28
+ let payload = {
29
+ accountMeta: getAccountMetaPayload(appId),
30
+ data: {
31
+ status: statuses
32
+ }
33
+ };
34
+ return JSON.stringify(payload);
35
+ }
36
+
37
+ export function buildFetchExperiencesPayload(appId: string, experienceKeys: string[], attributes: Record<string, string>): string {
38
+ let payload = {
39
+ accountMeta: getAccountMetaPayload(appId),
40
+ data: {
41
+ experienceKeys: experienceKeys,
42
+ attributes: attributes
43
+ }
44
+ };
45
+ return JSON.stringify(payload);
46
+ }
47
+
48
+ export function buildExperiencesShownPayload(appId: string, campaigns: ExperienceCampaign[]): string {
49
+ let payload = {
50
+ accountMeta: getAccountMetaPayload(appId),
51
+ data: {
52
+ experiences: campaigns.map(serializeCampaign)
53
+ }
54
+ };
55
+ return JSON.stringify(payload);
56
+ }
57
+
58
+ export function buildExperienceClickedPayload(appId: string, campaign: ExperienceCampaign): string {
59
+ let payload = {
60
+ accountMeta: getAccountMetaPayload(appId),
61
+ data: {
62
+ experience: serializeCampaign(campaign)
63
+ }
64
+ };
65
+ return JSON.stringify(payload);
66
+ }
67
+
68
+ export function buildOfferingsShownPayload(appId: string, offeringPayloads: Record<string, any>[]): string {
69
+ let payload = {
70
+ accountMeta: getAccountMetaPayload(appId),
71
+ data: {
72
+ offeringPayloads: offeringPayloads
73
+ }
74
+ };
75
+ return JSON.stringify(payload);
76
+ }
77
+
78
+ export function buildOfferingClickedPayload(
79
+ appId: string,
80
+ campaign: ExperienceCampaign,
81
+ offeringPayload: Record<string, any>
82
+ ): string {
83
+ let payload = {
84
+ accountMeta: getAccountMetaPayload(appId),
85
+ data: {
86
+ experience: serializeCampaign(campaign),
87
+ offeringPayload: offeringPayload
88
+ }
89
+ };
90
+ return JSON.stringify(payload);
91
+ }
@@ -0,0 +1,117 @@
1
+ import ExperienceCampaign from "../../model/ExperienceCampaign";
2
+ import ExperienceCampaignFailure from "../../model/ExperienceCampaignFailure";
3
+ import ExperienceCampaignMeta from "../../model/ExperienceCampaignMeta";
4
+ import ExperienceCampaignsMetadata from "../../model/ExperienceCampaignsMetadata";
5
+ import ExperienceCampaignsResult from "../../model/ExperienceCampaignsResult";
6
+ import { DataSource } from "../../model/DataSource";
7
+ import { ExperienceStatus } from "../../model/ExperienceStatus";
8
+ import { ExperienceFailureReason, KNOWN_FAILURE_REASONS } from "../../model/ExperienceFailureReason";
9
+ import { MoEngageLogger } from "react-native-moengage";
10
+ import {
11
+ MODULE_TAG,
12
+ keyData,
13
+ keyStatus,
14
+ keyExperiences,
15
+ keyExperienceKey,
16
+ keyExperienceName,
17
+ keyPayload,
18
+ keyExperienceContext,
19
+ keySource,
20
+ keyFailures,
21
+ keyReason,
22
+ keyExperienceKeys
23
+ } from "../Constants";
24
+
25
+ const TAG = `${MODULE_TAG}PayloadParser`;
26
+
27
+ function parseDataSource(value: string): DataSource {
28
+ if (value === DataSource.CACHE) return DataSource.CACHE;
29
+ if (value === DataSource.NETWORK) return DataSource.NETWORK;
30
+ MoEngageLogger.warn(`${TAG} parseDataSource() : Unknown DataSource "${value}", defaulting to NETWORK`);
31
+ return DataSource.NETWORK;
32
+ }
33
+
34
+ function parseExperienceStatus(value: string): ExperienceStatus {
35
+ switch (value.toLowerCase()) {
36
+ case ExperienceStatus.ACTIVE: return ExperienceStatus.ACTIVE;
37
+ case ExperienceStatus.PAUSED: return ExperienceStatus.PAUSED;
38
+ case ExperienceStatus.SCHEDULED: return ExperienceStatus.SCHEDULED;
39
+ default:
40
+ MoEngageLogger.warn(`${TAG} parseExperienceStatus() : Unknown ExperienceStatus "${value}", defaulting to ACTIVE`);
41
+ return ExperienceStatus.ACTIVE;
42
+ }
43
+ }
44
+
45
+ function parseFailureReason(value: string): ExperienceFailureReason {
46
+ if (KNOWN_FAILURE_REASONS.has(value as ExperienceFailureReason)) {
47
+ return value as ExperienceFailureReason;
48
+ }
49
+ MoEngageLogger.warn(`${TAG} parseFailureReason() : Unknown ExperienceFailureReason "${value}", defaulting to PERSONALIZATION_FAILED`);
50
+ return "PERSONALIZATION_FAILED";
51
+ }
52
+
53
+ export function parseExperiencesMetadata(payload: string): ExperienceCampaignsMetadata {
54
+ MoEngageLogger.verbose(`${TAG} parseExperiencesMetadata() : ${payload}`);
55
+ const json = JSON.parse(payload);
56
+ const data = json[keyData];
57
+ if (data == null || typeof data !== "object") {
58
+ MoEngageLogger.warn(`${TAG} parseExperiencesMetadata() : missing or invalid 'data' key, returning empty result`);
59
+ return new ExperienceCampaignsMetadata(DataSource.NETWORK, []);
60
+ }
61
+
62
+ const source = parseDataSource(data[keySource]);
63
+ const experiences: ExperienceCampaignMeta[] = [];
64
+
65
+ const experiencesArray = data[keyExperiences];
66
+ if (Array.isArray(experiencesArray)) {
67
+ for (const exp of experiencesArray) {
68
+ if (exp == null || typeof exp !== "object") continue;
69
+ experiences.push(new ExperienceCampaignMeta(
70
+ exp[keyExperienceKey] ?? "",
71
+ exp[keyExperienceName] ?? "",
72
+ parseExperienceStatus(exp[keyStatus])
73
+ ));
74
+ }
75
+ }
76
+
77
+ return new ExperienceCampaignsMetadata(source, experiences);
78
+ }
79
+
80
+ export function parseExperiencesResult(payload: string): ExperienceCampaignsResult {
81
+ MoEngageLogger.verbose(`${TAG} parseExperiencesResult() : ${payload}`);
82
+ const json = JSON.parse(payload);
83
+ const data = json[keyData];
84
+ if (data == null || typeof data !== "object") {
85
+ MoEngageLogger.warn(`${TAG} parseExperiencesResult() : missing or invalid 'data' key, returning empty result`);
86
+ return new ExperienceCampaignsResult([], []);
87
+ }
88
+
89
+ const experiences: ExperienceCampaign[] = [];
90
+ const failures: ExperienceCampaignFailure[] = [];
91
+
92
+ const experiencesArray = data[keyExperiences];
93
+ if (Array.isArray(experiencesArray)) {
94
+ for (const exp of experiencesArray) {
95
+ if (exp == null || typeof exp !== "object") continue;
96
+ experiences.push(new ExperienceCampaign(
97
+ exp[keyExperienceKey] ?? "",
98
+ exp[keyPayload] ?? {},
99
+ exp[keyExperienceContext] ?? {},
100
+ parseDataSource(exp[keySource])
101
+ ));
102
+ }
103
+ }
104
+
105
+ const failuresArray = data[keyFailures];
106
+ if (Array.isArray(failuresArray)) {
107
+ for (const fail of failuresArray) {
108
+ if (fail == null || typeof fail !== "object") continue;
109
+ failures.push(new ExperienceCampaignFailure(
110
+ parseFailureReason(fail[keyReason]),
111
+ fail[keyExperienceKeys] ?? []
112
+ ));
113
+ }
114
+ }
115
+
116
+ return new ExperienceCampaignsResult(experiences, failures);
117
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Source from which an experience campaign or its metadata was retrieved.
3
+ *
4
+ * @author MoEngage
5
+ * @since 1.0.0
6
+ */
7
+ export enum DataSource {
8
+ /** Returned from local cache. */
9
+ CACHE = "CACHE",
10
+ /** Fetched from the MoEngage backend. */
11
+ NETWORK = "NETWORK",
12
+ }
@@ -0,0 +1,47 @@
1
+ import { DataSource } from "./DataSource";
2
+
3
+ /**
4
+ * Holds the data for a single experience campaign returned by the personalization
5
+ * service.
6
+ *
7
+ * @author MoEngage
8
+ * @since 1.0.0
9
+ */
10
+ export default class ExperienceCampaign {
11
+
12
+ /**
13
+ * Unique identifier for the experience.
14
+ * @since 1.0.0
15
+ */
16
+ experienceKey: string;
17
+
18
+ /**
19
+ * The JSON payload for the experience campaign.
20
+ * @since 1.0.0
21
+ */
22
+ payload: Record<string, any>;
23
+
24
+ /**
25
+ * Metadata describing the experience campaign (campaign id, audience, locale, etc.).
26
+ * @since 1.0.0
27
+ */
28
+ experienceContext: Record<string, string>;
29
+
30
+ /**
31
+ * Source from which the campaign was retrieved (cache or network).
32
+ * @since 1.0.0
33
+ */
34
+ source: DataSource;
35
+
36
+ constructor(
37
+ experienceKey: string,
38
+ payload: Record<string, any>,
39
+ experienceContext: Record<string, string>,
40
+ source: DataSource
41
+ ) {
42
+ this.experienceKey = experienceKey;
43
+ this.payload = payload;
44
+ this.experienceContext = experienceContext;
45
+ this.source = source;
46
+ }
47
+ }
@@ -0,0 +1,28 @@
1
+ import { ExperienceFailureReason } from "./ExperienceFailureReason";
2
+
3
+ /**
4
+ * Represents a categorized failure for one or more experience campaigns within
5
+ * a fetch result.
6
+ *
7
+ * @author MoEngage
8
+ * @since 1.0.0
9
+ */
10
+ export default class ExperienceCampaignFailure {
11
+
12
+ /**
13
+ * Reason this group of experience campaigns failed.
14
+ * @since 1.0.0
15
+ */
16
+ reason: ExperienceFailureReason;
17
+
18
+ /**
19
+ * List of experience keys that failed for this reason.
20
+ * @since 1.0.0
21
+ */
22
+ experienceKeys: string[];
23
+
24
+ constructor(reason: ExperienceFailureReason, experienceKeys: string[]) {
25
+ this.reason = reason;
26
+ this.experienceKeys = experienceKeys;
27
+ }
28
+ }
@@ -0,0 +1,34 @@
1
+ import { ExperienceStatus } from "./ExperienceStatus";
2
+
3
+ /**
4
+ * Lightweight metadata for an experience campaign returned by `fetchExperiencesMeta`.
5
+ *
6
+ * @author MoEngage
7
+ * @since 1.0.0
8
+ */
9
+ export default class ExperienceCampaignMeta {
10
+
11
+ /**
12
+ * Unique identifier for the experience.
13
+ * @since 1.0.0
14
+ */
15
+ experienceKey: string;
16
+
17
+ /**
18
+ * Human-readable name of the experience campaign.
19
+ * @since 1.0.0
20
+ */
21
+ experienceName: string;
22
+
23
+ /**
24
+ * Current status of the experience campaign.
25
+ * @since 1.0.0
26
+ */
27
+ status: ExperienceStatus;
28
+
29
+ constructor(experienceKey: string, experienceName: string, status: ExperienceStatus) {
30
+ this.experienceKey = experienceKey;
31
+ this.experienceName = experienceName;
32
+ this.status = status;
33
+ }
34
+ }
@@ -0,0 +1,29 @@
1
+ import { DataSource } from "./DataSource";
2
+ import ExperienceCampaignMeta from "./ExperienceCampaignMeta";
3
+
4
+ /**
5
+ * Container for the result of `fetchExperiencesMeta` — the source of the data
6
+ * and the list of experience metadata entries.
7
+ *
8
+ * @author MoEngage
9
+ * @since 1.0.0
10
+ */
11
+ export default class ExperienceCampaignsMetadata {
12
+
13
+ /**
14
+ * Source from which the metadata was retrieved (cache or network).
15
+ * @since 1.0.0
16
+ */
17
+ source: DataSource;
18
+
19
+ /**
20
+ * List of experience metadata entries.
21
+ * @since 1.0.0
22
+ */
23
+ experiences: ExperienceCampaignMeta[];
24
+
25
+ constructor(source: DataSource, experiences: ExperienceCampaignMeta[]) {
26
+ this.source = source;
27
+ this.experiences = experiences;
28
+ }
29
+ }
@@ -0,0 +1,30 @@
1
+ import ExperienceCampaign from "./ExperienceCampaign";
2
+ import ExperienceCampaignFailure from "./ExperienceCampaignFailure";
3
+
4
+ /**
5
+ * Container for the result of `fetchExperiences` — the successfully fetched
6
+ * campaigns plus any failures grouped by reason.
7
+ *
8
+ * @author MoEngage
9
+ * @since 1.0.0
10
+ */
11
+ export default class ExperienceCampaignsResult {
12
+
13
+ /**
14
+ * Successfully fetched experience campaigns.
15
+ * @since 1.0.0
16
+ */
17
+ experiences: ExperienceCampaign[];
18
+
19
+ /**
20
+ * Failures grouped by reason — each entry lists the experience keys that
21
+ * failed for that reason.
22
+ * @since 1.0.0
23
+ */
24
+ failures: ExperienceCampaignFailure[];
25
+
26
+ constructor(experiences: ExperienceCampaign[], failures: ExperienceCampaignFailure[]) {
27
+ this.experiences = experiences;
28
+ this.failures = failures;
29
+ }
30
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Server-side failure reasons for an experience campaign fetch. Mirrors the
3
+ * `ExperienceFailureReason` enum in the contract proto.
4
+ *
5
+ * SDK-level errors (network errors, SDK not initialized, etc.) surface via
6
+ * `Promise.reject` from the fetch APIs and are not part of this union.
7
+ *
8
+ * @author MoEngage
9
+ * @since 1.0.0
10
+ */
11
+ export type ExperienceFailureReason =
12
+ | "USER_IN_CAMPAIGN_CONTROL_GROUP"
13
+ | "USER_IN_GLOBAL_CONTROL_GROUP"
14
+ | "USER_NOT_IN_SEGMENT"
15
+ | "INVALID_EXPERIENCE_KEY"
16
+ | "MAX_LIMIT_BREACHED"
17
+ | "EXPERIENCE_NOT_ACTIVE"
18
+ | "EXPERIENCE_EXPIRED"
19
+ | "PERSONALIZATION_FAILED";
20
+
21
+ /**
22
+ * Set of all known failure reasons. Used by the parser to validate incoming
23
+ * values and fall back to `PERSONALIZATION_FAILED` for unknown values.
24
+ *
25
+ * @since 1.0.0
26
+ */
27
+ export const KNOWN_FAILURE_REASONS: ReadonlySet<ExperienceFailureReason> = new Set([
28
+ "USER_IN_CAMPAIGN_CONTROL_GROUP",
29
+ "USER_IN_GLOBAL_CONTROL_GROUP",
30
+ "USER_NOT_IN_SEGMENT",
31
+ "INVALID_EXPERIENCE_KEY",
32
+ "MAX_LIMIT_BREACHED",
33
+ "EXPERIENCE_NOT_ACTIVE",
34
+ "EXPERIENCE_EXPIRED",
35
+ "PERSONALIZATION_FAILED",
36
+ ]);
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Lifecycle status of an experience campaign on the MoEngage dashboard.
3
+ *
4
+ * @author MoEngage
5
+ * @since 1.0.0
6
+ */
7
+ export enum ExperienceStatus {
8
+ /** Currently running. */
9
+ ACTIVE = "active",
10
+ /** Manually paused on the dashboard. */
11
+ PAUSED = "paused",
12
+ /** Scheduled to start in the future. */
13
+ SCHEDULED = "scheduled",
14
+ }