Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cf25bed
feat: Add Identity.searchAdvertiser for IDSync Search
rmi22186 Apr 28, 2026
5382278
feat: Switch Identity.searchAdvertiser to Basic auth (apiKey + secret)
rmi22186 Apr 28, 2026
7653323
Revert "feat: Switch Identity.searchAdvertiser to Basic auth (apiKey …
rmi22186 Apr 28, 2026
b366bbd
feat: Report searchAdvertiser network errors via ErrorReportingDispat…
rmi22186 Apr 28, 2026
bb58f23
fix: Address cursor-bot review on searchAdvertiser
rmi22186 Apr 28, 2026
d6cefa0
fix: Deliver correct callback shape on searchAdvertiser opt-out
rmi22186 Apr 29, 2026
4b4c12f
refactor: Use const for searchAdvertiser locals in identity.js
rmi22186 Apr 29, 2026
7095652
fix: Wrap searchAdvertiser request setup in the existing try/catch
rmi22186 Apr 29, 2026
e496209
refactor: Tighten searchAdvertiser surface; cache-bust /v1/search URL
rmi22186 Apr 29, 2026
3f9d142
fix: Address ultrareview findings on searchAdvertiser
rmi22186 Apr 29, 2026
0d7312d
chore: Tighten searchAdvertiser docs and drop redundant test
rmi22186 Apr 29, 2026
2ebcbb4
refactor: Rename searchAdvertiser to searchWorkspace
rmi22186 Apr 29, 2026
fdfc04a
update query parameter
rmi22186 Apr 30, 2026
3a17b19
refactor: Rename Identity.searchWorkspace to Identity.search
rmi22186 Apr 30, 2026
e3e190f
refactor: Extract executeSearchRequest into identity-utils.ts
rmi22186 Apr 30, 2026
d73b680
refactor: Prefix Search type names with Identity
rmi22186 Apr 30, 2026
d7501b3
refactor: Address PR #1255 review nits on Identity.search
rmi22186 Apr 30, 2026
0feca7a
refactor: Address PR #1255 review nits + snippet stub for Identity.se…
rmi22186 Apr 30, 2026
4246883
fix: Use destructured _ErrorReportingDispatcher in executeSearchRequest
rmi22186 Apr 30, 2026
5f12367
test: Add Identity.search coverage for snippet + stub
rmi22186 Apr 30, 2026
b605284
refactor: Use Date.now() in buildIdentitySearchEnvelope
rmi22186 Apr 30, 2026
723e7e8
Apply suggestion from @alexs-mparticle
rmi22186 Apr 30, 2026
e47d52a
refactor: Propagate Environment type and drop redundant Response casts
rmi22186 Apr 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion snippet.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
'stopTrackingLocation',
];
var ecommerceMethods = ['setCurrencyCode', 'logCheckout'];
var identityMethods = ['identify', 'login', 'logout', 'modify'];
var identityMethods = ['identify', 'login', 'logout', 'modify', 'search'];
var roktMethods = [
'selectPlacements',
'hashAttributes',
Expand Down
2 changes: 1 addition & 1 deletion snippet.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion snippet.rokt.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
'stopTrackingLocation',
];
var ecommerceMethods = ['setCurrencyCode', 'logCheckout'];
var identityMethods = ['identify', 'login', 'logout', 'modify'];
var identityMethods = ['identify', 'login', 'logout', 'modify', 'search'];
var roktMethods = [
'selectPlacements',
'hashAttributes',
Expand Down
2 changes: 1 addition & 1 deletion snippet.rokt.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

83 changes: 82 additions & 1 deletion src/identity-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Constants, { ONE_DAY_IN_SECONDS, MILLIS_IN_ONE_SEC } from './constants';
import { Dictionary, parseNumber, isObject, generateHash, isEmpty } from './utils';
import { Dictionary, Environment, parseNumber, isObject, generateHash, generateUniqueId, isEmpty, isFunction } from './utils';
import { BaseVault } from './vault';
import Types from './types';
import {
Expand All @@ -14,8 +14,16 @@ import {
IMParticleUser,
} from './identity-user-interfaces';
import { IStore } from './store';
import type { IMParticleWebSDKInstance } from './mp-instance';
import {
IIdentitySearchKnownIdentities,
IIdentitySearchRequestBody,
IdentitySearchCallback,
sendSearchRequest,
} from './identity/search';

const { Identify, Modify, Login, Logout } = Constants.IdentityMethods;
const { HTTPCodes, Messages } = Constants;
export const CACHE_HEADER = 'x-mp-max-age' as const;

export type IdentityCache = BaseVault<Dictionary<ICachedIdentityCall>>;
Expand Down Expand Up @@ -321,3 +329,76 @@ export const hasExplicitIdentifier = (store: IStore | undefined | null): boolean
}
return !!store?.SDKConfig?.deviceId;
};

/**
* Builds the /v1/identify-style envelope (client_sdk, environment,
* request_id, request_timestamp_ms) used to correlate IDSync requests
* across endpoints. `known_identities` is omitted so the caller can
* fold in the search-specific identifiers alongside the envelope.
*/
export const buildIdentitySearchEnvelope = (
environment: Environment,
): Omit<IIdentitySearchRequestBody, 'known_identities'> => ({
client_sdk: {
platform: Constants.platform,
sdk_vendor: Constants.sdkVendor,
sdk_version: Constants.sdkVersion,
},
environment,
request_id: generateUniqueId(),
request_timestamp_ms: Date.now(),
});

