Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

userId: fix unhandled rejection from refreshUserIds #12246

Merged
merged 2 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 22 additions & 18 deletions modules/userId/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,7 @@ function getUserIdsAsEidBySource(sourceName) {
* Sample use case is exposing this function to ESP
*/
function getEncryptedEidsForSource(source, encrypt, customFunction) {
return initIdSystem().then(() => {
return retryOnCancel().then(() => {
let eidsSignals = {};

if (isFn(customFunction)) {
Expand Down Expand Up @@ -782,6 +782,23 @@ function registerSignalSources() {
}
}

function retryOnCancel(initParams) {
return initIdSystem(initParams).then(
() => getUserIds(),
(e) => {
if (e === INIT_CANCELED) {
// there's a pending refresh - because GreedyPromise runs this synchronously, we are now in the middle
// of canceling the previous init, before the refresh logic has had a chance to run.
// Use a "normal" Promise to clear the stack and let it complete (or this will just recurse infinitely)
return Promise.resolve().then(getUserIdsAsync)
} else {
logError('Error initializing userId', e)
return GreedyPromise.reject(e)
}
}
);
}

/**
* Force (re)initialization of ID submodules.
*
Expand All @@ -793,12 +810,12 @@ function registerSignalSources() {
* @param callback? called when the refresh is complete
*/
function refreshUserIds({submoduleNames} = {}, callback) {
return initIdSystem({refresh: true, submoduleNames})
.then(() => {
return retryOnCancel({refresh: true, submoduleNames})
.then((userIds) => {
if (callback && isFn(callback)) {
callback();
}
return getUserIds();
return userIds;
});
}

Expand All @@ -814,20 +831,7 @@ function refreshUserIds({submoduleNames} = {}, callback) {
*/

function getUserIdsAsync() {
return initIdSystem().then(
() => getUserIds(),
(e) => {
if (e === INIT_CANCELED) {
// there's a pending refresh - because GreedyPromise runs this synchronously, we are now in the middle
// of canceling the previous init, before the refresh logic has had a chance to run.
// Use a "normal" Promise to clear the stack and let it complete (or this will just recurse infinitely)
return Promise.resolve().then(getUserIdsAsync)
} else {
logError('Error initializing userId', e)
return GreedyPromise.reject(e)
}
}
);
return retryOnCancel();
}

export function getConsentHash() {
Expand Down
24 changes: 13 additions & 11 deletions test/spec/modules/userId_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1102,18 +1102,20 @@ describe('User ID', function () {
});
});

it('should still resolve promises returned by getUserIdsAsync', () => {
startInit();
let result = null;
getGlobal().getUserIdsAsync().then((val) => { result = val; });
return clearStack().then(() => {
expect(result).to.equal(null); // auction has not ended, callback should not have been called
mockIdCallback.callsFake((cb) => cb(MOCK_ID));
return getGlobal().refreshUserIds().then(clearStack);
}).then(() => {
expect(result).to.deep.equal(getGlobal().getUserIds()) // auction still not over, but refresh was explicitly forced
['refreshUserIds', 'getUserIdsAsync'].forEach(method => {
it(`should still resolve promises returned by ${method}`, () => {
startInit();
let result = null;
getGlobal()[method]().then((val) => { result = val; });
return clearStack().then(() => {
expect(result).to.equal(null); // auction has not ended, callback should not have been called
mockIdCallback.callsFake((cb) => cb(MOCK_ID));
return getGlobal().refreshUserIds().then(clearStack);
}).then(() => {
expect(result).to.deep.equal(getGlobal().getUserIds()) // auction still not over, but refresh was explicitly forced
});
});
});
})

it('should not stop auctions', (done) => {
// simulate an infinite `auctionDelay`; refreshing should still allow the auction to continue
Expand Down