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

fix: stringifying session ID for airship #3896

Merged
merged 10 commits into from
Nov 21, 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
5 changes: 4 additions & 1 deletion src/v0/destinations/airship/data/airshipTrackConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
{
"destKey": "session_id",
"sourceKeys": ["properties.sessionId", "context.sessionId"],
"required": false
"required": false,
"metadata": {
"type": "toString"
}
},
{
"destKey": "transaction",
Expand Down
12 changes: 11 additions & 1 deletion src/v0/destinations/airship/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,20 @@ const {
extractCustomFields,
isEmptyObject,
simpleProcessRouterDest,
convertToUuid,
} = require('../../util');
const { JSON_MIME_TYPE } = require('../../util/constant');
const { transformSessionId } = require('./utils');

const DEFAULT_ACCEPT_HEADER = 'application/vnd.urbanairship+json; version=3';

const transformSessionId = (rawSessionId) => {
try {
return convertToUuid(rawSessionId);
} catch (error) {
throw new InstrumentationError(`Failed to transform session ID: ${error.message}`);
}
};

const identifyResponseBuilder = (message, { Config }) => {
const tagPayload = constructPayload(message, identifyMapping);
const { apiKey, dataCenter } = Config;
Expand Down Expand Up @@ -129,6 +137,8 @@ const trackResponseBuilder = async (message, { Config }) => {

name = name.toLowerCase();
const payload = constructPayload(message, trackMapping);

// ref : https://docs.airship.com/api/ua/#operation-api-custom-events-post
if (isDefinedAndNotNullAndNotEmpty(payload.session_id)) {
payload.session_id = transformSessionId(payload.session_id);
}
Expand Down
12 changes: 0 additions & 12 deletions src/v0/destinations/airship/utils.js

This file was deleted.

25 changes: 25 additions & 0 deletions src/v0/util/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
const moment = require('moment-timezone');
const sha256 = require('sha256');
const crypto = require('crypto');
const { v5 } = require('uuid');
const {
InstrumentationError,
BaseError,
Expand Down Expand Up @@ -2330,6 +2331,29 @@

const isEventSentByVDMV2Flow = (event) =>
event?.connection?.config?.destination?.schemaVersion === VDM_V2_SCHEMA_VERSION;

const convertToUuid = (input) => {
const NAMESPACE = v5.DNS;

Check warning on line 2336 in src/v0/util/index.js

View check run for this annotation

Codecov / codecov/patch

src/v0/util/index.js#L2336

Added line #L2336 was not covered by tests

if (!isDefinedAndNotNull(input)) {
throw new InstrumentationError('Input is undefined or null.');

Check warning on line 2339 in src/v0/util/index.js

View check run for this annotation

Codecov / codecov/patch

src/v0/util/index.js#L2339

Added line #L2339 was not covered by tests
}

try {

Check warning on line 2342 in src/v0/util/index.js

View check run for this annotation

Codecov / codecov/patch

src/v0/util/index.js#L2342

Added line #L2342 was not covered by tests
krishna2020 marked this conversation as resolved.
Show resolved Hide resolved
// Stringify and trim the input
const trimmedInput = String(input).trim();

Check warning on line 2344 in src/v0/util/index.js

View check run for this annotation

Codecov / codecov/patch

src/v0/util/index.js#L2344

Added line #L2344 was not covered by tests

// Check for empty input after trimming
if (!trimmedInput) {
throw new InstrumentationError('Input is empty or invalid.');

Check warning on line 2348 in src/v0/util/index.js

View check run for this annotation

Codecov / codecov/patch

src/v0/util/index.js#L2348

Added line #L2348 was not covered by tests
}
// Generate and return UUID
return v5(trimmedInput, NAMESPACE);

Check warning on line 2351 in src/v0/util/index.js

View check run for this annotation

Codecov / codecov/patch

src/v0/util/index.js#L2351

Added line #L2351 was not covered by tests
} catch (error) {
const errorMessage = `Failed to transform input to uuid: ${error.message}`;
throw new InstrumentationError(errorMessage);

Check warning on line 2354 in src/v0/util/index.js

View check run for this annotation

Codecov / codecov/patch

src/v0/util/index.js#L2353-L2354

Added lines #L2353 - L2354 were not covered by tests
}
};
// ========================================================================
// EXPORTS
// ========================================================================
Expand Down Expand Up @@ -2456,4 +2480,5 @@
getRelativePathFromURL,
removeEmptyKey,
isAxiosError,
convertToUuid,
};
64 changes: 64 additions & 0 deletions src/v0/util/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const { InstrumentationError } = require('@rudderstack/integrations-lib');
const utilities = require('.');
const { getFuncTestData } = require('../../../test/testHelper');
const { FilteredEventsError } = require('./errorTypes');
const { v5 } = require('uuid');
const {
hasCircularReference,
flattenJson,
Expand All @@ -11,6 +12,7 @@ const {
groupRouterTransformEvents,
isAxiosError,
removeHyphens,
convertToUuid,
} = require('./index');
const exp = require('constants');

Expand Down Expand Up @@ -985,3 +987,65 @@ describe('removeHyphens', () => {
});
});
});

describe('convertToUuid', () => {
const NAMESPACE = v5.DNS;

test('should generate UUID for valid string input', () => {
const input = 'testInput';
const expectedUuid = '7ba1e88f-acf9-5528-9c1c-0c897ed80e1e';
const result = convertToUuid(input);
expect(result).toBe(expectedUuid);
});

test('should generate UUID for valid numeric input', () => {
const input = 123456;
const expectedUuid = 'a52b2702-9bcf-5701-852a-2f4edc640fe1';
const result = convertToUuid(input);
expect(result).toBe(expectedUuid);
});

test('should trim spaces and generate UUID', () => {
const input = ' testInput ';
const expectedUuid = '7ba1e88f-acf9-5528-9c1c-0c897ed80e1e';
const result = convertToUuid(input);
expect(result).toBe(expectedUuid);
});

test('should throw an error for empty input', () => {
const input = '';
expect(() => convertToUuid(input)).toThrow(InstrumentationError);
expect(() => convertToUuid(input)).toThrow('Input is empty or invalid.');
});

test('to throw an error for null input', () => {
const input = null;
expect(() => convertToUuid(input)).toThrow(InstrumentationError);
expect(() => convertToUuid(input)).toThrow('Input is undefined or null');
});

test('to throw an error for undefined input', () => {
const input = undefined;
expect(() => convertToUuid(input)).toThrow(InstrumentationError);
expect(() => convertToUuid(input)).toThrow('Input is undefined or null');
});

test('should throw an error for input that is whitespace only', () => {
const input = ' ';
expect(() => convertToUuid(input)).toThrow(InstrumentationError);
expect(() => convertToUuid(input)).toThrow('Input is empty or invalid.');
});

test('should handle long string input gracefully', () => {
const input = 'a'.repeat(1000);
const expectedUuid = v5(input, NAMESPACE);
const result = convertToUuid(input);
expect(result).toBe(expectedUuid);
});

test('any invalid input if stringified does not throw error', () => {
const input = {};
const result = convertToUuid(input);
expect(result).toBe('672ca00c-37f4-5d71-b8c3-6ae0848080ec');
});
});
Loading
Loading