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

feat: set server side cookies #1649

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
6a681c1
feat: added new load option to set cookies from serverside
MoumitaM Mar 18, 2024
ec4e81e
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM Mar 18, 2024
7b90beb
chore: address review comments
MoumitaM Mar 19, 2024
91789ef
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM Mar 21, 2024
ffa79ee
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM Mar 27, 2024
a59c8d0
chore: modify configuration for getAsyncData fn
MoumitaM Mar 27, 2024
e4717c2
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM Apr 18, 2024
41cd5af
chore: updated cookie setter provider
MoumitaM Apr 22, 2024
0c65220
Merge branch 'feature/sdk-1301-create-a-cookie-setter-provider-for-se…
MoumitaM Apr 22, 2024
0f49281
chore: address review comments
MoumitaM May 6, 2024
5d277b6
chore: resolve merge conflict
MoumitaM May 6, 2024
e4ce0a0
chore: address review comment
MoumitaM May 7, 2024
d2fece5
chore: fix error handling to remove cookies if value is empty
MoumitaM May 7, 2024
a1eaf29
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM May 7, 2024
b1f9bf3
chore: address review comment
MoumitaM May 9, 2024
14f5c23
chore: update cb fn
MoumitaM May 10, 2024
4109f61
chore: add new type for callback
MoumitaM May 10, 2024
54350fb
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM May 10, 2024
e653c05
chore: update test case
MoumitaM May 12, 2024
a90f320
chore: update test case
MoumitaM May 12, 2024
569a8ff
chore: removed unnecessary code
MoumitaM May 12, 2024
e885ef3
chore: more test cases added
MoumitaM May 16, 2024
07cf076
chore: removed code for client side cookie removal and update test cases
MoumitaM May 17, 2024
595c160
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM May 17, 2024
29c848b
chore: use current page url as the base url for cookie request
MoumitaM May 17, 2024
5eaadf1
chore: removed unnecessary only statement
MoumitaM May 18, 2024
2987039
chore: review comment address
MoumitaM May 20, 2024
40523f5
chore: review comment address
MoumitaM May 20, 2024
6797508
chore: use nullish coalescing operator
MoumitaM May 20, 2024
e64fe44
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM May 20, 2024
8f8e48f
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM May 20, 2024
1d1107b
chore: enhance test case
MoumitaM May 20, 2024
598599e
Merge branch 'feature/sdk-1301-create-a-cookie-setter-provider-for-se…
MoumitaM May 20, 2024
b7c464c
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM May 20, 2024
3492300
chore: remove leading slash from provided endpoint
MoumitaM May 21, 2024
3229627
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM May 21, 2024
022d23b
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM May 21, 2024
b0e1484
Merge branch 'develop' into feature/sdk-1301-create-a-cookie-setter-p…
MoumitaM May 21, 2024
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
4 changes: 2 additions & 2 deletions packages/analytics-js-common/src/types/LoadOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ export type LoadOptions = {
consentManagement?: ConsentManagementOptions;
sameDomainCookiesOnly?: boolean;
externalAnonymousIdCookieName?: string;
useServerSideCookie?: boolean;
cookieServerUrl?: string;
useServerSideCookies?: boolean;
dataServerUrl?: string;
};

