Skip to content

Commit

Permalink
fix: stringifying session ID for airship (#3896)
Browse files Browse the repository at this point in the history
* fix: stringifying session ID for airship

* fix: test case numbering fix

* fix: adding more test cases

* fix: review comment addressed

* fix: edit error message

* chore: refactored code

* chore: handled stringifcation in convertToUuid

* fix: adding test cases

* fix: small edit

* fix: code review addressed

---------

Co-authored-by: krishnachaitanya <[email protected]>
  • Loading branch information
shrouti1507 and krishna2020 authored Nov 21, 2024
1 parent ca71a31 commit bb0b9dc
Show file tree
Hide file tree
Showing 6 changed files with 366 additions and 15 deletions.
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 uaParser = require('ua-parser-js');
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 isEventSentByVDMV1Flow = (event) => event?.message?.context?.mappedToDesti

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

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

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

try {
// Stringify and trim the input
const trimmedInput = String(input).trim();

// Check for empty input after trimming
if (!trimmedInput) {
throw new InstrumentationError('Input is empty or invalid.');
}
// Generate and return UUID
return v5(trimmedInput, NAMESPACE);
} catch (error) {
const errorMessage = `Failed to transform input to uuid: ${error.message}`;
throw new InstrumentationError(errorMessage);
}
};
// ========================================================================
// EXPORTS
// ========================================================================
Expand Down Expand Up @@ -2456,4 +2480,5 @@ module.exports = {
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

0 comments on commit bb0b9dc

Please sign in to comment.