From 61ec6faefcc472d7fa7f55793a4778463d9f6adf Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Mon, 13 Mar 2023 15:15:55 -0700 Subject: [PATCH 1/4] feat!: Add uuidv4 to platform. --- packages/sdk/server-node/package.json | 4 ++- .../server-node/src/platform/NodeCrypto.ts | 5 +++ .../internal/events/EventProcessor.test.ts | 4 +++ .../shared/common/src/api/platform/Crypto.ts | 1 + .../__tests__/BigSegmentsManager.test.ts | 4 +++ .../LDClientImpl.bigSegments.test.ts | 4 +++ .../evaluation/Evaluator.segments.test.ts | 4 +++ .../__tests__/evaluation/mocks/hasher.ts | 8 +++++ .../__tests__/events/EventProcessor.test.ts | 33 ++++++++++++------- packages/shared/sdk-server/package.json | 1 - .../src/events/DiagnosticsManager.ts | 3 +- .../sdk-server/src/events/EventSender.ts | 16 +++++++-- 12 files changed, 68 insertions(+), 19 deletions(-) diff --git a/packages/sdk/server-node/package.json b/packages/sdk/server-node/package.json index 95cc697ab2..e0680e3e2d 100644 --- a/packages/sdk/server-node/package.json +++ b/packages/sdk/server-node/package.json @@ -21,10 +21,12 @@ "dependencies": { "@launchdarkly/js-server-sdk-common": "0.1.0", "https-proxy-agent": "^5.0.1", - "launchdarkly-eventsource": "^1.4.4" + "launchdarkly-eventsource": "^1.4.4", + "uuid": "^9.0.0" }, "devDependencies": { "@types/jest": "^29.4.0", + "@types/uuid": "^9.0.1", "@typescript-eslint/eslint-plugin": "^5.22.0", "@typescript-eslint/parser": "^5.22.0", "eslint": "^8.14.0", diff --git a/packages/sdk/server-node/src/platform/NodeCrypto.ts b/packages/sdk/server-node/src/platform/NodeCrypto.ts index 011b67455c..fdf1549281 100644 --- a/packages/sdk/server-node/src/platform/NodeCrypto.ts +++ b/packages/sdk/server-node/src/platform/NodeCrypto.ts @@ -2,6 +2,7 @@ import { platform } from '@launchdarkly/js-server-sdk-common'; import * as crypto from 'crypto'; +import { v4 } from 'uuid'; export default class NodeCrypto implements platform.Crypto { createHash(algorithm: string): platform.Hasher { @@ -11,4 +12,8 @@ export default class NodeCrypto implements platform.Crypto { createHmac(algorithm: string, key: string): platform.Hmac { return crypto.createHmac(algorithm, key); } + + uuidv4() { + return v4(); + } } diff --git a/packages/shared/common/__tests__/internal/events/EventProcessor.test.ts b/packages/shared/common/__tests__/internal/events/EventProcessor.test.ts index a77efd3cd8..58dff12dac 100644 --- a/packages/shared/common/__tests__/internal/events/EventProcessor.test.ts +++ b/packages/shared/common/__tests__/internal/events/EventProcessor.test.ts @@ -171,6 +171,10 @@ describe('given an event processor', () => { // Not used for this test. throw new Error('Function not implemented.'); }, + uuidv4(): string { + // Not used for this test. + throw new Error(`Function not implemented.`); + }, }, requests: { /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ diff --git a/packages/shared/common/src/api/platform/Crypto.ts b/packages/shared/common/src/api/platform/Crypto.ts index f24ce431ee..4762b77c66 100644 --- a/packages/shared/common/src/api/platform/Crypto.ts +++ b/packages/shared/common/src/api/platform/Crypto.ts @@ -28,4 +28,5 @@ export interface Hmac extends Hasher { export interface Crypto { createHash(algorithm: string): Hasher; createHmac(algorithm: string, key: string): Hmac; + uuidv4(): string; } diff --git a/packages/shared/sdk-server/__tests__/BigSegmentsManager.test.ts b/packages/shared/sdk-server/__tests__/BigSegmentsManager.test.ts index d6293e6e4c..55cefcfba9 100644 --- a/packages/shared/sdk-server/__tests__/BigSegmentsManager.test.ts +++ b/packages/shared/sdk-server/__tests__/BigSegmentsManager.test.ts @@ -42,6 +42,10 @@ const crypto: Crypto = { // Not used for this test. throw new Error(`Function not implemented.${algorithm}${key}`); }, + uuidv4(): string { + // Not used for this test. + throw new Error(`Function not implemented.`); + }, }; describe.each(['STALE', 'HEALTHY'])('given a %s store', (status) => { diff --git a/packages/shared/sdk-server/__tests__/LDClientImpl.bigSegments.test.ts b/packages/shared/sdk-server/__tests__/LDClientImpl.bigSegments.test.ts index e25b8a3606..5d0c2bbe51 100644 --- a/packages/shared/sdk-server/__tests__/LDClientImpl.bigSegments.test.ts +++ b/packages/shared/sdk-server/__tests__/LDClientImpl.bigSegments.test.ts @@ -45,6 +45,10 @@ const crypto: Crypto = { // Not used for this test. throw new Error(`Function not implemented.${algorithm}${key}`); }, + uuidv4(): string { + // Not used for this test. + throw new Error(`Function not implemented.`); + }, }; describe('given test data with big segments', () => { diff --git a/packages/shared/sdk-server/__tests__/evaluation/Evaluator.segments.test.ts b/packages/shared/sdk-server/__tests__/evaluation/Evaluator.segments.test.ts index cdcb35b3ae..a83267b099 100644 --- a/packages/shared/sdk-server/__tests__/evaluation/Evaluator.segments.test.ts +++ b/packages/shared/sdk-server/__tests__/evaluation/Evaluator.segments.test.ts @@ -257,6 +257,10 @@ describe('when evaluating user equivalent contexts for segments', () => { // Not used for this test. throw new Error(`Function not implemented.${algorithm}${key}`); }, + uuidv4(): string { + // Not used for this test. + throw new Error(`Function not implemented.`); + }, }; const bucketingPlatform = { ...basicPlatform, crypto }; diff --git a/packages/shared/sdk-server/__tests__/evaluation/mocks/hasher.ts b/packages/shared/sdk-server/__tests__/evaluation/mocks/hasher.ts index 4e2f180a0d..c786e6c51c 100644 --- a/packages/shared/sdk-server/__tests__/evaluation/mocks/hasher.ts +++ b/packages/shared/sdk-server/__tests__/evaluation/mocks/hasher.ts @@ -7,6 +7,8 @@ export const hasher: Hasher = { digest: jest.fn(() => '1234567890123456'), }; +let counter = 0; + export const crypto: Crypto = { createHash(algorithm: string): Hasher { expect(algorithm).toEqual('sha1'); @@ -16,4 +18,10 @@ export const crypto: Crypto = { // Not used for this test. throw new Error(`Function not implemented.${algorithm}${key}`); }, + uuidv4(): string { + counter += 1; + // Will provide a unique value for tests. + // Very much not a UUID of course. + return `${counter}`; + }, }; diff --git a/packages/shared/sdk-server/__tests__/events/EventProcessor.test.ts b/packages/shared/sdk-server/__tests__/events/EventProcessor.test.ts index 35ed6530ba..595903b1ce 100644 --- a/packages/shared/sdk-server/__tests__/events/EventProcessor.test.ts +++ b/packages/shared/sdk-server/__tests__/events/EventProcessor.test.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { nanoid } from 'nanoid'; import { EventSource, EventSourceInitDict, @@ -13,6 +12,8 @@ import { ClientContext, Context, internal, + Hmac, + Hasher, } from '@launchdarkly/js-sdk-common'; import DiagnosticsManager from '../../src/events/DiagnosticsManager'; import Configuration from '../../src/options/Configuration'; @@ -29,11 +30,6 @@ interface RequestState { requestsMade: Array<{ url: string; options: Options }>; } -// Mock the nanoid module so we can replace the implementation in specific tests. -jest.mock('nanoid', () => ({ - nanoid: jest.fn(() => jest.requireActual('nanoid').nanoid()), -})); - function makePlatform(requestState: RequestState) { const info: Info = { platformData(): PlatformData { @@ -114,14 +110,27 @@ function makePlatform(requestState: RequestState) { throw new Error('Function not implemented.'); }, }; - return { info, requests, waitForMessages }; + return { + info, + requests, + waitForMessages, + crypto: { + createHash(algorithm: string): Hasher { + // Not used for this test. + throw new Error(`Function not implemented.${algorithm}`); + }, + createHmac(algorithm: string, key: string): Hmac { + // Not used for this test. + throw new Error(`Function not implemented.${algorithm}${key}`); + }, + uuidv4: () => '9-ypf7NswGfZ3CN2WpTix', + }, + }; } const user = { key: 'userKey', name: 'Red' }; describe('given an event processor with diagnostics manager', () => { - jest.mock('nanoid', () => ({ nanoid: () => '9-ypf7NswGfZ3CN2WpTix' })); - let eventProcessor: internal.EventProcessor; const requestState: RequestState = { @@ -145,12 +154,11 @@ describe('given an event processor with diagnostics manager', () => { let waitForMessages: (count: number) => Promise; beforeEach(() => { - // @ts-ignore - nanoid.mockImplementation(() => '9-ypf7NswGfZ3CN2WpTix'); - const platform = makePlatform(requestState); + info = platform.info; requests = platform.requests; + const { crypto } = platform; waitForMessages = platform.waitForMessages; resetRequestState(); @@ -171,6 +179,7 @@ describe('given an event processor with diagnostics manager', () => { // Replace info and requests. info, requests, + crypto, }, store ); diff --git a/packages/shared/sdk-server/package.json b/packages/shared/sdk-server/package.json index 43e107bc25..19926480bb 100644 --- a/packages/shared/sdk-server/package.json +++ b/packages/shared/sdk-server/package.json @@ -22,7 +22,6 @@ "license": "Apache-2.0", "dependencies": { "@launchdarkly/js-sdk-common": "0.1.0", - "nanoid": "^3.0.0", "semver": "^7.3.8" }, "devDependencies": { diff --git a/packages/shared/sdk-server/src/events/DiagnosticsManager.ts b/packages/shared/sdk-server/src/events/DiagnosticsManager.ts index 08ba1a52d6..3f176d5906 100644 --- a/packages/shared/sdk-server/src/events/DiagnosticsManager.ts +++ b/packages/shared/sdk-server/src/events/DiagnosticsManager.ts @@ -1,4 +1,3 @@ -import { nanoid } from 'nanoid'; import { Platform } from '@launchdarkly/js-sdk-common'; import { LDFeatureStore } from '../api/subsystems'; import Configuration, { defaultValues } from '../options/Configuration'; @@ -103,7 +102,7 @@ export default class DiagnosticsManager { this.startTime = Date.now(); this.dataSinceDate = this.startTime; this.id = { - diagnosticId: nanoid(), + diagnosticId: platform.crypto.uuidv4(), sdkKeySuffix: sdkKey.length > 6 ? sdkKey.substring(sdkKey.length - 6) : sdkKey, }; } diff --git a/packages/shared/sdk-server/src/events/EventSender.ts b/packages/shared/sdk-server/src/events/EventSender.ts index c35e6f4f2d..8943406307 100644 --- a/packages/shared/sdk-server/src/events/EventSender.ts +++ b/packages/shared/sdk-server/src/events/EventSender.ts @@ -1,5 +1,10 @@ -import { ApplicationTags, ClientContext, Requests, subsystem } from '@launchdarkly/js-sdk-common'; -import { nanoid } from 'nanoid'; +import { + ApplicationTags, + ClientContext, + Crypto, + Requests, + subsystem, +} from '@launchdarkly/js-sdk-common'; import defaultHeaders from '../data_sources/defaultHeaders'; import httpErrorMessage from '../data_sources/httpErrorMessage'; import { LDUnexpectedResponseError, isHttpRecoverable } from '../errors'; @@ -19,6 +24,8 @@ export default class EventSender implements subsystem.LDEventSender { private requests: Requests; + private crypto: Crypto; + constructor(config: EventSenderOptions, clientContext: ClientContext) { this.defaultHeaders = { ...defaultHeaders( @@ -33,6 +40,8 @@ export default class EventSender implements subsystem.LDEventSender { this.diagnosticEventsUri = `${clientContext.basicConfiguration.serviceEndpoints.events}/diagnostic`; this.requests = clientContext.platform.requests; + + this.crypto = clientContext.platform.crypto; } private async tryPostingEvents( @@ -102,7 +111,8 @@ export default class EventSender implements subsystem.LDEventSender { type: subsystem.LDEventType, data: any ): Promise { - const payloadId = type === subsystem.LDEventType.AnalyticsEvents ? nanoid() : undefined; + const payloadId = + type === subsystem.LDEventType.AnalyticsEvents ? this.crypto.uuidv4() : undefined; const uri = type === subsystem.LDEventType.AnalyticsEvents ? this.eventsUri : this.diagnosticEventsUri; From 4c3eee7fd9f262a3691883c6b60c3eb903ab16a3 Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Mon, 13 Mar 2023 15:24:04 -0700 Subject: [PATCH 2/4] chore: Remove uuid dependency. --- packages/sdk/server-node/package.json | 4 +--- packages/sdk/server-node/src/platform/NodeCrypto.ts | 9 ++++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/sdk/server-node/package.json b/packages/sdk/server-node/package.json index e0680e3e2d..95cc697ab2 100644 --- a/packages/sdk/server-node/package.json +++ b/packages/sdk/server-node/package.json @@ -21,12 +21,10 @@ "dependencies": { "@launchdarkly/js-server-sdk-common": "0.1.0", "https-proxy-agent": "^5.0.1", - "launchdarkly-eventsource": "^1.4.4", - "uuid": "^9.0.0" + "launchdarkly-eventsource": "^1.4.4" }, "devDependencies": { "@types/jest": "^29.4.0", - "@types/uuid": "^9.0.1", "@typescript-eslint/eslint-plugin": "^5.22.0", "@typescript-eslint/parser": "^5.22.0", "eslint": "^8.14.0", diff --git a/packages/sdk/server-node/src/platform/NodeCrypto.ts b/packages/sdk/server-node/src/platform/NodeCrypto.ts index fdf1549281..2801361ee6 100644 --- a/packages/sdk/server-node/src/platform/NodeCrypto.ts +++ b/packages/sdk/server-node/src/platform/NodeCrypto.ts @@ -1,19 +1,18 @@ /* eslint-disable class-methods-use-this */ import { platform } from '@launchdarkly/js-server-sdk-common'; -import * as crypto from 'crypto'; -import { v4 } from 'uuid'; +import { createHash, createHmac, randomUUID } from 'crypto'; export default class NodeCrypto implements platform.Crypto { createHash(algorithm: string): platform.Hasher { - return crypto.createHash(algorithm); + return createHash(algorithm); } createHmac(algorithm: string, key: string): platform.Hmac { - return crypto.createHmac(algorithm, key); + return createHmac(algorithm, key); } uuidv4() { - return v4(); + return randomUUID(); } } From d228541d684ebb22b9ea7fab422e0cc358000195 Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Mon, 13 Mar 2023 15:51:48 -0700 Subject: [PATCH 3/4] chore: Change name from uuidv4 to randomUUID. --- packages/sdk/server-node/src/platform/NodeCrypto.ts | 2 +- packages/shared/common/src/api/platform/Crypto.ts | 2 +- packages/shared/sdk-server/__tests__/BigSegmentsManager.test.ts | 2 +- .../sdk-server/__tests__/LDClientImpl.bigSegments.test.ts | 2 +- .../sdk-server/__tests__/evaluation/Evaluator.segments.test.ts | 2 +- packages/shared/sdk-server/__tests__/evaluation/mocks/hasher.ts | 2 +- .../shared/sdk-server/__tests__/events/EventProcessor.test.ts | 2 +- packages/shared/sdk-server/src/events/DiagnosticsManager.ts | 2 +- packages/shared/sdk-server/src/events/EventSender.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/sdk/server-node/src/platform/NodeCrypto.ts b/packages/sdk/server-node/src/platform/NodeCrypto.ts index 2801361ee6..3f28241987 100644 --- a/packages/sdk/server-node/src/platform/NodeCrypto.ts +++ b/packages/sdk/server-node/src/platform/NodeCrypto.ts @@ -12,7 +12,7 @@ export default class NodeCrypto implements platform.Crypto { return createHmac(algorithm, key); } - uuidv4() { + randomUUID() { return randomUUID(); } } diff --git a/packages/shared/common/src/api/platform/Crypto.ts b/packages/shared/common/src/api/platform/Crypto.ts index 4762b77c66..417fe03fbd 100644 --- a/packages/shared/common/src/api/platform/Crypto.ts +++ b/packages/shared/common/src/api/platform/Crypto.ts @@ -28,5 +28,5 @@ export interface Hmac extends Hasher { export interface Crypto { createHash(algorithm: string): Hasher; createHmac(algorithm: string, key: string): Hmac; - uuidv4(): string; + randomUUID(): string; } diff --git a/packages/shared/sdk-server/__tests__/BigSegmentsManager.test.ts b/packages/shared/sdk-server/__tests__/BigSegmentsManager.test.ts index 55cefcfba9..be30b893ff 100644 --- a/packages/shared/sdk-server/__tests__/BigSegmentsManager.test.ts +++ b/packages/shared/sdk-server/__tests__/BigSegmentsManager.test.ts @@ -42,7 +42,7 @@ const crypto: Crypto = { // Not used for this test. throw new Error(`Function not implemented.${algorithm}${key}`); }, - uuidv4(): string { + randomUUID(): string { // Not used for this test. throw new Error(`Function not implemented.`); }, diff --git a/packages/shared/sdk-server/__tests__/LDClientImpl.bigSegments.test.ts b/packages/shared/sdk-server/__tests__/LDClientImpl.bigSegments.test.ts index 5d0c2bbe51..bc4dda0ab3 100644 --- a/packages/shared/sdk-server/__tests__/LDClientImpl.bigSegments.test.ts +++ b/packages/shared/sdk-server/__tests__/LDClientImpl.bigSegments.test.ts @@ -45,7 +45,7 @@ const crypto: Crypto = { // Not used for this test. throw new Error(`Function not implemented.${algorithm}${key}`); }, - uuidv4(): string { + randomUUID(): string { // Not used for this test. throw new Error(`Function not implemented.`); }, diff --git a/packages/shared/sdk-server/__tests__/evaluation/Evaluator.segments.test.ts b/packages/shared/sdk-server/__tests__/evaluation/Evaluator.segments.test.ts index a83267b099..5f90f6b72d 100644 --- a/packages/shared/sdk-server/__tests__/evaluation/Evaluator.segments.test.ts +++ b/packages/shared/sdk-server/__tests__/evaluation/Evaluator.segments.test.ts @@ -257,7 +257,7 @@ describe('when evaluating user equivalent contexts for segments', () => { // Not used for this test. throw new Error(`Function not implemented.${algorithm}${key}`); }, - uuidv4(): string { + randomUUID(): string { // Not used for this test. throw new Error(`Function not implemented.`); }, diff --git a/packages/shared/sdk-server/__tests__/evaluation/mocks/hasher.ts b/packages/shared/sdk-server/__tests__/evaluation/mocks/hasher.ts index c786e6c51c..6fe6aab5ef 100644 --- a/packages/shared/sdk-server/__tests__/evaluation/mocks/hasher.ts +++ b/packages/shared/sdk-server/__tests__/evaluation/mocks/hasher.ts @@ -18,7 +18,7 @@ export const crypto: Crypto = { // Not used for this test. throw new Error(`Function not implemented.${algorithm}${key}`); }, - uuidv4(): string { + randomUUID(): string { counter += 1; // Will provide a unique value for tests. // Very much not a UUID of course. diff --git a/packages/shared/sdk-server/__tests__/events/EventProcessor.test.ts b/packages/shared/sdk-server/__tests__/events/EventProcessor.test.ts index 595903b1ce..27396f7a4f 100644 --- a/packages/shared/sdk-server/__tests__/events/EventProcessor.test.ts +++ b/packages/shared/sdk-server/__tests__/events/EventProcessor.test.ts @@ -123,7 +123,7 @@ function makePlatform(requestState: RequestState) { // Not used for this test. throw new Error(`Function not implemented.${algorithm}${key}`); }, - uuidv4: () => '9-ypf7NswGfZ3CN2WpTix', + randomUUID: () => '9-ypf7NswGfZ3CN2WpTix', }, }; } diff --git a/packages/shared/sdk-server/src/events/DiagnosticsManager.ts b/packages/shared/sdk-server/src/events/DiagnosticsManager.ts index 3f176d5906..1b9c41f7b2 100644 --- a/packages/shared/sdk-server/src/events/DiagnosticsManager.ts +++ b/packages/shared/sdk-server/src/events/DiagnosticsManager.ts @@ -102,7 +102,7 @@ export default class DiagnosticsManager { this.startTime = Date.now(); this.dataSinceDate = this.startTime; this.id = { - diagnosticId: platform.crypto.uuidv4(), + diagnosticId: platform.crypto.randomUUID(), sdkKeySuffix: sdkKey.length > 6 ? sdkKey.substring(sdkKey.length - 6) : sdkKey, }; } diff --git a/packages/shared/sdk-server/src/events/EventSender.ts b/packages/shared/sdk-server/src/events/EventSender.ts index 8943406307..074706d02e 100644 --- a/packages/shared/sdk-server/src/events/EventSender.ts +++ b/packages/shared/sdk-server/src/events/EventSender.ts @@ -112,7 +112,7 @@ export default class EventSender implements subsystem.LDEventSender { data: any ): Promise { const payloadId = - type === subsystem.LDEventType.AnalyticsEvents ? this.crypto.uuidv4() : undefined; + type === subsystem.LDEventType.AnalyticsEvents ? this.crypto.randomUUID() : undefined; const uri = type === subsystem.LDEventType.AnalyticsEvents ? this.eventsUri : this.diagnosticEventsUri; From 419de2cdc771d2d047c9c80ad0fea2f62fd585df Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Mon, 13 Mar 2023 15:54:50 -0700 Subject: [PATCH 4/4] EventProcessor.test.ts. --- .../common/__tests__/internal/events/EventProcessor.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/common/__tests__/internal/events/EventProcessor.test.ts b/packages/shared/common/__tests__/internal/events/EventProcessor.test.ts index 58dff12dac..8e5d173b35 100644 --- a/packages/shared/common/__tests__/internal/events/EventProcessor.test.ts +++ b/packages/shared/common/__tests__/internal/events/EventProcessor.test.ts @@ -171,7 +171,7 @@ describe('given an event processor', () => { // Not used for this test. throw new Error('Function not implemented.'); }, - uuidv4(): string { + randomUUID(): string { // Not used for this test. throw new Error(`Function not implemented.`); },