export type ConsentOptions = {
Expand Down
1 change: 1 addition & 0 deletions packages/analytics-js-common/src/types/Source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export type Source = {
id: string;
config?: SourceConfig;
dataplanes?: Record<ResidencyServerRegion, RegionDetails[]>;
workspaceId: string;
};
2 changes: 1 addition & 1 deletion packages/analytics-js/.size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ module.exports = [
{
name: 'Core - CDN',
path: 'dist/cdn/modern/iife/rsa.min.js',
limit: '23 KiB',
limit: '23.5 KiB',
},
];
2 changes: 1 addition & 1 deletion packages/analytics-js/__fixtures__/msw.handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ const handlers = [
},
});
}),
http.post(`${dummyDataplaneHost}/setCookie`, () => {
http.post(`${dummyDataplaneHost}/rsaRequest`, () => {
return new HttpResponse(null, {
status: 200,
headers: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ describe('ConfigManager', () => {
const expectedSourceState = {
id: dummySourceConfigResponse.source.id,
config: dummySourceConfigResponse.source.config,
workspaceId: dummySourceConfigResponse.source.workspaceId,
};
state.lifecycle.dataPlaneUrl.value = sampleDataPlaneUrl;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1374,12 +1374,12 @@ describe('User session manager', () => {
});

describe('getExternalAnonymousIdByCookieName', () => {
it('Should return null if the cookie value does not exists', () => {
it('should return null if the cookie value does not exists', () => {
const externalAnonymousId =
userSessionManager.getExternalAnonymousIdByCookieName('anonId_cookie');
expect(externalAnonymousId).toEqual(null);
});
it('Should return the cookie value if exists', () => {
it('should return the cookie value if exists', () => {
document.cookie = 'anonId_cookie=sampleAnonymousId12345';
const externalAnonymousId =
userSessionManager.getExternalAnonymousIdByCookieName('anonId_cookie');
Expand All @@ -1388,12 +1388,21 @@ describe('User session manager', () => {
});

describe('syncValueToStorage', () => {
it('Should call setServerSideCookie method in case useServerSideCookie load option is set to true', () => {
state.loadOptions.value.useServerSideCookie = true;
it('should not call setServerSideCookie method in case useServerSideCookies load option is not set', () => {
state.storage.entries.value = entriesWithOnlyCookieStorage;
const spy = jest.spyOn(userSessionManager, 'setServerSideCookie');
userSessionManager.syncValueToStorage('anonymousId', 'dummy_anonymousId');
expect(spy).toHaveBeenCalledWith('rl_anonymous_id', '"dummy_anonymousId"');
expect(spy).not.toHaveBeenCalled();
});
it('should call setServerSideCookie method in case useServerSideCookies load option is set to true', () => {
state.loadOptions.value.useServerSideCookies = true;
state.storage.entries.value = entriesWithOnlyCookieStorage;
const spy = jest.spyOn(userSessionManager, 'setServerSideCookie');
userSessionManager.syncValueToStorage('anonymousId', 'dummy_anonymousId');
expect(spy).toHaveBeenCalledWith(
[{ name: 'rl_anonymous_id', value: 'dummy_anonymousId' }],
expect.any(Object),
);
});
});

Expand All @@ -1405,61 +1414,95 @@ describe('User session manager', () => {
afterAll(() => {
server.close();
});
it('Should make external request to exposed endpoint', () => {
const mockCookieStore = {
encrypt: jest.fn(val => `encrypted_${JSON.parse(val)}`),
set: jest.fn(),
};
it('should make external request to exposed endpoint', () => {
state.lifecycle.activeDataplaneUrl.value = 'https://dummy.dataplane.host.com';
state.source.value = { workspaceId: 'sample_workspaceId' };
state.storage.cookie.value = {
maxage: 10 * 60 * 1000, // 10 min
path: '/',
domain: 'example.com',
samesite: 'Lax',
};
const spy = jest.spyOn(defaultHttpClient, 'getAsyncData');
userSessionManager.setServerSideCookie('key', 'sample_cookie_value_1234');
userSessionManager.setServerSideCookie(
[{ name: 'key', value: 'sample_cookie_value_1234' }],
mockCookieStore,
);
expect(spy).toHaveBeenCalledWith({
url: `https://dummy.dataplane.host.com/setCookie`,
url: `https://dummy.dataplane.host.com/rsaRequest`,
options: {
method: 'POST',
data: JSON.stringify({
key: 'key',
value: 'sample_cookie_value_1234',
options: {
maxage: 10 * 60 * 1000,
path: '/',
domain: 'example.com',
samesite: 'Lax',
reqType: 'setCookies',
workspaceId: 'sample_workspaceId',
data: {
options: {
maxAge: 10 * 60 * 1000,
path: '/',
domain: 'example.com',
sameSite: 'Lax',
secure: undefined,
},
cookies: [
{
name: 'key',
value: 'encrypted_sample_cookie_value_1234',
},
],
},
}),
sendRawData: true,
},
isRawResponse: true,
callback: expect.any(Function),
});
});
it('Should use provided server url to make external request for setting cookie', () => {
it('should use provided server url to make external request for setting cookie', () => {
state.lifecycle.activeDataplaneUrl.value = 'https://dummy.dataplane.host.com';
state.loadOptions.value.cookieServerUrl = 'https://example.com';
state.source.value = { workspaceId: 'sample_workspaceId' };
state.loadOptions.value.dataServerUrl = 'https://example.com';
state.storage.cookie.value = {
maxage: 10 * 60 * 1000, // 10 min
path: '/',
domain: 'example.com',
samesite: 'Lax',
};
const spy = jest.spyOn(defaultHttpClient, 'getAsyncData');
userSessionManager.setServerSideCookie('key', 'sample_cookie_value_1234');
userSessionManager.setServerSideCookie(
[{ name: 'key', value: 'sample_cookie_value_1234' }],
mockCookieStore,
);
expect(spy).toHaveBeenCalledWith({
url: `https://example.com/setCookie`,
url: `https://example.com/rsaRequest`,
options: {
method: 'POST',
data: JSON.stringify({
key: 'key',
value: 'sample_cookie_value_1234',
options: {
maxage: 10 * 60 * 1000,
path: '/',
domain: 'example.com',
samesite: 'Lax',
reqType: 'setCookies',
workspaceId: 'sample_workspaceId',
data: {
options: {
maxAge: 10 * 60 * 1000,
path: '/',
domain: 'example.com',
sameSite: 'Lax',
secure: undefined,
},
cookies: [
{
name: 'key',
value: 'encrypted_sample_cookie_value_1234',
},
],
},
}),
sendRawData: true,
},
isRawResponse: true,
callback: expect.any(Function),
});
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/analytics-js/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
configUrl: '__CONFIG_SERVER_HOST__',
destSDKBaseURL:
'__DEST_SDK_BASE_URL__' + window.rudderAnalyticsBuildType + '/js-integrations',
// pluginsSDKBaseURL: '__PLUGINS_BASE_URL__' + window.rudderAnalyticsBuildType + '/plugins',
pluginsSDKBaseURL: '__PLUGINS_BASE_URL__' + window.rudderAnalyticsBuildType + '/plugins',
// useServerSideCookie:true,
// queueOptions: {
// batch: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
import type { Destination } from '@rudderstack/analytics-js-common/types/Destination';
import type { ILogger } from '@rudderstack/analytics-js-common/types/Logger';
import { CONFIG_MANAGER } from '@rudderstack/analytics-js-common/constants/loggerContexts';
import { isValidSourceConfig, validateLoadArgs } from './util/validate';
import {
isValidSourceConfig,
validateLoadArgs,
validateAndReturnCookieServerUrl,
} from './util/validate';
import {
DATA_PLANE_URL_ERROR,
SOURCE_CONFIG_FETCH_ERROR,
Expand Down Expand Up @@ -91,6 +95,13 @@
lockIntegrationsVersion,
this.logger,
);
if (state.loadOptions.value.dataServerUrl) {
state.loadOptions.value.dataServerUrl = validateAndReturnCookieServerUrl(

Check warning on line 99 in packages/analytics-js/src/components/configManager/ConfigManager.ts

View check run for this annotation

Codecov / codecov/patch

packages/analytics-js/src/components/configManager/ConfigManager.ts#L99

Added line #L99 was not covered by tests
state.loadOptions.value.useServerSideCookies,
state.loadOptions.value.dataServerUrl,
this.logger,
);
}
});

this.getConfig();
Expand Down Expand Up @@ -160,6 +171,7 @@
state.source.value = {
config: res.source.config,
id: res.source.id,
workspaceId: res.source.workspaceId,
};

// set device mode destination related information in state
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
SUPPORTED_STORAGE_TYPES,
type StorageType,
} from '@rudderstack/analytics-js-common/types/Storage';
import type { ILogger } from '@rudderstack/analytics-js-common/types/Logger';
import {
WRITE_KEY_VALIDATION_ERROR,
DATA_PLANE_URL_VALIDATION_ERROR,
COOKIE_SERVER_URL_INVALID_ERROR,
} from '../../../constants/logMessages';
import { isValidUrl } from '../../utilities/url';

Expand Down Expand Up @@ -37,10 +39,25 @@
const isValidStorageType = (storageType?: StorageType): boolean =>
typeof storageType === 'string' && SUPPORTED_STORAGE_TYPES.includes(storageType);

const validateAndReturnCookieServerUrl = (
useServerSideCookies?: boolean,
dataServerUrl?: string,
logger?: ILogger,
) => {
if (useServerSideCookies && dataServerUrl) {
if (isValidUrl(dataServerUrl)) {
return dataServerUrl;

Check warning on line 49 in packages/analytics-js/src/components/configManager/util/validate.ts

View check run for this annotation

Codecov / codecov/patch

packages/analytics-js/src/components/configManager/util/validate.ts#L49

Added line #L49 was not covered by tests
}
logger?.error(COOKIE_SERVER_URL_INVALID_ERROR('dataServerUrl'));
}
return 'invalid';

Check warning on line 53 in packages/analytics-js/src/components/configManager/util/validate.ts

View check run for this annotation

Codecov / codecov/patch

packages/analytics-js/src/components/configManager/util/validate.ts#L53

Added line #L53 was not covered by tests
};

export {
validateLoadArgs,
isValidSourceConfig,
isValidStorageType,
validateWriteKey,
validateDataPlaneUrl,
validateAndReturnCookieServerUrl,
};
Loading
Loading