diff --git a/src/forwarders.interfaces.ts b/src/forwarders.interfaces.ts index 17caa1a67..de2d6d71e 100644 --- a/src/forwarders.interfaces.ts +++ b/src/forwarders.interfaces.ts @@ -85,6 +85,8 @@ export interface ConfiguredKit removeUserAttribute(key: string): string; setUserAttribute(key: string, value: string): string; setUserIdentity(id: UserIdentityId, type: UserIdentityType): void; + // Legacy method present on some kit implementations; invoked on logout. + logOut?: (evt: SDKEvent) => void; // TODO: https://go.mparticle.com/work/SQDSDKS-5156 isSandbox: boolean; diff --git a/src/identity-utils.ts b/src/identity-utils.ts index 59ef35274..d8e5d43b6 100644 --- a/src/identity-utils.ts +++ b/src/identity-utils.ts @@ -5,8 +5,8 @@ import Types from './types'; import { IdentityApiData, UserIdentities, - IdentityCallback, } from '@mparticle/web-sdk'; +import { IdentityCallback } from './identity-user-interfaces'; import { IdentityAPIMethod, IIdentityRequest } from './identity.interfaces'; import { IdentityResultBody, diff --git a/src/identity.interfaces.ts b/src/identity.interfaces.ts index a223b77d2..36699fdac 100644 --- a/src/identity.interfaces.ts +++ b/src/identity.interfaces.ts @@ -1,4 +1,5 @@ import { IdentityApiData, MPID, UserIdentities } from '@mparticle/web-sdk'; +import { Context } from '@mparticle/event-models'; import AudienceManager from './audienceManager'; import { ICachedIdentityCall, IKnownIdentities } from './identity-utils'; import { BaseVault } from './vault'; @@ -18,6 +19,7 @@ const { platform, sdkVendor, sdkVersion, HTTPCodes } = Constants; export type IdentityPreProcessResult = { valid: boolean; error?: string; + cleanedIdentities?: IdentityApiData; }; export type IdentityAPIMethod = valueof; @@ -49,14 +51,14 @@ export enum SDKIdentityTypeEnum { export interface IIdentityAPIRequestData { client_sdk: { - platform: typeof platform; - sdk_vendor: typeof sdkVendor; - sdk_version: typeof sdkVersion; + platform: string; + sdk_vendor: string; + sdk_version: string; }; - context: string | null; + context: Context | null; environment: Environment; request_id: string; - request_timestamp_unixtime_ms: number; + request_timestamp_ms: number; previous_mpid: MPID | null; known_identities: IKnownIdentities; } @@ -86,7 +88,7 @@ export interface IIdentityRequest { sdkVendor: string, sdkVersion: string, deviceId: string, - context: string | null, + context: Context | null, mpid: MPID ): IIdentityAPIRequestData; createModifyIdentityRequest( @@ -95,17 +97,20 @@ export interface IIdentityRequest { platform: string, sdkVendor: string, sdkVersion: string, - context: string | null + context: Context | null ): IIdentityAPIModifyRequestData; createIdentityChanges( previousIdentities: UserIdentities, - newIdentitie: UserIdentities - ): IIdentityAPIIdentityChangeData; + newIdentities: UserIdentities + ): IIdentityAPIIdentityChangeData[]; preProcessIdentityRequest( identityApiData: IdentityApiData, callback: IdentityCallback, method: IdentityAPIMethod ): IdentityPreProcessResult; + createAliasNetworkRequest(aliasRequest: IAliasRequest): object; + convertAliasToNative(aliasRequest: IAliasRequest): object; + convertToNative(identityApiData: IdentityApiData): object | void; } export type AliasRequestScope = 'device' | 'mpid'; @@ -150,7 +155,7 @@ export interface SDKIdentityApi { getUsers?(): IMParticleUser[]; aliasUsers?( aliasRequest?: IAliasRequest, - callback?: IdentityCallback + callback?: IAliasCallback ): void; createAliasRequest?( sourceUser: IMParticleUser, @@ -211,12 +216,12 @@ export interface IIdentity { IdentityAPI: SDKIdentityApi; IdentityRequest: IIdentityRequest; - mParticleUser(mpid: MPID, IsLoggedIn: boolean): IMParticleUser; + mParticleUser(mpid?: MPID, IsLoggedIn?: boolean): IMParticleUser; createUserAttributeChange( key: string, - newValue: string, - previousUserAttributeValue: string, + newValue: string | string[] | null, + previousUserAttributeValue: string | string[] | null, isNewAttribute: boolean, deleted: boolean, user: IMParticleUser @@ -234,13 +239,13 @@ export interface IIdentity { callback: IdentityCallback, identityApiData: IdentityApiData, method: IdentityAPIMethod, - knownIdentities: UserIdentities, + knownIdentities: IKnownIdentities, parsingCachedResponse: boolean ): void; sendUserAttributeChangeEvent( attributeKey: string, - newUserAttributeValue: string, - previousUserAttributeValue: string, + newUserAttributeValue: string | string[] | null, + previousUserAttributeValue: string | string[] | null, isNewAttribute: boolean, deleted: boolean, user: IMParticleUser @@ -251,6 +256,14 @@ export interface IIdentity { mpid: MPID, prevUserIdentities: UserIdentities ): void; + reinitForwardersOnUserChange( + prevUser: IMParticleUser, + newUser: IMParticleUser + ): void; + setForwarderCallbacks( + user: IMParticleUser, + method: IdentityAPIMethod + ): void; /** * @deprecated diff --git a/src/identity.js b/src/identity.ts similarity index 85% rename from src/identity.js rename to src/identity.ts index 29c9dacb9..e74271fb1 100644 --- a/src/identity.js +++ b/src/identity.ts @@ -1,36 +1,68 @@ import Constants, { HTTP_OK } from './constants'; import Types, { IdentityType } from './types'; +import AudienceManager, { IAudienceMemberships } from './audienceManager'; import { cacheOrClearIdCache, createKnownIdentities, executeSearchRequest, + IKnownIdentities, + IParseCachedIdentityResponse, normalizeUserIdentityKeys, tryCacheIdentity, } from './identity-utils'; -import AudienceManager from './audienceManager'; -const { Messages, HTTPCodes, FeatureFlags, IdentityMethods } = Constants; -const { ErrorMessages } = Messages; -const { CacheIdentity } = FeatureFlags; -const { Identify, Modify, Login, Logout } = IdentityMethods; +import { + IdentityCallback, + IIdentityResponse, + IMParticleUser, + mParticleUserCart, +} from './identity-user-interfaces'; +import { + AliasRequestScope, + IAliasCallback, + IAliasRequest, + IdentityAPIMethod, + IIdentity, + IIdentityAPIRequestData, + SDKIdentityTypeEnum, +} from './identity.interfaces'; +import { IdentitySearchCallback } from './identity/search'; +import { IMParticleWebSDKInstance } from './mp-instance'; +import { processReadyQueue } from './pre-init-utils'; +import { BaseEvent, SDKEvent, SDKLoggerApi } from './sdkRuntimeModels'; +import { hasMPIDAndUserLoginChanged, hasMPIDChanged } from './user-utils'; import { generateDeprecationMessage, isEmpty, isFunction, isObject, } from './utils'; -import { hasMPIDAndUserLoginChanged, hasMPIDChanged } from './user-utils'; -import { processReadyQueue } from './pre-init-utils'; +import { + ConsentState, + IdentityApiData, + MPID, + UserAttributesValue, + UserIdentities, +} from '@mparticle/web-sdk'; +import { Context } from '@mparticle/event-models'; + +const { Messages, HTTPCodes, FeatureFlags, IdentityMethods } = Constants; +const { ErrorMessages } = Messages; +const { CacheIdentity } = FeatureFlags; +const { Identify, Modify, Login, Logout } = IdentityMethods; -export default function Identity(mpInstance) { +export default function Identity( + this: IIdentity, + mpInstance: IMParticleWebSDKInstance +) { const { getFeatureFlag, extend } = mpInstance._Helpers; - var self = this; + const self: IIdentity = this; this.idCache = null; this.audienceManager = null; // https://go.mparticle.com/work/SQDSDKS-6353 this.IdentityRequest = { - preProcessIdentityRequest: function(identityApiData, callback, method) { + preProcessIdentityRequest: function(identityApiData: IdentityApiData, callback: IdentityCallback, method: IdentityAPIMethod) { mpInstance.Logger.verbose( Messages.InformationMessages.StartingLogEvent + ': ' + method ); @@ -51,7 +83,7 @@ export default function Identity(mpInstance) { } : removedFalsyIdentityData; - var identityValidationResult = mpInstance._Helpers.Validators.validateIdentities( + const identityValidationResult = mpInstance._Helpers.Validators.validateIdentities( cleanedIdentityApiData, method ); @@ -70,7 +102,7 @@ export default function Identity(mpInstance) { callback && !mpInstance._Helpers.Validators.isFunction(callback) ) { - var error = + const error = 'The optional callback must be a function. You tried entering a(n) ' + typeof callback; mpInstance.Logger.error(error); @@ -87,24 +119,24 @@ export default function Identity(mpInstance) { }, createIdentityRequest: function( - identityApiData, - platform, - sdkVendor, - sdkVersion, - deviceId, - context, - mpid + identityApiData: IdentityApiData, + platform: string, + sdkVendor: string, + sdkVersion: string, + deviceId: string, + context: Context | null, + mpid: MPID ) { - var APIRequest = { + const APIRequest: IIdentityAPIRequestData = { client_sdk: { platform: platform, sdk_vendor: sdkVendor, sdk_version: sdkVersion, }, context: context, - environment: mpInstance._Store.SDKConfig.isDevelopmentMode + environment: (mpInstance._Store.SDKConfig.isDevelopmentMode ? 'development' - : 'production', + : 'production'), request_id: mpInstance._Helpers.generateUniqueId(), request_timestamp_ms: new Date().getTime(), previous_mpid: mpid || null, @@ -118,12 +150,12 @@ export default function Identity(mpInstance) { }, createModifyIdentityRequest: function( - currentUserIdentities, - newUserIdentities, - platform, - sdkVendor, - sdkVersion, - context + currentUserIdentities: UserIdentities, + newUserIdentities: UserIdentities, + platform: string, + sdkVendor: string, + sdkVersion: string, + context: Context | null ) { return { client_sdk: { @@ -132,9 +164,9 @@ export default function Identity(mpInstance) { sdk_version: sdkVersion, }, context: context, - environment: mpInstance._Store.SDKConfig.isDevelopmentMode + environment: (mpInstance._Store.SDKConfig.isDevelopmentMode ? 'development' - : 'production', + : 'production'), request_id: mpInstance._Helpers.generateUniqueId(), request_timestamp_ms: new Date().getTime(), identity_changes: this.createIdentityChanges( @@ -144,16 +176,15 @@ export default function Identity(mpInstance) { }; }, - createIdentityChanges: function(previousIdentities, newIdentities) { - var identityChanges = []; - var key; + createIdentityChanges: function(previousIdentities: UserIdentities, newIdentities: UserIdentities) { + const identityChanges = []; if ( newIdentities && isObject(newIdentities) && previousIdentities && isObject(previousIdentities) ) { - for (key in newIdentities) { + for (const key in newIdentities) { identityChanges.push({ old_value: previousIdentities[key] || null, new_value: newIdentities[key], @@ -166,12 +197,12 @@ export default function Identity(mpInstance) { }, // takes 2 UI objects keyed by name, combines them, returns them keyed by type - combineUserIdentities: function(previousUIByName, newUIByName) { - var combinedUIByType = {}; - var combinedUIByName = extend({}, previousUIByName, newUIByName); + combineUserIdentities: function(previousUIByName: UserIdentities, newUIByName: UserIdentities) { + const combinedUIByType = {}; + const combinedUIByName = extend({}, previousUIByName, newUIByName); - for (var key in combinedUIByName) { - var type = Types.IdentityType.getIdentityType(key); + for (const key in combinedUIByName) { + const type = Types.IdentityType.getIdentityType(key); // this check removes anything that is not whitelisted as an identity type if (type !== false && type >= 0) { combinedUIByType[Types.IdentityType.getIdentityType(key)] = @@ -182,7 +213,7 @@ export default function Identity(mpInstance) { return combinedUIByType; }, - createAliasNetworkRequest: function(aliasRequest) { + createAliasNetworkRequest: function(aliasRequest: IAliasRequest) { return { request_id: mpInstance._Helpers.generateUniqueId(), request_type: 'alias', @@ -201,7 +232,7 @@ export default function Identity(mpInstance) { }; }, - convertAliasToNative: function(aliasRequest) { + convertAliasToNative: function(aliasRequest: IAliasRequest) { return { DestinationMpid: aliasRequest.destinationMpid, SourceMpid: aliasRequest.sourceMpid, @@ -211,10 +242,10 @@ export default function Identity(mpInstance) { }; }, - convertToNative: function(identityApiData) { - var nativeIdentityRequest = []; + convertToNative: function(identityApiData: IdentityApiData) { + const nativeIdentityRequest = []; if (identityApiData && identityApiData.userIdentities) { - for (var key in identityApiData.userIdentities) { + for (const key in identityApiData.userIdentities) { if (identityApiData.userIdentities.hasOwnProperty(key)) { nativeIdentityRequest.push({ Type: Types.IdentityType.getIdentityType(key), @@ -227,6 +258,7 @@ export default function Identity(mpInstance) { UserIdentities: nativeIdentityRequest, }; } + return undefined; }, }; /** @@ -242,21 +274,21 @@ export default function Identity(mpInstance) { * @param {Object} identityApiData The identityApiData object as indicated [here](https://github.com/mParticle/mparticle-sdk-javascript/blob/master-v2/README.md#1-customize-the-sdk) * @param {Function} [callback] A callback function that is called when the identify request completes */ - identify: function(identityApiData, callback) { + identify: function(identityApiData: IdentityApiData, callback: IdentityCallback) { // https://go.mparticle.com/work/SQDSDKS-6337 - var mpid, - currentUser = mpInstance.Identity.getCurrentUser(), - preProcessResult = mpInstance._Identity.IdentityRequest.preProcessIdentityRequest( - identityApiData, - callback, - Identify - ); + let mpid; + const currentUser = mpInstance.Identity.getCurrentUser(); + const preProcessResult = mpInstance._Identity.IdentityRequest.preProcessIdentityRequest( + identityApiData, + callback, + Identify + ); if (currentUser) { mpid = currentUser.getMPID(); } if (preProcessResult.valid) { - var identityApiRequest = mpInstance._Identity.IdentityRequest.createIdentityRequest( + const identityApiRequest = mpInstance._Identity.IdentityRequest.createIdentityRequest( preProcessResult.cleanedIdentities, Constants.platform, Constants.sdkVendor, @@ -273,7 +305,7 @@ export default function Identity(mpInstance) { const successfullyCachedIdentity = tryCacheIdentity( identityApiRequest.known_identities, self.idCache, - self.parseIdentityResponse, + self.parseIdentityResponse as IParseCachedIdentityResponse, mpid, callback, identityApiData, @@ -327,7 +359,9 @@ export default function Identity(mpInstance) { HTTPCodes.validationIssue, preProcessResult.error ); - mpInstance.Logger.verbose(preProcessResult); + mpInstance.Logger.verbose( + preProcessResult.error ?? JSON.stringify(preProcessResult) + ); } }, /** @@ -336,22 +370,22 @@ export default function Identity(mpInstance) { * @param {Object} identityApiData The identityApiData object as indicated [here](https://github.com/mParticle/mparticle-sdk-javascript/blob/master-v2/README.md#1-customize-the-sdk) * @param {Function} [callback] A callback function that is called when the logout request completes */ - logout: function(identityApiData, callback) { + logout: function(identityApiData: IdentityApiData, callback: IdentityCallback) { // https://go.mparticle.com/work/SQDSDKS-6337 - var mpid, - currentUser = mpInstance.Identity.getCurrentUser(), - preProcessResult = mpInstance._Identity.IdentityRequest.preProcessIdentityRequest( - identityApiData, - callback, - Logout - ); + let mpid; + const currentUser = mpInstance.Identity.getCurrentUser(); + const preProcessResult = mpInstance._Identity.IdentityRequest.preProcessIdentityRequest( + identityApiData, + callback, + Logout + ); if (currentUser) { mpid = currentUser.getMPID(); } if (preProcessResult.valid) { - var evt, - identityApiRequest = mpInstance._Identity.IdentityRequest.createIdentityRequest( + let evt; + const identityApiRequest = mpInstance._Identity.IdentityRequest.createIdentityRequest( preProcessResult.cleanedIdentities, Constants.platform, Constants.sdkVendor, @@ -395,7 +429,7 @@ export default function Identity(mpInstance) { mpInstance._Store.activeForwarders.forEach(function( forwarder ) { - if (forwarder.logOut) { + if (typeof forwarder.logOut === 'function') { forwarder.logOut(evt); } }); @@ -417,7 +451,9 @@ export default function Identity(mpInstance) { HTTPCodes.validationIssue, preProcessResult.error ); - mpInstance.Logger.verbose(preProcessResult); + mpInstance.Logger.verbose( + preProcessResult.error ?? JSON.stringify(preProcessResult) + ); } }, /** @@ -426,22 +462,22 @@ export default function Identity(mpInstance) { * @param {Object} identityApiData The identityApiData object as indicated [here](https://github.com/mParticle/mparticle-sdk-javascript/blob/master-v2/README.md#1-customize-the-sdk) * @param {Function} [callback] A callback function that is called when the login request completes */ - login: function(identityApiData, callback) { + login: function(identityApiData: IdentityApiData, callback: IdentityCallback) { // https://go.mparticle.com/work/SQDSDKS-6337 - var mpid, - currentUser = mpInstance.Identity.getCurrentUser(), - preProcessResult = mpInstance._Identity.IdentityRequest.preProcessIdentityRequest( - identityApiData, - callback, - Login - ); + let mpid; + const currentUser = mpInstance.Identity.getCurrentUser(); + const preProcessResult = mpInstance._Identity.IdentityRequest.preProcessIdentityRequest( + identityApiData, + callback, + Login + ); if (currentUser) { mpid = currentUser.getMPID(); } if (preProcessResult.valid) { - var identityApiRequest = mpInstance._Identity.IdentityRequest.createIdentityRequest( + const identityApiRequest = mpInstance._Identity.IdentityRequest.createIdentityRequest( preProcessResult.cleanedIdentities, Constants.platform, Constants.sdkVendor, @@ -459,7 +495,7 @@ export default function Identity(mpInstance) { const successfullyCachedIdentity = tryCacheIdentity( identityApiRequest.known_identities, self.idCache, - self.parseIdentityResponse, + self.parseIdentityResponse as IParseCachedIdentityResponse, mpid, callback, identityApiData, @@ -513,7 +549,9 @@ export default function Identity(mpInstance) { HTTPCodes.validationIssue, preProcessResult.error ); - mpInstance.Logger.verbose(preProcessResult); + mpInstance.Logger.verbose( + preProcessResult.error ?? JSON.stringify(preProcessResult) + ); } }, /** @@ -522,24 +560,24 @@ export default function Identity(mpInstance) { * @param {Object} identityApiData The identityApiData object as indicated [here](https://github.com/mParticle/mparticle-sdk-javascript/blob/master-v2/README.md#1-customize-the-sdk) * @param {Function} [callback] A callback function that is called when the modify request completes */ - modify: function(identityApiData, callback) { + modify: function(identityApiData: IdentityApiData, callback: IdentityCallback) { // https://go.mparticle.com/work/SQDSDKS-6337 - var mpid, - currentUser = mpInstance.Identity.getCurrentUser(), - preProcessResult = mpInstance._Identity.IdentityRequest.preProcessIdentityRequest( - identityApiData, - callback, - Modify - ); + let mpid; + const currentUser = mpInstance.Identity.getCurrentUser(); + const preProcessResult = mpInstance._Identity.IdentityRequest.preProcessIdentityRequest( + identityApiData, + callback, + Modify + ); if (currentUser) { mpid = currentUser.getMPID(); } if (preProcessResult.valid) { - var newUserIdentities = + const newUserIdentities = identityApiData && identityApiData.userIdentities ? preProcessResult.cleanedIdentities.userIdentities : {}; - var identityApiRequest = mpInstance._Identity.IdentityRequest.createModifyIdentityRequest( + const identityApiRequest = mpInstance._Identity.IdentityRequest.createModifyIdentityRequest( currentUser ? currentUser.getUserIdentities().userIdentities : {}, @@ -572,8 +610,7 @@ export default function Identity(mpInstance) { callback, identityApiData, self.parseIdentityResponse, - mpid, - identityApiRequest.known_identities + mpid ); } } else { @@ -592,7 +629,9 @@ export default function Identity(mpInstance) { HTTPCodes.validationIssue, preProcessResult.error ); - mpInstance.Logger.verbose(preProcessResult); + mpInstance.Logger.verbose( + preProcessResult.error ?? JSON.stringify(preProcessResult) + ); } }, /** @@ -601,7 +640,7 @@ export default function Identity(mpInstance) { * @return {Object} the current user object */ getCurrentUser: function() { - var mpid; + let mpid; if (mpInstance._Store) { mpid = mpInstance._Store.mpid; if (mpid) { @@ -627,8 +666,8 @@ export default function Identity(mpInstance) { * @param {String} mpid of the desired user * @return {Object} the user for mpid */ - getUser: function(mpid) { - var persistence = mpInstance._Persistence.getPersistence(); + getUser: function(mpid: MPID) { + const persistence = mpInstance._Persistence.getPersistence(); if (persistence) { if ( persistence[mpid] && @@ -649,18 +688,18 @@ export default function Identity(mpInstance) { * @return {Array} array of users */ getUsers: function() { - var persistence = mpInstance._Persistence.getPersistence(); - var users = []; + const persistence = mpInstance._Persistence.getPersistence(); + const users = []; if (persistence) { - for (var key in persistence) { + for (const key in persistence) { if (!Constants.SDKv2NonMPIDCookieKeys.hasOwnProperty(key)) { users.push(self.mParticleUser(key)); } } } users.sort(function(a, b) { - var aLastSeen = a.getLastSeenTime() || 0; - var bLastSeen = b.getLastSeenTime() || 0; + const aLastSeen = a.getLastSeenTime() || 0; + const bLastSeen = b.getLastSeenTime() || 0; if (aLastSeen > bLastSeen) { return -1; } else { @@ -676,8 +715,8 @@ export default function Identity(mpInstance) { * @param {Object} aliasRequest object representing an AliasRequest * @param {Function} [callback] A callback function that is called when the aliasUsers request completes */ - aliasUsers: function(aliasRequest, callback) { - var message; + aliasUsers: function(aliasRequest: IAliasRequest, callback: IAliasCallback) { + let message; if (!aliasRequest.destinationMpid || !aliasRequest.sourceMpid) { message = Messages.ValidationMessages.AliasMissingMpid; } @@ -722,9 +761,9 @@ export default function Identity(mpInstance) { ' -> ' + aliasRequest.destinationMpid ); - var aliasRequestMessage = mpInstance._Identity.IdentityRequest.createAliasNetworkRequest( + const aliasRequestMessage = mpInstance._Identity.IdentityRequest.createAliasNetworkRequest( aliasRequest - ); + ) as IAliasRequest; mpInstance._IdentityAPIClient.sendAliasRequest( aliasRequestMessage, callback @@ -759,7 +798,7 @@ export default function Identity(mpInstance) { * @param {Object} knownIdentities A `UserIdentities` map. * @param {Function} callback Invoked with the `IIdentitySearchResult`. */ - search: function(workspaceApiKey, knownIdentities, callback) { + search: function(workspaceApiKey: string, knownIdentities: UserIdentities, callback: IdentitySearchCallback) { executeSearchRequest( mpInstance, workspaceApiKey, @@ -783,7 +822,7 @@ export default function Identity(mpInstance) { after applying this adjustment it will be impossible to create an aliasRequest passes the aliasUsers() validation that the startTime must be less than the endTime */ - createAliasRequest: function(sourceUser, destinationUser, scope) { + createAliasRequest: function(sourceUser: IMParticleUser, destinationUser: IMParticleUser, scope: AliasRequestScope) { try { if (!destinationUser || !sourceUser) { mpInstance.Logger.error( @@ -791,7 +830,7 @@ export default function Identity(mpInstance) { ); return null; } - var startTime = sourceUser.getFirstSeenTime(); + let startTime = sourceUser.getFirstSeenTime(); if (!startTime) { mpInstance.Identity.getUsers().forEach(function(user) { if ( @@ -802,14 +841,14 @@ export default function Identity(mpInstance) { } }); } - var minFirstSeenTimeMs = + const minFirstSeenTimeMs = new Date().getTime() - mpInstance._Store.SDKConfig.aliasMaxWindow * 24 * 60 * 60 * 1000; - var endTime = + const endTime = sourceUser.getLastSeenTime() || new Date().getTime(); //if the startTime is greater than $maxAliasWindow ago, adjust the startTime to the earliest allowed if (startTime < minFirstSeenTimeMs) { @@ -844,8 +883,7 @@ export default function Identity(mpInstance) { * Example: mParticle.Identity.getCurrentUser().getAllUserAttributes() * @class mParticle.Identity.getCurrentUser() */ - this.mParticleUser = function(mpid, isLoggedIn) { - var self = this; + this.mParticleUser = function(mpid?: MPID, isLoggedIn?: boolean): IMParticleUser { return { /** * Get user identities for current user @@ -856,7 +894,7 @@ export default function Identity(mpInstance) { const currentUserIdentities = {}; const identities = mpInstance._Store.getUserIdentities(mpid); - for (var identityType in identities) { + for (const identityType in identities) { if (identities.hasOwnProperty(identityType)) { currentUserIdentities[ Types.IdentityType.getIdentityName( @@ -883,7 +921,7 @@ export default function Identity(mpInstance) { * @method setUserTag * @param {String} tagName */ - setUserTag: function(tagName) { + setUserTag: function(tagName: string) { if (!mpInstance._Helpers.Validators.isValidKeyValue(tagName)) { mpInstance.Logger.error(Messages.ErrorMessages.BadKey); return; @@ -896,7 +934,7 @@ export default function Identity(mpInstance) { * @method removeUserTag * @param {String} tagName */ - removeUserTag: function(tagName) { + removeUserTag: function(tagName: string) { if (!mpInstance._Helpers.Validators.isValidKeyValue(tagName)) { mpInstance.Logger.error(Messages.ErrorMessages.BadKey); return; @@ -912,7 +950,7 @@ export default function Identity(mpInstance) { */ // https://go.mparticle.com/work/SQDSDKS-4576 // https://go.mparticle.com/work/SQDSDKS-6373 - setUserAttribute: function(key, newValue) { + setUserAttribute: function(key: string, newValue: string) { mpInstance._SessionManager.resetSessionTimer(); if (mpInstance._Helpers.canLog()) { @@ -988,11 +1026,11 @@ export default function Identity(mpInstance) { * @param {Object} user attribute object with keys of the attribute type, and value of the attribute value */ // https://go.mparticle.com/work/SQDSDKS-6373 - setUserAttributes: function(userAttributes) { + setUserAttributes: function(userAttributes: Record) { mpInstance._SessionManager.resetSessionTimer(); if (isObject(userAttributes)) { if (mpInstance._Helpers.canLog()) { - for (var key in userAttributes) { + for (const key in userAttributes) { if (userAttributes.hasOwnProperty(key)) { this.setUserAttribute(key, userAttributes[key]); } @@ -1010,8 +1048,9 @@ export default function Identity(mpInstance) { * @method removeUserAttribute * @param {String} key */ - removeUserAttribute: function(key) { - var cookies, userAttributes; + removeUserAttribute: function(key: string) { + let cookies; + let userAttributes; mpInstance._SessionManager.resetSessionTimer(); if (!mpInstance._Helpers.Validators.isValidKeyValue(key)) { @@ -1029,7 +1068,7 @@ export default function Identity(mpInstance) { userAttributes = this.getAllUserAttributes(); - var existingProp = mpInstance._Helpers.findKeyInObject( + const existingProp = mpInstance._Helpers.findKeyInObject( userAttributes, key ); @@ -1038,7 +1077,7 @@ export default function Identity(mpInstance) { key = existingProp; } - var deletedUAKeyCopy = userAttributes[key] + const deletedUAKeyCopy = userAttributes[key] ? userAttributes[key].toString() : null; @@ -1076,7 +1115,7 @@ export default function Identity(mpInstance) { * @param {Array} value an array of values */ // https://go.mparticle.com/work/SQDSDKS-6373 - setUserAttributeList: function(key, newValue) { + setUserAttributeList: function(key: string, newValue: UserAttributesValue[]) { mpInstance._SessionManager.resetSessionTimer(); if (!mpInstance._Helpers.Validators.isValidKeyValue(key)) { @@ -1087,7 +1126,7 @@ export default function Identity(mpInstance) { if (!Array.isArray(newValue)) { mpInstance.Logger.error( 'The value you passed in to setUserAttributeList must be an array. You passed in a ' + - typeof value + typeof newValue ); return; } @@ -1135,7 +1174,7 @@ export default function Identity(mpInstance) { ) { userAttributeChange = true; } else { - for (var i = 0; i < newValue.length; i++) { + for (let i = 0; i < newValue.length; i++) { if (previousUserAttributeValue[i] !== newValue[i]) { userAttributeChange = true; break; @@ -1146,7 +1185,7 @@ export default function Identity(mpInstance) { if (userAttributeChange) { self.sendUserAttributeChangeEvent( key, - newValue, + newValue as unknown as string[], previousUserAttributeValue, isNewAttribute, false, @@ -1170,7 +1209,7 @@ export default function Identity(mpInstance) { * @method removeAllUserAttributes */ removeAllUserAttributes: function() { - var userAttributes; + let userAttributes; mpInstance._SessionManager.resetSessionTimer(); @@ -1186,7 +1225,7 @@ export default function Identity(mpInstance) { mpInstance._APIClient.prepareForwardingStats ); if (userAttributes) { - for (var prop in userAttributes) { + for (const prop in userAttributes) { if (userAttributes.hasOwnProperty(prop)) { mpInstance._Forwarders.handleForwarderUserAttributes( 'removeUserAttribute', @@ -1205,11 +1244,11 @@ export default function Identity(mpInstance) { * @return {Object} an object of only keys with array values. Example: { attr1: [1, 2, 3], attr2: ['a', 'b', 'c'] } */ getUserAttributesLists: function() { - var userAttributes, - userAttributesLists = {}; + let userAttributes; + const userAttributesLists = {}; userAttributes = this.getAllUserAttributes(); - for (var key in userAttributes) { + for (const key in userAttributes) { if ( userAttributes.hasOwnProperty(key) && Array.isArray(userAttributes[key]) @@ -1233,12 +1272,11 @@ export default function Identity(mpInstance) { if (userAttributes) { for (const prop in userAttributes) { if (userAttributes.hasOwnProperty(prop)) { - if (Array.isArray(userAttributes[prop])) { - userAttributesCopy[prop] = userAttributes[ - prop - ].slice(); + const attrValue = userAttributes[prop]; + if (Array.isArray(attrValue)) { + userAttributesCopy[prop] = attrValue.slice(); } else { - userAttributesCopy[prop] = userAttributes[prop]; + userAttributesCopy[prop] = attrValue; } } } @@ -1271,7 +1309,7 @@ export default function Identity(mpInstance) { * @method setConsentState * @param {Object} consent state */ - setConsentState: function(state) { + setConsentState: function(state: ConsentState) { mpInstance._Store.setConsentState(mpid, state); mpInstance._Forwarders.initForwarders( this.getUserIdentities().userIdentities, @@ -1294,7 +1332,7 @@ export default function Identity(mpInstance) { * @param {Function} [callback] A callback function that is invoked when the user audience request completes */ // https://go.mparticle.com/work/SQDSDKS-6436 - getUserAudiences: function(callback) { + getUserAudiences: function(callback: IdentityCallback) { // user audience API is feature flagged if ( !mpInstance._Helpers.getFeatureFlag( @@ -1310,12 +1348,14 @@ export default function Identity(mpInstance) { self.audienceManager = new AudienceManager( mpInstance._Store.SDKConfig.userAudienceUrl, mpInstance._Store.devToken, - mpInstance.Logger, - mpid + mpInstance.Logger ); } - self.audienceManager.sendGetUserAudienceRequest(mpid, callback); + self.audienceManager.sendGetUserAudienceRequest( + mpid, + callback as unknown as (userAudiences: IAudienceMemberships) => void + ); }, }; }; @@ -1326,7 +1366,7 @@ export default function Identity(mpInstance) { * @class mParticle.Identity.getCurrentUser().getCart() * @deprecated */ - this.mParticleUserCart = function() { + this.mParticleUserCart = function(): mParticleUserCart { return { /** * Adds a cart product to the user cart @@ -1395,14 +1435,14 @@ export default function Identity(mpInstance) { // https://go.mparticle.com/work/SQDSDKS-6355 this.parseIdentityResponse = function( - identityResponse, - previousMPID, - callback, - identityApiData, - method, - knownIdentities, - parsingCachedResponse - ) { + identityResponse: IIdentityResponse, + previousMPID: MPID, + callback: IdentityCallback, + identityApiData: IdentityApiData, + method: IdentityAPIMethod, + knownIdentities: IKnownIdentities, + parsingCachedResponse: boolean + ): void { const prevUser = mpInstance.Identity.getUser(previousMPID); const prevUserMPID = prevUser ? prevUser.getMPID() : null; const previousUIByName = prevUser @@ -1634,11 +1674,11 @@ export default function Identity(mpInstance) { // compare what identities exist vs what is previously was for the specific user if they were in memory before. // if it's the first time the user is logging in, send a user identity change request with this.sendUserIdentityChangeEvent = function( - newUserIdentities, - method, - mpid, - prevUserIdentities - ) { + newUserIdentities: UserIdentities, + method: IdentityAPIMethod, + mpid: MPID, + prevUserIdentities: UserIdentities + ): void { if (!mpid) { // https://go.mparticle.com/work/SQDSDKS-6501 if (method !== Modify) { @@ -1649,38 +1689,40 @@ export default function Identity(mpInstance) { // https://go.mparticle.com/work/SQDSDKS-6354 const currentUserInMemory = this.IdentityAPI.getUser(mpid); - for (var identityType in newUserIdentities) { + for (const identityType in newUserIdentities) { // Verifies a change actually happened if ( - prevUserIdentities[identityType] !== - newUserIdentities[identityType] + prevUserIdentities[identityType as keyof UserIdentities] !== + newUserIdentities[identityType as keyof UserIdentities] ) { // If a new identity type was introduced when the identity changes // we need to notify the server so that the user profile is updated in // the mParticle UI. - const isNewUserIdentityType = !prevUserIdentities[identityType]; + const isNewUserIdentityType = !prevUserIdentities[ + identityType as keyof UserIdentities + ]; const userIdentityChangeEvent = self.createUserIdentityChange( - identityType, - newUserIdentities[identityType], - prevUserIdentities[identityType], + identityType as SDKIdentityTypeEnum, + newUserIdentities[identityType as keyof UserIdentities], + prevUserIdentities[identityType as keyof UserIdentities], isNewUserIdentityType, currentUserInMemory ); mpInstance._APIClient?.sendEventToServer( - userIdentityChangeEvent + userIdentityChangeEvent as unknown as SDKEvent ); } } }; this.createUserIdentityChange = function( - identityType, - newIdentity, - oldIdentity, - isIdentityTypeNewToBatch, - userInMemory + identityType: SDKIdentityTypeEnum, + newIdentity: string, + oldIdentity: string, + isIdentityTypeNewToBatch: boolean, + userInMemory: IMParticleUser ) { - var userIdentityChangeEvent; + let userIdentityChangeEvent; // https://go.mparticle.com/work/SQDSDKS-6439 userIdentityChangeEvent = mpInstance._ServerModel.createEventObject({ @@ -1698,20 +1740,20 @@ export default function Identity(mpInstance) { }, }, userInMemory, - }); + } as BaseEvent); return userIdentityChangeEvent; }; this.sendUserAttributeChangeEvent = function( - attributeKey, - newUserAttributeValue, - previousUserAttributeValue, - isNewAttribute, - deleted, - user - ) { - var userAttributeChangeEvent = self.createUserAttributeChange( + attributeKey: string, + newUserAttributeValue: string | string[] | null, + previousUserAttributeValue: string | string[] | null, + isNewAttribute: boolean, + deleted: boolean, + user: IMParticleUser + ): void { + const userAttributeChangeEvent = self.createUserAttributeChange( attributeKey, newUserAttributeValue, previousUserAttributeValue, @@ -1720,22 +1762,22 @@ export default function Identity(mpInstance) { user ); if (userAttributeChangeEvent) { - mpInstance._APIClient?.sendEventToServer(userAttributeChangeEvent); + mpInstance._APIClient?.sendEventToServer(userAttributeChangeEvent as unknown as SDKEvent); } }; this.createUserAttributeChange = function( - key, - newValue, - previousUserAttributeValue, - isNewAttribute, - deleted, - user + key: string, + newValue: string | string[] | null, + previousUserAttributeValue: string | string[] | null, + isNewAttribute: boolean, + deleted: boolean, + user: IMParticleUser ) { if (typeof previousUserAttributeValue === 'undefined') { previousUserAttributeValue = null; } - var userAttributeChangeEvent; + let userAttributeChangeEvent; if (newValue !== previousUserAttributeValue) { // https://go.mparticle.com/work/SQDSDKS-6439 userAttributeChangeEvent = mpInstance._ServerModel.createEventObject( @@ -1743,8 +1785,8 @@ export default function Identity(mpInstance) { messageType: Types.MessageType.UserAttributeChange, userAttributeChanges: { UserAttributeName: key, - New: newValue, - Old: previousUserAttributeValue, + New: newValue as string, + Old: previousUserAttributeValue as string, Deleted: deleted, IsNewAttribute: isNewAttribute, }, @@ -1755,7 +1797,7 @@ export default function Identity(mpInstance) { return userAttributeChangeEvent; }; - this.reinitForwardersOnUserChange = function(prevUser, newUser) { + this.reinitForwardersOnUserChange = function(prevUser: IMParticleUser, newUser: IMParticleUser): void { if (hasMPIDAndUserLoginChanged(prevUser, newUser)) { mpInstance._Forwarders?.initForwarders( newUser.getUserIdentities().userIdentities, @@ -1764,7 +1806,7 @@ export default function Identity(mpInstance) { } }; - this.setForwarderCallbacks = function(user, method) { + this.setForwarderCallbacks = function(user: IMParticleUser, method: IdentityAPIMethod): void { // https://go.mparticle.com/work/SQDSDKS-6036 mpInstance._Forwarders?.setForwarderUserIdentities( user.getUserIdentities().userIdentities @@ -1774,8 +1816,18 @@ export default function Identity(mpInstance) { }; } +interface IdentityApiDataWithAlias extends IdentityApiData { + /** @deprecated */ + onUserAlias?: (previousUser: IMParticleUser, newUser: IMParticleUser) => void; +} + // https://go.mparticle.com/work/SQDSDKS-6359 -function tryOnUserAlias(previousUser, newUser, identityApiData, logger) { +function tryOnUserAlias( + previousUser: IMParticleUser, + newUser: IMParticleUser, + identityApiData: IdentityApiDataWithAlias, + logger: SDKLoggerApi +): void { if ( identityApiData && identityApiData.onUserAlias && diff --git a/src/identityApiClient.ts b/src/identityApiClient.ts index c26b9bade..f33351de9 100644 --- a/src/identityApiClient.ts +++ b/src/identityApiClient.ts @@ -5,7 +5,7 @@ import { XHRUploader, IFetchPayload, } from './uploaders'; -import { CACHE_HEADER } from './identity-utils'; +import { CACHE_HEADER, IKnownIdentities } from './identity-utils'; import { obfuscateData, parseNumber, valueof } from './utils'; import { IAliasCallback, @@ -13,11 +13,11 @@ import { IdentityAPIMethod, IIdentity, IIdentityAPIRequestData, + IIdentityAPIModifyRequestData, } from './identity.interfaces'; import { IdentityApiData, MPID, - UserIdentities, } from '@mparticle/web-sdk'; import { IdentityCallback, @@ -37,13 +37,13 @@ export interface IIdentityApiClient { aliasCallback: IAliasCallback ) => Promise; sendIdentityRequest: ( - identityApiRequest: IIdentityAPIRequestData, + identityApiRequest: IIdentityAPIRequestData | IIdentityAPIModifyRequestData, method: IdentityAPIMethod, callback: IdentityCallback, originalIdentityApiData: IdentityApiData, parseIdentityResponse: IIdentity['parseIdentityResponse'], mpid: MPID, - knownIdentities: UserIdentities + knownIdentities?: IKnownIdentities ) => Promise; getUploadUrl: (method: IdentityAPIMethod, mpid: MPID) => string; getIdentityResponseFromFetch: ( @@ -186,13 +186,13 @@ export default function IdentityAPIClient( }; this.sendIdentityRequest = async function( - identityApiRequest: IIdentityAPIRequestData, + identityApiRequest: IIdentityAPIRequestData | IIdentityAPIModifyRequestData, method: IdentityAPIMethod, callback: IdentityCallback, originalIdentityApiData: IdentityApiData, parseIdentityResponse: IIdentity['parseIdentityResponse'], mpid: MPID, - knownIdentities: UserIdentities + knownIdentities?: IKnownIdentities ) { if (mpInstance._RoktManager?.isInitialized) { mpInstance._Store.identifyRequestCount = (mpInstance._Store.identifyRequestCount || 0) + 1; diff --git a/src/mp-instance.ts b/src/mp-instance.ts index 618b19ab3..e42e0b348 100644 --- a/src/mp-instance.ts +++ b/src/mp-instance.ts @@ -35,7 +35,7 @@ import Identity from './identity'; import Consent, { IConsent } from './consent'; import KitBlocker from './kitBlocking'; import ConfigAPIClient, { IKitConfigs } from './configAPIClient'; -import IdentityAPIClient from './identityApiClient'; +import IdentityAPIClient, { IIdentityApiClient } from './identityApiClient'; import { isFunction, parseConfig, valueof, generateDeprecationMessage, extend } from './utils'; import { DisabledVault, LocalStorageVault } from './vault'; import { removeExpiredIdentityCacheDates, hasExplicitIdentifier } from './identity-utils'; @@ -82,7 +82,7 @@ export interface IMParticleWebSDKInstance extends MParticleWebSDK { _ForwardingStatsUploader: ForwardingStatsUploader; _Helpers: SDKHelpersApi; _Identity: IIdentity; - _IdentityAPIClient: typeof IdentityAPIClient; + _IdentityAPIClient: IIdentityApiClient; _IntegrationCapture: IntegrationCapture; _NativeSdkHelpers: INativeSdkHelpers; _Persistence: IPersistence; diff --git a/src/persistence.interfaces.ts b/src/persistence.interfaces.ts index 639de8a3e..4cc7c9bc1 100644 --- a/src/persistence.interfaces.ts +++ b/src/persistence.interfaces.ts @@ -118,12 +118,12 @@ export interface IPersistence { getCookieDomain(): string; getDomain(doc: string, locationHostname: string): string; saveUserCookieSyncDatesToPersistence(mpid: MPID, csd: CookieSyncDates): void; - savePersistence(persistance: IPersistenceMinified): void; + savePersistence(persistance: IPersistenceMinified, mpid?: MPID): void; getPersistence(): IPersistenceMinified; - getFirstSeenTime(mpid: MPID): string | null; - setFirstSeenTime(mpid: MPID, time: number): void; + getFirstSeenTime(mpid: MPID): number | null; + setFirstSeenTime(mpid: MPID, time?: number): void; getLastSeenTime(mpid: MPID): number | null; - setLastSeenTime(mpid: MPID, time: number): void; + setLastSeenTime(mpid: MPID, time?: number): void; getDeviceId(): string; setDeviceId(guid: string): void; resetPersistence(): void; diff --git a/src/store.ts b/src/store.ts index 4bd1617e1..2b12400aa 100644 --- a/src/store.ts +++ b/src/store.ts @@ -156,6 +156,7 @@ export interface IFeatureFlags { export interface IStore { isEnabled: boolean; isInitialized: boolean; + mpid: MPID; // Session Attributes are persistent attributes that are tied to the current session and // are uploaded then cleared when the session ends. diff --git a/test/jest/identity.spec.ts b/test/jest/identity.spec.ts index 22c799e33..1ca1aa9fa 100644 --- a/test/jest/identity.spec.ts +++ b/test/jest/identity.spec.ts @@ -17,6 +17,7 @@ import { import { hasExplicitIdentifier } from '../../src/identity-utils'; import { IStore } from '../../src/store'; import { MessageType } from '../../src/types'; +import { Context } from '@mparticle/event-models'; describe('Identity', () => { describe('#types', () => { @@ -122,10 +123,10 @@ describe('Identity', () => { sdk_vendor: 'mparticle', sdk_version: '1.0.0', }, - context: 'test-context', + context: 'test-context' as unknown as Context, environment: 'development', request_id: '123', - request_timestamp_unixtime_ms: Date.now(), + request_timestamp_ms: Date.now(), previous_mpid: null, known_identities: { email: 'user@mparticle.com', @@ -142,10 +143,10 @@ describe('Identity', () => { sdk_vendor: 'mparticle', sdk_version: '1.0.0', }, - context: 'test-context', + context: 'test-context' as unknown as Context, environment: 'development', request_id: '123', - request_timestamp_unixtime_ms: Date.now(), + request_timestamp_ms: Date.now(), identity_changes: [ { identity_type: SDKIdentityTypeEnum.google, diff --git a/test/src/tests-identity.ts b/test/src/tests-identity.ts index e17ecf5cd..661fd31be 100644 --- a/test/src/tests-identity.ts +++ b/test/src/tests-identity.ts @@ -15,6 +15,7 @@ import { IdentityApiData, UserIdentities, } from '@mparticle/web-sdk'; +import { Context } from '@mparticle/event-models'; import { IdentityCache } from '../../src/identity-utils'; import { IAliasRequest, @@ -1599,7 +1600,7 @@ describe('identity', function() { 'test-platform', 'test-sdk-vendor', 'test-sdk-version', - 'test-context' + 'test-context' as unknown as Context ); expect(identityRequest.identity_changes[0]).to.have.all.keys([ @@ -1650,7 +1651,7 @@ describe('identity', function() { 'test-platform', 'test-sdk-vendor', 'test-sdk-version', - 'test-context' + 'test-context' as unknown as Context ); expect(identityRequest.identity_changes[0]).to.have.all.keys([ diff --git a/test/src/tests-identityApiClient.ts b/test/src/tests-identityApiClient.ts index 0f3142af2..1b3f296d6 100644 --- a/test/src/tests-identityApiClient.ts +++ b/test/src/tests-identityApiClient.ts @@ -6,6 +6,7 @@ import { IAliasRequest, IIdentityAPIRequestData, } from '../../src/identity.interfaces'; +import { Context } from '@mparticle/event-models'; import Constants, { HTTP_ACCEPTED, HTTP_BAD_REQUEST, @@ -42,10 +43,10 @@ describe('Identity Api Client', () => { sdk_vendor: 'mparticle', sdk_version: '1.0.0', }, - context: 'test-context', + context: 'test-context' as unknown as Context, environment: 'development', request_id: '123', - request_timestamp_unixtime_ms: Date.now(), + request_timestamp_ms: Date.now(), previous_mpid: null, known_identities: { email: 'user@mparticle.com',