Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
10 changes: 7 additions & 3 deletions packages/sdk/server-node/src/platform/NodeCrypto.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
/* eslint-disable class-methods-use-this */
import { platform } from '@launchdarkly/js-server-sdk-common';

import * as crypto from 'crypto';
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 randomUUID();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our targeted versions of node should have this built-in. Which is better than a dependency. But it isn't something we can promise will be in all server SDKs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Less code = more better

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions packages/shared/common/src/api/platform/Crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ export interface Hmac extends Hasher {
export interface Crypto {
createHash(algorithm: string): Hasher;
createHmac(algorithm: string, key: string): Hmac;
uuidv4(): string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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}`;
},
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { nanoid } from 'nanoid';
import {
EventSource,
EventSourceInitDict,
Expand All @@ -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';
Expand All @@ -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 {
Expand Down Expand Up @@ -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 = {
Expand All @@ -145,12 +154,11 @@ describe('given an event processor with diagnostics manager', () => {
let waitForMessages: (count: number) => Promise<number>;

beforeEach(() => {
// @ts-ignore
nanoid.mockImplementation(() => '9-ypf7NswGfZ3CN2WpTix');

const platform = makePlatform(requestState);

info = platform.info;
requests = platform.requests;
const { crypto } = platform;
waitForMessages = platform.waitForMessages;

resetRequestState();
Expand All @@ -171,6 +179,7 @@ describe('given an event processor with diagnostics manager', () => {
// Replace info and requests.
info,
requests,
crypto,
},
store
);
Expand Down
1 change: 0 additions & 1 deletion packages/shared/sdk-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -103,7 +102,7 @@ export default class DiagnosticsManager {
this.startTime = Date.now();
this.dataSinceDate = this.startTime;
this.id = {
diagnosticId: nanoid(),
diagnosticId: platform.crypto.uuidv4(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now we use the platform provided uuidv4. (Platform here being our implementation of the platform. Like server-node,)

sdkKeySuffix: sdkKey.length > 6 ? sdkKey.substring(sdkKey.length - 6) : sdkKey,
};
}
Expand Down
16 changes: 13 additions & 3 deletions packages/shared/sdk-server/src/events/EventSender.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -102,7 +111,8 @@ export default class EventSender implements subsystem.LDEventSender {
type: subsystem.LDEventType,
data: any
): Promise<subsystem.LDEventSenderResult> {
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;

Expand Down