/**
* Wires the SDK instance into `sendSearchRequest`: gates on `canLog`,
* builds the `/v1/search` URL and request envelope, and dispatches.
* Lives here so the SDK glue (URL building, opt-out gate, dispatcher
* plumbing) is type-checked instead of being expressed in plain JS.
*/
export const executeSearchRequest = (
mpInstance: IMParticleWebSDKInstance,
workspaceApiKey: string,
knownIdentities: IIdentitySearchKnownIdentities,
callback: IdentitySearchCallback,
): void => {
const { _Helpers, _Store, Logger, _ErrorReportingDispatcher } = mpInstance;
Comment thread
cursor[bot] marked this conversation as resolved.
const { identityUrl, isDevelopmentMode } = _Store.SDKConfig;

if (!_Helpers.canLog()) {
Logger.verbose(Messages.InformationMessages.AbandonLogEvent);
if (isFunction(callback)) {
try {
callback({
httpCode: HTTPCodes.loggingDisabledOrMissingAPIKey,
});
} catch (e) {
Logger.error(
'Error invoking search callback: ' +
((e as Error)?.message || String(e)),
);
}
}
return;
}

// The Search endpoint is colocated with /v1/identify under
// identityUrl, so we reuse the same service URL builder. We do
// NOT append the apiKey to the URL — auth is done via x-mp-key.
const serviceUrl: string = _Helpers.createServiceUrl(identityUrl);
const searchUrl: string = serviceUrl + 'search?cb=1';

const environment: Environment = isDevelopmentMode
? 'development'
: 'production';

sendSearchRequest(
knownIdentities,
workspaceApiKey,
() => buildIdentitySearchEnvelope(environment),
searchUrl,
callback,
Logger,
undefined,
_ErrorReportingDispatcher,
);
};
27 changes: 27 additions & 0 deletions src/identity.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import {
mParticleUserCart,
IIdentityResponse,
} from './identity-user-interfaces';
import {
IIdentitySearchKnownIdentities,
IdentitySearchCallback,
} from './identity/search';
const { platform, sdkVendor, sdkVersion, HTTPCodes } = Constants;

export type IdentityPreProcessResult = {
Expand Down Expand Up @@ -156,8 +160,31 @@ export interface SDKIdentityApi {
destinationUser: IMParticleUser,
scope?: AliasRequestScope
): IAliasRequest;
/**
* Sends a request to mParticle's IDSync `/v1/search` endpoint to look up
* a workspace identity without affecting the current user. The callback
* receives `httpCode` (always) and an optional `body` containing the
* parsed JSON response. Consumers should gate behaviour on
* `httpCode === 200`.
*
* `workspaceApiKey` is a workspace-specific API key supplied by the
* caller (from a kit's settings). It is sent as the `x-mp-key` header.
* The SDK's own workspace token is intentionally not used.
*/
search?(
workspaceApiKey: string,
knownIdentities: IIdentitySearchKnownIdentities,
callback: IdentitySearchCallback
): void;
}

export type {
IIdentitySearchKnownIdentities,
IIdentitySearchResult,
IIdentitySearchResponseBody,
IdentitySearchCallback,
} from './identity/search';

export interface IIdentity {
audienceManager: AudienceManager;
idCache: BaseVault<Dictionary<ICachedIdentityCall>>;
Expand Down
27 changes: 27 additions & 0 deletions src/identity.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import Constants, { HTTP_OK } from './constants';

Check failure on line 1 in src/identity.js

View workflow job for this annotation

GitHub Actions / Core Tests / Core SDK Tests

Declaration emit for this file requires using private name 'mParticleUserCart'. An explicit type annotation may unblock declaration emit.

Check failure on line 1 in src/identity.js

View workflow job for this annotation

GitHub Actions / Core Tests / Core SDK Tests

Declaration emit for this file requires using private name 'mParticleUser'. An explicit type annotation may unblock declaration emit.
import Types, { IdentityType } from './types';
import {
cacheOrClearIdCache,
createKnownIdentities,
executeSearchRequest,
tryCacheIdentity,
} from './identity-utils';
import AudienceManager from './audienceManager';
Expand Down Expand Up @@ -730,6 +731,32 @@
}
},

/**
* Search the IDSync Workspace endpoint for a known identity.
*
* POSTs to mParticle's `/v1/search` endpoint and invokes `callback`
* with `{ httpCode, body? }`.
*
* The `workspaceApiKey` is a workspace-specific API key supplied by
* the caller (passed in from a kit's settings). It is intentionally
* NOT read from the SDK's own workspace token, so that workspace
* searches can be authorised independently of the host SDK's
* workspace.
*
* @method search
* @param {String} workspaceApiKey Workspace API key (sent as x-mp-key).
* @param {Object} knownIdentities `{ email: string }`
* @param {Function} callback Invoked with the `IIdentitySearchResult`.
*/
search: function(workspaceApiKey, knownIdentities, callback) {
executeSearchRequest(
mpInstance,
workspaceApiKey,
knownIdentities,
callback
);
},
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.

/**
Create a default AliasRequest for 2 MParticleUsers. This will construct the request
using the sourceUser's firstSeenTime as the startTime, and its lastSeenTime as the endTime.
Expand Down
Loading
Loading