From a2dbfe413bf8d42316881bb4b195bc877c7ab901 Mon Sep 17 00:00:00 2001 From: LaunchDarklyReleaseBot Date: Fri, 25 Aug 2023 23:36:23 -0700 Subject: [PATCH 01/21] feat: implement useEnvironmentEndpoint logic by inspecting the sdk-key prefix. --- packages/sdk/cloudflare/src/index.ts | 12 ++++---- packages/shared/sdk-server-edge/package.json | 2 +- .../sdk-server-edge/src/api/LDClient.ts | 6 ++-- packages/shared/sdk-server-edge/src/index.ts | 6 ++-- .../src/data_sources/defaultHeaders.ts | 6 +++- .../sdk-server/src/events/EventSender.ts | 28 ++++++++++++------- 6 files changed, 36 insertions(+), 24 deletions(-) diff --git a/packages/sdk/cloudflare/src/index.ts b/packages/sdk/cloudflare/src/index.ts index 7aa051b97..692a0c6fc 100644 --- a/packages/sdk/cloudflare/src/index.ts +++ b/packages/sdk/cloudflare/src/index.ts @@ -35,20 +35,20 @@ export type { LDClient }; * (`new LDClient()/new LDClientImpl()/new LDClient()`); the SDK does not currently support * this. * + * @param clientSideID + * The client side ID. This is only used to query the kvNamespace above, + * not to connect with LaunchDarkly servers. * @param kvNamespace * The Cloudflare KV configured for LaunchDarkly. - * @param sdkKey - * The client side SDK key. This is only used to query the kvNamespace above, - * not to connect with LaunchDarkly servers. * @param options * Optional configuration settings. The only supported option is logger. * @return * The new {@link LDClient} instance. */ -export const init = (sdkKey: string, kvNamespace: KVNamespace, options: LDOptions = {}) => { +export const init = (clientSideID: string, kvNamespace: KVNamespace, options: LDOptions = {}) => { const logger = options.logger ?? BasicLogger.get(); - return initEdge(sdkKey, createPlatformInfo(), { - featureStore: new EdgeFeatureStore(kvNamespace, sdkKey, 'Cloudflare', logger), + return initEdge(clientSideID, createPlatformInfo(), { + featureStore: new EdgeFeatureStore(kvNamespace, clientSideID, 'Cloudflare', logger), logger, ...options, }); diff --git a/packages/shared/sdk-server-edge/package.json b/packages/shared/sdk-server-edge/package.json index 485fe063f..f7bed3fb2 100644 --- a/packages/shared/sdk-server-edge/package.json +++ b/packages/shared/sdk-server-edge/package.json @@ -26,7 +26,7 @@ ], "scripts": { "doc": "../../../scripts/build-doc.sh .", - "build": "../../../scripts/build-package.sh", + "build": "/bin/sh ../../../scripts/build-package.sh", "tsw": "yarn tsc --watch", "clean": "rimraf dist", "start": "rimraf dist && yarn tsw", diff --git a/packages/shared/sdk-server-edge/src/api/LDClient.ts b/packages/shared/sdk-server-edge/src/api/LDClient.ts index d2b4e821b..ed8a06f09 100644 --- a/packages/shared/sdk-server-edge/src/api/LDClient.ts +++ b/packages/shared/sdk-server-edge/src/api/LDClient.ts @@ -12,11 +12,11 @@ import createOptions from './createOptions'; export class LDClient extends LDClientImpl { emitter: EventEmitter; - // sdkKey is only used to query featureStore, not to initialize with LD servers - constructor(sdkKey: string, platformInfo: Info, options: LDOptions) { + // clientSideID is only used to query featureStore, not to initialize with LD servers + constructor(clientSideID: string, platformInfo: Info, options: LDOptions) { const em = new EventEmitter(); const platform = new EdgePlatform(platformInfo); - super('n/a', platform, createOptions(options), createCallbacks(em)); + super(clientSideID, platform, createOptions(options), createCallbacks(em)); this.emitter = em; } } diff --git a/packages/shared/sdk-server-edge/src/index.ts b/packages/shared/sdk-server-edge/src/index.ts index 60d9785e8..24a6c49b7 100644 --- a/packages/shared/sdk-server-edge/src/index.ts +++ b/packages/shared/sdk-server-edge/src/index.ts @@ -19,9 +19,9 @@ export type { LDClient, LDOptions, EdgeProvider }; * * This is an internal API to be used directly only by LaunchDarkly Edge SDKs. */ -export const init = (sdkKey: string, platformInfo: Info, options: LDOptionsInternal) => { +export const init = (clientSideID: string, platformInfo: Info, options: LDOptionsInternal) => { // this throws if options are invalid - validateOptions(sdkKey, options); + validateOptions(clientSideID, options); - return new LDClient(sdkKey, platformInfo, options); + return new LDClient(clientSideID, platformInfo, options); }; diff --git a/packages/shared/sdk-server/src/data_sources/defaultHeaders.ts b/packages/shared/sdk-server/src/data_sources/defaultHeaders.ts index 42db4ac2a..99ebc9045 100644 --- a/packages/shared/sdk-server/src/data_sources/defaultHeaders.ts +++ b/packages/shared/sdk-server/src/data_sources/defaultHeaders.ts @@ -11,12 +11,16 @@ export default function defaultHeaders( ): { [key: string]: string } { const sdkData = info.sdkData(); const headers: { [key: string]: string } = { - authorization: sdkKey, 'user-agent': `${sdkData.userAgentBase ? sdkData.userAgentBase : 'NodeJSClient'}/${ sdkData.version }`, }; + // edge sdks use clientSideID and don't need the authorization header + if (sdkKey.startsWith('sdk-')) { + headers.authorization = sdkKey; + } + if (sdkData.wrapperName) { headers['x-launchdarkly-wrapper'] = sdkData.wrapperVersion ? `${sdkData.wrapperName}/${sdkData.wrapperVersion}` diff --git a/packages/shared/sdk-server/src/events/EventSender.ts b/packages/shared/sdk-server/src/events/EventSender.ts index e405a6254..d19153929 100644 --- a/packages/shared/sdk-server/src/events/EventSender.ts +++ b/packages/shared/sdk-server/src/events/EventSender.ts @@ -28,21 +28,29 @@ export default class EventSender implements subsystem.LDEventSender { private crypto: Crypto; constructor(config: EventSenderOptions, clientContext: ClientContext) { + const { + basicConfiguration: { + sdkKey, + serviceEndpoints: { events }, + }, + platform: { info, requests, crypto }, + } = clientContext; + this.defaultHeaders = { - ...defaultHeaders( - clientContext.basicConfiguration.sdkKey, - config, - clientContext.platform.info, - ), + ...defaultHeaders(sdkKey, config, info), }; - this.eventsUri = `${clientContext.basicConfiguration.serviceEndpoints.events}/bulk`; - - this.diagnosticEventsUri = `${clientContext.basicConfiguration.serviceEndpoints.events}/diagnostic`; + this.eventsUri = `${events}/bulk`; - this.requests = clientContext.platform.requests; + // edge sdks use clientSideID so we use the environment endpoint + const useEnvironmentEndpoint = !sdkKey.startsWith('sdk-'); + if (useEnvironmentEndpoint) { + this.eventsUri = `${this.eventsUri}/${sdkKey}`; + } - this.crypto = clientContext.platform.crypto; + this.diagnosticEventsUri = `${events}/diagnostic`; + this.requests = requests; + this.crypto = crypto; } private async tryPostingEvents( From cada2f61573771c2cd445cc20cf1cde143deb612 Mon Sep 17 00:00:00 2001 From: LaunchDarklyReleaseBot Date: Fri, 25 Aug 2023 23:42:00 -0700 Subject: [PATCH 02/21] fix: broken tests. --- .../__tests__/data_sources/Requestor.test.ts | 9 +++++++-- .../data_sources/defaultHeaders.test.ts | 16 ++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/shared/sdk-server/__tests__/data_sources/Requestor.test.ts b/packages/shared/sdk-server/__tests__/data_sources/Requestor.test.ts index 0077d8caa..c13a1f575 100644 --- a/packages/shared/sdk-server/__tests__/data_sources/Requestor.test.ts +++ b/packages/shared/sdk-server/__tests__/data_sources/Requestor.test.ts @@ -80,7 +80,12 @@ describe('given a requestor', () => { }, }; - requestor = new Requestor('sdkKey', new Configuration({}), basicPlatform.info, requests); + requestor = new Requestor( + 'sdk-my-sdk-Key', + new Configuration({}), + basicPlatform.info, + requests, + ); }); it('gets data', (done) => { @@ -91,7 +96,7 @@ describe('given a requestor', () => { expect(requestsMade.length).toBe(1); expect(requestsMade[0].url).toBe('https://sdk.launchdarkly.com/sdk/latest-all'); - expect(requestsMade[0].options.headers?.authorization).toBe('sdkKey'); + expect(requestsMade[0].options.headers?.authorization).toBe('sdk-my-sdk-Key'); done(); }); diff --git a/packages/shared/sdk-server/__tests__/data_sources/defaultHeaders.test.ts b/packages/shared/sdk-server/__tests__/data_sources/defaultHeaders.test.ts index 5da91e08b..f0d0f9fe8 100644 --- a/packages/shared/sdk-server/__tests__/data_sources/defaultHeaders.test.ts +++ b/packages/shared/sdk-server/__tests__/data_sources/defaultHeaders.test.ts @@ -20,37 +20,37 @@ const makeInfo = (wrapperName?: string, wrapperVersion?: string, userAgentBase?: it('sets SDK key', () => { const config = new Configuration({}); - const h = defaultHeaders('my-sdk-key', config, makeInfo()); - expect(h).toMatchObject({ authorization: 'my-sdk-key' }); + const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo()); + expect(h).toMatchObject({ authorization: 'sdk-my-sdk-key' }); }); it('sets the default user agent', () => { const config = new Configuration({}); - const h = defaultHeaders('my-sdk-key', config, makeInfo()); + const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo()); expect(h).toMatchObject({ 'user-agent': 'NodeJSClient/2.2.2' }); }); it('sets the SDK specific user agent', () => { const config = new Configuration({}); - const h = defaultHeaders('my-sdk-key', config, makeInfo(undefined, undefined, 'CATS')); + const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo(undefined, undefined, 'CATS')); expect(h).toMatchObject({ 'user-agent': 'CATS/2.2.2' }); }); it('does not include wrapper header by default', () => { const config = new Configuration({}); - const h = defaultHeaders('my-sdk-key', config, makeInfo()); + const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo()); expect(h['x-launchdarkly-wrapper']).toBeUndefined(); }); it('sets wrapper header with name only', () => { const config = new Configuration({}); - const h = defaultHeaders('my-sdk-key', config, makeInfo('my-wrapper')); + const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo('my-wrapper')); expect(h).toMatchObject({ 'x-launchdarkly-wrapper': 'my-wrapper' }); }); it('sets wrapper header with name and version', () => { const config = new Configuration({}); - const h = defaultHeaders('my-sdk-key', config, makeInfo('my-wrapper', '2.0')); + const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo('my-wrapper', '2.0')); expect(h).toMatchObject({ 'x-launchdarkly-wrapper': 'my-wrapper/2.0' }); }); @@ -61,7 +61,7 @@ it('sets the X-LaunchDarkly-Tags header with valid tags.', () => { version: 'test-version', }, }); - const h = defaultHeaders('my-sdk-key', config, makeInfo('my-wrapper')); + const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo('my-wrapper')); expect(h).toMatchObject({ 'x-launchdarkly-tags': 'application-id/test-application application-version/test-version', }); From 37bdbf4371c323e932561d578898a1ac77e6eb8d Mon Sep 17 00:00:00 2001 From: LaunchDarklyReleaseBot Date: Fri, 25 Aug 2023 23:55:51 -0700 Subject: [PATCH 03/21] fix: wrong events path for clientSideID --- .../shared/sdk-server/src/events/EventSender.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/shared/sdk-server/src/events/EventSender.ts b/packages/shared/sdk-server/src/events/EventSender.ts index d19153929..afd7bc103 100644 --- a/packages/shared/sdk-server/src/events/EventSender.ts +++ b/packages/shared/sdk-server/src/events/EventSender.ts @@ -40,15 +40,12 @@ export default class EventSender implements subsystem.LDEventSender { ...defaultHeaders(sdkKey, config, info), }; - this.eventsUri = `${events}/bulk`; + const isClientSideID = !sdkKey.startsWith('sdk-'); + this.eventsUri = isClientSideID ? `${events}/events/bulk/${sdkKey}` : `${events}/bulk`; + this.diagnosticEventsUri = isClientSideID + ? `${events}/events/diagnostic/${sdkKey}` + : `${events}/diagnostic`; - // edge sdks use clientSideID so we use the environment endpoint - const useEnvironmentEndpoint = !sdkKey.startsWith('sdk-'); - if (useEnvironmentEndpoint) { - this.eventsUri = `${this.eventsUri}/${sdkKey}`; - } - - this.diagnosticEventsUri = `${events}/diagnostic`; this.requests = requests; this.crypto = crypto; } From cfbc5455073e22dce897afb442fc4cb7be6aa0a3 Mon Sep 17 00:00:00 2001 From: LaunchDarklyReleaseBot Date: Sun, 27 Aug 2023 19:30:41 -0700 Subject: [PATCH 04/21] fix: added shell directive. added link-dev for cloudflare/example to work locally. --- link-dev.sh | 15 +++++++++++++++ package.json | 4 ++-- packages/sdk/cloudflare/example/package.json | 6 ++++-- packages/sdk/cloudflare/example/src/index.ts | 11 +++++++---- packages/shared/sdk-server-edge/package.json | 2 +- .../shared/sdk-server/src/events/EventSender.ts | 8 ++++++++ scripts/build-doc.sh | 2 ++ scripts/build-package.sh | 4 +++- scripts/doc-name.sh | 2 ++ scripts/package-name.sh | 2 ++ scripts/publish-doc.sh | 12 +++++++----- scripts/publish.sh | 2 ++ scripts/replace-version.sh | 2 ++ 13 files changed, 57 insertions(+), 15 deletions(-) create mode 100755 link-dev.sh diff --git a/link-dev.sh b/link-dev.sh new file mode 100755 index 000000000..afa008c11 --- /dev/null +++ b/link-dev.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +echo "===== Linking to cloudflare/example" + +rm -rf packages/sdk/cloudflare/example/node_modules/@launchdarkly/js-sdk-common/dist +cp -r packages/shared/common/dist packages/sdk/cloudflare/example/node_modules/@launchdarkly/js-sdk-common/dist + +rm -rf packages/sdk/cloudflare/example/node_modules/@launchdarkly/js-server-sdk-common/dist +cp -r packages/shared/sdk-server/dist packages/sdk/cloudflare/example/node_modules/@launchdarkly/js-server-sdk-common/dist + +rm -rf packages/sdk/cloudflare/example/node_modules/@launchdarkly/js-server-sdk-common-edge/dist +cp -r packages/shared/sdk-server-edge/dist packages/sdk/cloudflare/example/node_modules/@launchdarkly/js-server-sdk-common-edge/dist + +rm -rf packages/sdk/cloudflare/example/node_modules/@launchdarkly/cloudflare-server-sdk/dist +cp -r packages/sdk/cloudflare/dist packages/sdk/cloudflare/example/node_modules/@launchdarkly/cloudflare-server-sdk/dist diff --git a/package.json b/package.json index fb5f38b8a..ad4839889 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "packages/shared/akamai-edgeworker-sdk", "packages/sdk/server-node", "packages/sdk/cloudflare", - "packages/sdk/cloudflare/example", "packages/sdk/vercel", "packages/sdk/akamai-base", "packages/sdk/akamai-base/example", @@ -30,7 +29,8 @@ "contract-test-harness": "curl -s https://raw.githubusercontent.com/launchdarkly/sdk-test-harness/master/downloader/run.sh \\ | VERSION=v2 PARAMS=\"-url http://localhost:8000 -debug -stop-service-at-end $TEST_HARNESS_PARAMS\" sh", "contract-tests": "npm run contract-test-service & npm run contract-test-harness", "prettier": "npx prettier --write \"**/*.{js,ts,tsx,json,yaml,yml,md}\" --log-level warn", - "check": "yarn && yarn prettier && yarn lint && tsc && yarn build" + "check": "yarn && yarn prettier && yarn lint && tsc && yarn build", + "link-dev": "./link-dev.sh" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.1.1", diff --git a/packages/sdk/cloudflare/example/package.json b/packages/sdk/cloudflare/example/package.json index fee105ba2..5b9cc8711 100644 --- a/packages/sdk/cloudflare/example/package.json +++ b/packages/sdk/cloudflare/example/package.json @@ -20,9 +20,11 @@ "wrangler": "2.13.0" }, "scripts": { - "build": "node build.js", + "build": "yarn && yarn link-dev && node build.js", "start": "wrangler dev", "deploy": "wrangler publish", - "test": "yarn build && node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js" + "test": "yarn build && node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js", + "clean": "rm -rf dist && rm -rf node_modules && rm -rf .yarn/cache && yarn build", + "link-dev": "cd ../../../../ && yarn link-dev" } } diff --git a/packages/sdk/cloudflare/example/src/index.ts b/packages/sdk/cloudflare/example/src/index.ts index 9658d32c0..f03554970 100644 --- a/packages/sdk/cloudflare/example/src/index.ts +++ b/packages/sdk/cloudflare/example/src/index.ts @@ -2,16 +2,19 @@ import { init as initLD } from '@launchdarkly/cloudflare-server-sdk'; export default { async fetch(request: Request, env: Bindings): Promise { - const sdkKey = 'test-sdk-key'; - const flagKey = 'testFlag1'; - const context = { kind: 'user', key: 'test-user-key-1', email: 'test@gmail.com' }; + const clientSideID = '59b2b2596d1a250b1c78baa4'; + const flagKey = 'dev-test-flag'; + const context = { kind: 'org', key: 'org-key-cf', email: 'testcforg@gmail.com' }; // start using ld - const client = initLD(sdkKey, env.LD_KV); + const client = initLD(clientSideID, env.LD_KV, { sendEvents: true }); await client.waitForInitialization(); const flagValue = await client.variation(flagKey, context, false); const flagDetail = await client.variationDetail(flagKey, context, false); const allFlags = await client.allFlagsState(context); + await client.flush((err, res) => { + console.log(`============ flushed events result: ${res}. error: ${err}`); + }); const resp = ` ${flagKey}: ${flagValue} diff --git a/packages/shared/sdk-server-edge/package.json b/packages/shared/sdk-server-edge/package.json index f7bed3fb2..485fe063f 100644 --- a/packages/shared/sdk-server-edge/package.json +++ b/packages/shared/sdk-server-edge/package.json @@ -26,7 +26,7 @@ ], "scripts": { "doc": "../../../scripts/build-doc.sh .", - "build": "/bin/sh ../../../scripts/build-package.sh", + "build": "../../../scripts/build-package.sh", "tsw": "yarn tsc --watch", "clean": "rimraf dist", "start": "rimraf dist && yarn tsw", diff --git a/packages/shared/sdk-server/src/events/EventSender.ts b/packages/shared/sdk-server/src/events/EventSender.ts index afd7bc103..b8b71397b 100644 --- a/packages/shared/sdk-server/src/events/EventSender.ts +++ b/packages/shared/sdk-server/src/events/EventSender.ts @@ -2,6 +2,7 @@ import { ApplicationTags, ClientContext, Crypto, + LDLogger, Requests, subsystem, } from '@launchdarkly/js-sdk-common'; @@ -27,11 +28,14 @@ export default class EventSender implements subsystem.LDEventSender { private crypto: Crypto; + private logger?: LDLogger; + constructor(config: EventSenderOptions, clientContext: ClientContext) { const { basicConfiguration: { sdkKey, serviceEndpoints: { events }, + logger, }, platform: { info, requests, crypto }, } = clientContext; @@ -48,6 +52,7 @@ export default class EventSender implements subsystem.LDEventSender { this.requests = requests; this.crypto = crypto; + this.logger = logger; } private async tryPostingEvents( @@ -71,6 +76,9 @@ export default class EventSender implements subsystem.LDEventSender { } let error; try { + this.logger?.error(`===== uri: ${uri}`); + this.logger?.error(`===== headers: ${JSON.stringify(headers)}`); + this.logger?.error(`===== events: ${JSON.stringify(events)}`); const res = await this.requests.fetch(uri, { headers, body: JSON.stringify(events), diff --git a/scripts/build-doc.sh b/scripts/build-doc.sh index 33887ce58..e91ccfd5f 100755 --- a/scripts/build-doc.sh +++ b/scripts/build-doc.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # Run this script like: # ./scripts/build-doc.sh packages/sdk/node diff --git a/scripts/build-package.sh b/scripts/build-package.sh index 93e38fe00..25282db79 100755 --- a/scripts/build-package.sh +++ b/scripts/build-package.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # Run this script like: # ./scripts/build-package.sh @@ -23,7 +25,7 @@ ESM_PACKAGE_JSON=$( jq -n \ --arg type "module" \ '{ name: $name, version: $version, type: $type }' ) -tsc --module commonjs --outDir dist/cjs/ +tsc --module commonjs --outDir dist/cjs/ echo "$CJS_PACKAGE_JSON" > dist/cjs/package.json tsc --module es2022 --outDir dist/esm/ diff --git a/scripts/doc-name.sh b/scripts/doc-name.sh index 150c0fb70..7ba442cb6 100755 --- a/scripts/doc-name.sh +++ b/scripts/doc-name.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # Given a path get the name for the documentation. # ./scripts/doc-name.sh packages/sdk/server-node # Produces something like: diff --git a/scripts/package-name.sh b/scripts/package-name.sh index e443b2c7d..6d398cb95 100755 --- a/scripts/package-name.sh +++ b/scripts/package-name.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # Given a path get the name of the package. # ./scripts/package-name.sh packages/sdk/server-node # Produces something like: diff --git a/scripts/publish-doc.sh b/scripts/publish-doc.sh index 44d885c79..730c513a5 100755 --- a/scripts/publish-doc.sh +++ b/scripts/publish-doc.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # Run this script like: # ./scripts/publish-doc.sh packages/sdk/node @@ -55,10 +57,10 @@ set +e while true do - + git pull origin gh-pages --no-edit # should accept the default message after_pull_sha=$(git rev-parse HEAD) - + # The first time this runs the head_sha will be empty and they will not match. # If the push fails, then we pull again, and if the SHA does not change, then # the push will not succeed. @@ -66,13 +68,13 @@ do echo "Failed to get changes. Could not publish docs." exit 1 fi - + head_sha=$after_pull_sha - + if git push; then break fi - + echo "Push failed, trying again." done diff --git a/scripts/publish.sh b/scripts/publish.sh index 597e964da..1b92c1c3c 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -1,3 +1,5 @@ +#!/bin/bash + if $LD_RELEASE_IS_DRYRUN ; then # Dry run just pack the workspace. echo "Doing a dry run of publishing." diff --git a/scripts/replace-version.sh b/scripts/replace-version.sh index 87d2309af..154e9c4e8 100755 --- a/scripts/replace-version.sh +++ b/scripts/replace-version.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # Run this script like: # ./scripts/replace-version.sh packages/sdk/node From 28741948f234fe30da82b84b73df819b0d8dc9d4 Mon Sep 17 00:00:00 2001 From: LaunchDarklyReleaseBot Date: Sun, 27 Aug 2023 19:53:18 -0700 Subject: [PATCH 05/21] chore: remove debug statements. --- packages/sdk/cloudflare/example/src/index.ts | 11 +++++++---- packages/shared/sdk-server/src/events/EventSender.ts | 9 +-------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/sdk/cloudflare/example/src/index.ts b/packages/sdk/cloudflare/example/src/index.ts index f03554970..d9063d96e 100644 --- a/packages/sdk/cloudflare/example/src/index.ts +++ b/packages/sdk/cloudflare/example/src/index.ts @@ -1,9 +1,10 @@ +/* eslint-disable no-console */ import { init as initLD } from '@launchdarkly/cloudflare-server-sdk'; export default { async fetch(request: Request, env: Bindings): Promise { - const clientSideID = '59b2b2596d1a250b1c78baa4'; - const flagKey = 'dev-test-flag'; + const clientSideID = 'client-side-id'; + const flagKey = 'testFlag1'; const context = { kind: 'org', key: 'org-key-cf', email: 'testcforg@gmail.com' }; // start using ld @@ -12,8 +13,11 @@ export default { const flagValue = await client.variation(flagKey, context, false); const flagDetail = await client.variationDetail(flagKey, context, false); const allFlags = await client.allFlagsState(context); + + // Gotcha: you must call flush otherwise events will not be sent to LD servers + // due to the ephemeral nature of edge workers. await client.flush((err, res) => { - console.log(`============ flushed events result: ${res}. error: ${err}`); + console.log(`flushed events result: ${res}, error: ${err}`); }); const resp = ` @@ -21,7 +25,6 @@ export default { detail: ${JSON.stringify(flagDetail)} allFlags: ${JSON.stringify(allFlags)}`; - // eslint-disable-next-line console.log(`------------- ${resp}`); return new Response(`${resp}`); }, diff --git a/packages/shared/sdk-server/src/events/EventSender.ts b/packages/shared/sdk-server/src/events/EventSender.ts index b8b71397b..036e6a476 100644 --- a/packages/shared/sdk-server/src/events/EventSender.ts +++ b/packages/shared/sdk-server/src/events/EventSender.ts @@ -2,7 +2,6 @@ import { ApplicationTags, ClientContext, Crypto, - LDLogger, Requests, subsystem, } from '@launchdarkly/js-sdk-common'; @@ -28,14 +27,11 @@ export default class EventSender implements subsystem.LDEventSender { private crypto: Crypto; - private logger?: LDLogger; - constructor(config: EventSenderOptions, clientContext: ClientContext) { const { basicConfiguration: { sdkKey, serviceEndpoints: { events }, - logger, }, platform: { info, requests, crypto }, } = clientContext; @@ -44,6 +40,7 @@ export default class EventSender implements subsystem.LDEventSender { ...defaultHeaders(sdkKey, config, info), }; + // edge sdks use clientSideID to send events const isClientSideID = !sdkKey.startsWith('sdk-'); this.eventsUri = isClientSideID ? `${events}/events/bulk/${sdkKey}` : `${events}/bulk`; this.diagnosticEventsUri = isClientSideID @@ -52,7 +49,6 @@ export default class EventSender implements subsystem.LDEventSender { this.requests = requests; this.crypto = crypto; - this.logger = logger; } private async tryPostingEvents( @@ -76,9 +72,6 @@ export default class EventSender implements subsystem.LDEventSender { } let error; try { - this.logger?.error(`===== uri: ${uri}`); - this.logger?.error(`===== headers: ${JSON.stringify(headers)}`); - this.logger?.error(`===== events: ${JSON.stringify(events)}`); const res = await this.requests.fetch(uri, { headers, body: JSON.stringify(events), From 19082277c1b072afc3f4b836b28a00d96d0a7d6e Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Mon, 28 Aug 2023 13:59:00 -0700 Subject: [PATCH 06/21] chore: better support for event sender analytics paths (#259) --- .../src/internal/events/LDInternalOptions.ts | 5 ++++ .../common/src/internal/events/index.ts | 10 ++++++- .../common/src/options/ServiceEndpoints.ts | 29 ++++++++++++++++++- .../sdk-server-edge/src/api/LDClient.ts | 9 ++++-- .../shared/sdk-server/src/LDClientImpl.ts | 3 +- .../src/data_sources/defaultHeaders.ts | 6 ++-- .../sdk-server/src/events/EventSender.ts | 18 ++++++------ .../sdk-server/src/options/Configuration.ts | 5 +++- .../store/node-server-sdk-dynamodb/README.md | 4 ++- 9 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 packages/shared/common/src/internal/events/LDInternalOptions.ts diff --git a/packages/shared/common/src/internal/events/LDInternalOptions.ts b/packages/shared/common/src/internal/events/LDInternalOptions.ts new file mode 100644 index 000000000..63ade435d --- /dev/null +++ b/packages/shared/common/src/internal/events/LDInternalOptions.ts @@ -0,0 +1,5 @@ +export type LDInternalOptions = { + analyticsEventPath?: string; + diagnosticEventPath?: string; + includeAuthorizationHeader?: boolean; +}; diff --git a/packages/shared/common/src/internal/events/index.ts b/packages/shared/common/src/internal/events/index.ts index 7b32527fb..df46697e6 100644 --- a/packages/shared/common/src/internal/events/index.ts +++ b/packages/shared/common/src/internal/events/index.ts @@ -3,5 +3,13 @@ import InputCustomEvent from './InputCustomEvent'; import InputEvalEvent from './InputEvalEvent'; import InputEvent from './InputEvent'; import InputIdentifyEvent from './InputIdentifyEvent'; +import type { LDInternalOptions } from './LDInternalOptions'; -export { InputCustomEvent, InputEvalEvent, InputEvent, InputIdentifyEvent, EventProcessor }; +export { + EventProcessor, + InputCustomEvent, + InputEvalEvent, + InputEvent, + InputIdentifyEvent, + LDInternalOptions, +}; diff --git a/packages/shared/common/src/options/ServiceEndpoints.ts b/packages/shared/common/src/options/ServiceEndpoints.ts index a47cdcc5d..513db850e 100644 --- a/packages/shared/common/src/options/ServiceEndpoints.ts +++ b/packages/shared/common/src/options/ServiceEndpoints.ts @@ -12,9 +12,36 @@ export default class ServiceEndpoints { public readonly events: string; - public constructor(streaming: string, polling: string, events: string) { + /** Valid paths are: + * /bulk + * /events/bulk/envId + * /mobile + */ + public readonly analyticsEventPath: string; + + /** Valid paths are: + * /diagnostic + * /events/diagnostic/envId + * /mobile/events/diagnostic + */ + public readonly diagnosticEventPath: string; + + // if true the sdk key will be included as authorization header + public readonly includeAuthorizationHeader: boolean; + + public constructor( + streaming: string, + polling: string, + events: string, + analyticsEventPath: string = '/bulk', + diagnosticEventPath: string = '/diagnostic', + includeAuthorizationHeader: boolean = true, + ) { this.streaming = canonicalizeUri(streaming); this.polling = canonicalizeUri(polling); this.events = canonicalizeUri(events); + this.analyticsEventPath = analyticsEventPath; + this.diagnosticEventPath = diagnosticEventPath; + this.includeAuthorizationHeader = includeAuthorizationHeader; } } diff --git a/packages/shared/sdk-server-edge/src/api/LDClient.ts b/packages/shared/sdk-server-edge/src/api/LDClient.ts index ed8a06f09..634820262 100644 --- a/packages/shared/sdk-server-edge/src/api/LDClient.ts +++ b/packages/shared/sdk-server-edge/src/api/LDClient.ts @@ -1,6 +1,6 @@ import { EventEmitter } from 'node:events'; -import { Info, LDClientImpl, LDOptions } from '@launchdarkly/js-server-sdk-common'; +import { Info, internal, LDClientImpl, LDOptions } from '@launchdarkly/js-server-sdk-common'; import EdgePlatform from '../platform'; import createCallbacks from './createCallbacks'; @@ -16,7 +16,12 @@ export class LDClient extends LDClientImpl { constructor(clientSideID: string, platformInfo: Info, options: LDOptions) { const em = new EventEmitter(); const platform = new EdgePlatform(platformInfo); - super(clientSideID, platform, createOptions(options), createCallbacks(em)); + const internalOptions: internal.LDInternalOptions = { + analyticsEventPath: `/events/bulk/${clientSideID}`, + diagnosticEventPath: `/events/diagnostic/${clientSideID}`, + includeAuthorizationHeader: false, + }; + super(clientSideID, platform, createOptions(options), createCallbacks(em), internalOptions); this.emitter = em; } } diff --git a/packages/shared/sdk-server/src/LDClientImpl.ts b/packages/shared/sdk-server/src/LDClientImpl.ts index 768252cdb..bd07fabd1 100644 --- a/packages/shared/sdk-server/src/LDClientImpl.ts +++ b/packages/shared/sdk-server/src/LDClientImpl.ts @@ -120,13 +120,14 @@ export default class LDClientImpl implements LDClient { private platform: Platform, options: LDOptions, callbacks: LDClientCallbacks, + internalOptions?: internal.LDInternalOptions, ) { this.onError = callbacks.onError; this.onFailed = callbacks.onFailed; this.onReady = callbacks.onReady; const { onUpdate, hasEventListeners } = callbacks; - const config = new Configuration(options); + const config = new Configuration(options, internalOptions); if (!sdkKey && !config.offline) { throw new Error('You must configure the client with an SDK key'); } diff --git a/packages/shared/sdk-server/src/data_sources/defaultHeaders.ts b/packages/shared/sdk-server/src/data_sources/defaultHeaders.ts index 99ebc9045..7f1eb0e92 100644 --- a/packages/shared/sdk-server/src/data_sources/defaultHeaders.ts +++ b/packages/shared/sdk-server/src/data_sources/defaultHeaders.ts @@ -8,6 +8,7 @@ export default function defaultHeaders( sdkKey: string, config: DefaultHeaderOptions, info: Info, + includeAuthorizationHeader: boolean = true, ): { [key: string]: string } { const sdkData = info.sdkData(); const headers: { [key: string]: string } = { @@ -16,8 +17,9 @@ export default function defaultHeaders( }`, }; - // edge sdks use clientSideID and don't need the authorization header - if (sdkKey.startsWith('sdk-')) { + // edge sdks sets this to false because they use the clientSideID + // and they don't need the authorization header + if (includeAuthorizationHeader) { headers.authorization = sdkKey; } diff --git a/packages/shared/sdk-server/src/events/EventSender.ts b/packages/shared/sdk-server/src/events/EventSender.ts index 036e6a476..1f5d34c0a 100644 --- a/packages/shared/sdk-server/src/events/EventSender.ts +++ b/packages/shared/sdk-server/src/events/EventSender.ts @@ -31,22 +31,22 @@ export default class EventSender implements subsystem.LDEventSender { const { basicConfiguration: { sdkKey, - serviceEndpoints: { events }, + serviceEndpoints: { + events, + analyticsEventPath, + diagnosticEventPath, + includeAuthorizationHeader, + }, }, platform: { info, requests, crypto }, } = clientContext; this.defaultHeaders = { - ...defaultHeaders(sdkKey, config, info), + ...defaultHeaders(sdkKey, config, info, includeAuthorizationHeader), }; - // edge sdks use clientSideID to send events - const isClientSideID = !sdkKey.startsWith('sdk-'); - this.eventsUri = isClientSideID ? `${events}/events/bulk/${sdkKey}` : `${events}/bulk`; - this.diagnosticEventsUri = isClientSideID - ? `${events}/events/diagnostic/${sdkKey}` - : `${events}/diagnostic`; - + this.eventsUri = `${events}${analyticsEventPath}`; + this.diagnosticEventsUri = `${events}${diagnosticEventPath}`; this.requests = requests; this.crypto = crypto; } diff --git a/packages/shared/sdk-server/src/options/Configuration.ts b/packages/shared/sdk-server/src/options/Configuration.ts index 35e4f0af0..a45998b26 100644 --- a/packages/shared/sdk-server/src/options/Configuration.ts +++ b/packages/shared/sdk-server/src/options/Configuration.ts @@ -1,5 +1,6 @@ import { ApplicationTags, + internal, LDClientContext, LDLogger, NumberWithMinimum, @@ -209,7 +210,7 @@ export default class Configuration { public readonly bigSegments?: LDBigSegmentsOptions; - constructor(options: LDOptions = {}) { + constructor(options: LDOptions = {}, internalOptions: internal.LDInternalOptions = {}) { // The default will handle undefined, but not null. // Because we can be called from JS we need to be extra defensive. // eslint-disable-next-line no-param-reassign @@ -228,6 +229,8 @@ export default class Configuration { validatedOptions.streamUri, validatedOptions.baseUri, validatedOptions.eventsUri, + internalOptions.analyticsEventPath, + internalOptions.diagnosticEventPath, ); this.eventsCapacity = validatedOptions.capacity; this.timeout = validatedOptions.timeout; diff --git a/packages/store/node-server-sdk-dynamodb/README.md b/packages/store/node-server-sdk-dynamodb/README.md index 59f9c6917..07aef68e0 100644 --- a/packages/store/node-server-sdk-dynamodb/README.md +++ b/packages/store/node-server-sdk-dynamodb/README.md @@ -52,7 +52,9 @@ const client = LaunchDarkly.init('YOUR SDK KEY', config); By default, the DynamoDB client will try to get your AWS credentials and region name from environment variables and/or local configuration files, as described in the AWS SDK documentation. You can also specify any valid [DynamoDB client options](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#constructor-property) like this: ```typescript -const dynamoDBOptions = { credentials: { accessKeyId: 'YOUR KEY', secretAccessKey: 'YOUR SECRET' }}; +const dynamoDBOptions = { + credentials: { accessKeyId: 'YOUR KEY', secretAccessKey: 'YOUR SECRET' }, +}; const store = DynamoDBFeatureStore('YOUR TABLE NAME', { clientOptions: dynamoDBOptions }); ``` From 11cb96aa4fc69cf8f20449a2ba69e59196fc4177 Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Mon, 28 Aug 2023 14:01:37 -0700 Subject: [PATCH 07/21] chore: rollback unit test sdk- prefix changes. --- .../__tests__/data_sources/Requestor.test.ts | 9 ++------- .../data_sources/defaultHeaders.test.ts | 16 ++++++++-------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/shared/sdk-server/__tests__/data_sources/Requestor.test.ts b/packages/shared/sdk-server/__tests__/data_sources/Requestor.test.ts index c13a1f575..0077d8caa 100644 --- a/packages/shared/sdk-server/__tests__/data_sources/Requestor.test.ts +++ b/packages/shared/sdk-server/__tests__/data_sources/Requestor.test.ts @@ -80,12 +80,7 @@ describe('given a requestor', () => { }, }; - requestor = new Requestor( - 'sdk-my-sdk-Key', - new Configuration({}), - basicPlatform.info, - requests, - ); + requestor = new Requestor('sdkKey', new Configuration({}), basicPlatform.info, requests); }); it('gets data', (done) => { @@ -96,7 +91,7 @@ describe('given a requestor', () => { expect(requestsMade.length).toBe(1); expect(requestsMade[0].url).toBe('https://sdk.launchdarkly.com/sdk/latest-all'); - expect(requestsMade[0].options.headers?.authorization).toBe('sdk-my-sdk-Key'); + expect(requestsMade[0].options.headers?.authorization).toBe('sdkKey'); done(); }); diff --git a/packages/shared/sdk-server/__tests__/data_sources/defaultHeaders.test.ts b/packages/shared/sdk-server/__tests__/data_sources/defaultHeaders.test.ts index f0d0f9fe8..5da91e08b 100644 --- a/packages/shared/sdk-server/__tests__/data_sources/defaultHeaders.test.ts +++ b/packages/shared/sdk-server/__tests__/data_sources/defaultHeaders.test.ts @@ -20,37 +20,37 @@ const makeInfo = (wrapperName?: string, wrapperVersion?: string, userAgentBase?: it('sets SDK key', () => { const config = new Configuration({}); - const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo()); - expect(h).toMatchObject({ authorization: 'sdk-my-sdk-key' }); + const h = defaultHeaders('my-sdk-key', config, makeInfo()); + expect(h).toMatchObject({ authorization: 'my-sdk-key' }); }); it('sets the default user agent', () => { const config = new Configuration({}); - const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo()); + const h = defaultHeaders('my-sdk-key', config, makeInfo()); expect(h).toMatchObject({ 'user-agent': 'NodeJSClient/2.2.2' }); }); it('sets the SDK specific user agent', () => { const config = new Configuration({}); - const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo(undefined, undefined, 'CATS')); + const h = defaultHeaders('my-sdk-key', config, makeInfo(undefined, undefined, 'CATS')); expect(h).toMatchObject({ 'user-agent': 'CATS/2.2.2' }); }); it('does not include wrapper header by default', () => { const config = new Configuration({}); - const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo()); + const h = defaultHeaders('my-sdk-key', config, makeInfo()); expect(h['x-launchdarkly-wrapper']).toBeUndefined(); }); it('sets wrapper header with name only', () => { const config = new Configuration({}); - const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo('my-wrapper')); + const h = defaultHeaders('my-sdk-key', config, makeInfo('my-wrapper')); expect(h).toMatchObject({ 'x-launchdarkly-wrapper': 'my-wrapper' }); }); it('sets wrapper header with name and version', () => { const config = new Configuration({}); - const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo('my-wrapper', '2.0')); + const h = defaultHeaders('my-sdk-key', config, makeInfo('my-wrapper', '2.0')); expect(h).toMatchObject({ 'x-launchdarkly-wrapper': 'my-wrapper/2.0' }); }); @@ -61,7 +61,7 @@ it('sets the X-LaunchDarkly-Tags header with valid tags.', () => { version: 'test-version', }, }); - const h = defaultHeaders('sdk-my-sdk-key', config, makeInfo('my-wrapper')); + const h = defaultHeaders('my-sdk-key', config, makeInfo('my-wrapper')); expect(h).toMatchObject({ 'x-launchdarkly-tags': 'application-id/test-application application-version/test-version', }); From a3d3132e55277e3728bef5bbd6ef761779bffaac Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Mon, 28 Aug 2023 14:11:23 -0700 Subject: [PATCH 08/21] chor: add comments --- .../common/src/internal/events/LDInternalOptions.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/shared/common/src/internal/events/LDInternalOptions.ts b/packages/shared/common/src/internal/events/LDInternalOptions.ts index 63ade435d..898197051 100644 --- a/packages/shared/common/src/internal/events/LDInternalOptions.ts +++ b/packages/shared/common/src/internal/events/LDInternalOptions.ts @@ -1,3 +1,13 @@ +/** + * This is for internal use only. + * + * Edge sdks use clientSideID to query feature stores. They also send analytics + * using this clientSideID. This is a hybrid behavior because they are based + * on js-server-common, but uses the clientSideID instead of the sdkKey for the + * above reasons. These internal options allow the edge sdks to use the + * EventSender to send analytics to the correct LD endpoints using + * the clientSideId. + */ export type LDInternalOptions = { analyticsEventPath?: string; diagnosticEventPath?: string; From 429e92acb1de1b7ae9fd14271976cce0b87910dc Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Mon, 28 Aug 2023 14:23:55 -0700 Subject: [PATCH 09/21] Update packages/shared/sdk-server-edge/src/api/LDClient.ts --- packages/shared/sdk-server-edge/src/api/LDClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/sdk-server-edge/src/api/LDClient.ts b/packages/shared/sdk-server-edge/src/api/LDClient.ts index 634820262..90b9ca40f 100644 --- a/packages/shared/sdk-server-edge/src/api/LDClient.ts +++ b/packages/shared/sdk-server-edge/src/api/LDClient.ts @@ -12,7 +12,7 @@ import createOptions from './createOptions'; export class LDClient extends LDClientImpl { emitter: EventEmitter; - // clientSideID is only used to query featureStore, not to initialize with LD servers + // clientSideID is only used to query the edge key-value store and send analytics, not to initialize with LD servers constructor(clientSideID: string, platformInfo: Info, options: LDOptions) { const em = new EventEmitter(); const platform = new EdgePlatform(platformInfo); From f87120931b2c9d92e2a0d6b87231968f1c104ad3 Mon Sep 17 00:00:00 2001 From: LaunchDarklyReleaseBot Date: Tue, 29 Aug 2023 13:07:46 -0700 Subject: [PATCH 10/21] chore: add release-please alpha config. --- release-please-config.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/release-please-config.json b/release-please-config.json index 63cc82409..164df2f9f 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -7,9 +7,14 @@ "bump-minor-pre-major": true }, "packages/sdk/server-node": {}, - "packages/sdk/cloudflare": {}, + "packages/sdk/cloudflare": { + "bump-minor-pre-major": true, + "release-as": "2.2.0-alpha" + }, "packages/sdk/vercel": { - "extra-files": ["src/createPlatformInfo.ts"] + "extra-files": ["src/createPlatformInfo.ts"], + "bump-minor-pre-major": true, + "release-as": "1.2.0-alpha" }, "packages/sdk/akamai-base": { "extra-files": ["src/index.ts"] From 37ebde71f8ab5f98670894a7486e1304cf5006d6 Mon Sep 17 00:00:00 2001 From: LaunchDarklyReleaseBot Date: Tue, 29 Aug 2023 13:14:25 -0700 Subject: [PATCH 11/21] Update release-please-config.json --- release-please-config.json | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/release-please-config.json b/release-please-config.json index 164df2f9f..63cc82409 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -7,14 +7,9 @@ "bump-minor-pre-major": true }, "packages/sdk/server-node": {}, - "packages/sdk/cloudflare": { - "bump-minor-pre-major": true, - "release-as": "2.2.0-alpha" - }, + "packages/sdk/cloudflare": {}, "packages/sdk/vercel": { - "extra-files": ["src/createPlatformInfo.ts"], - "bump-minor-pre-major": true, - "release-as": "1.2.0-alpha" + "extra-files": ["src/createPlatformInfo.ts"] }, "packages/sdk/akamai-base": { "extra-files": ["src/index.ts"] From ad7d012207585731447b9782b7c85f325595d2eb Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Wed, 30 Aug 2023 16:55:08 -0700 Subject: [PATCH 12/21] chore: flush events in waitUntil. improved handler types. --- packages/sdk/cloudflare/example/src/index.ts | 28 ++++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/sdk/cloudflare/example/src/index.ts b/packages/sdk/cloudflare/example/src/index.ts index d9063d96e..9f66c82f2 100644 --- a/packages/sdk/cloudflare/example/src/index.ts +++ b/packages/sdk/cloudflare/example/src/index.ts @@ -1,8 +1,14 @@ /* eslint-disable no-console */ import { init as initLD } from '@launchdarkly/cloudflare-server-sdk'; -export default { - async fetch(request: Request, env: Bindings): Promise { +// handler types ripped from the cloudflare docs: +// https://developers.cloudflare.com/workers/examples/accessing-the-cloudflare-object/ +const handler: ExportedHandler = { + async fetch( + request: Request, + env: Bindings, + executionContext: ExecutionContext, + ): Promise { const clientSideID = 'client-side-id'; const flagKey = 'testFlag1'; const context = { kind: 'org', key: 'org-key-cf', email: 'testcforg@gmail.com' }; @@ -14,18 +20,24 @@ export default { const flagDetail = await client.variationDetail(flagKey, context, false); const allFlags = await client.allFlagsState(context); - // Gotcha: you must call flush otherwise events will not be sent to LD servers - // due to the ephemeral nature of edge workers. - await client.flush((err, res) => { - console.log(`flushed events result: ${res}, error: ${err}`); - }); - const resp = ` ${flagKey}: ${flagValue} detail: ${JSON.stringify(flagDetail)} allFlags: ${JSON.stringify(allFlags)}`; console.log(`------------- ${resp}`); + + // Gotcha: you must call flush otherwise events will not be sent to LD servers + // due to the ephemeral nature of edge workers. + // https://developers.cloudflare.com/workers/runtime-apis/fetch-event/#waituntil + executionContext.waitUntil( + client.flush((err, res) => { + console.log(`flushed events result: ${res}, error: ${err}`); + }), + ); + return new Response(`${resp}`); }, }; + +export default handler; From 44c80444946ab69870bf1ca2cbf7d532ba655958 Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Thu, 21 Sep 2023 17:28:46 -0700 Subject: [PATCH 13/21] fix: remove link-dev and use yarn workspaces. --- link-dev.sh | 15 --------------- package.json | 4 ++-- 2 files changed, 2 insertions(+), 17 deletions(-) delete mode 100755 link-dev.sh diff --git a/link-dev.sh b/link-dev.sh deleted file mode 100755 index afa008c11..000000000 --- a/link-dev.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -echo "===== Linking to cloudflare/example" - -rm -rf packages/sdk/cloudflare/example/node_modules/@launchdarkly/js-sdk-common/dist -cp -r packages/shared/common/dist packages/sdk/cloudflare/example/node_modules/@launchdarkly/js-sdk-common/dist - -rm -rf packages/sdk/cloudflare/example/node_modules/@launchdarkly/js-server-sdk-common/dist -cp -r packages/shared/sdk-server/dist packages/sdk/cloudflare/example/node_modules/@launchdarkly/js-server-sdk-common/dist - -rm -rf packages/sdk/cloudflare/example/node_modules/@launchdarkly/js-server-sdk-common-edge/dist -cp -r packages/shared/sdk-server-edge/dist packages/sdk/cloudflare/example/node_modules/@launchdarkly/js-server-sdk-common-edge/dist - -rm -rf packages/sdk/cloudflare/example/node_modules/@launchdarkly/cloudflare-server-sdk/dist -cp -r packages/sdk/cloudflare/dist packages/sdk/cloudflare/example/node_modules/@launchdarkly/cloudflare-server-sdk/dist diff --git a/package.json b/package.json index f1d88f9dd..aa90bac2a 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "packages/shared/akamai-edgeworker-sdk", "packages/sdk/server-node", "packages/sdk/cloudflare", + "packages/sdk/cloudflare/example", "packages/sdk/vercel", "packages/sdk/akamai-base", "packages/sdk/akamai-base/example", @@ -29,8 +30,7 @@ "contract-test-harness": "curl -s https://raw.githubusercontent.com/launchdarkly/sdk-test-harness/master/downloader/run.sh \\ | VERSION=v2 PARAMS=\"-url http://localhost:8000 -debug -stop-service-at-end $TEST_HARNESS_PARAMS\" sh", "contract-tests": "npm run contract-test-service & npm run contract-test-harness", "prettier": "npx prettier --write \"**/*.{js,ts,tsx,json,yaml,yml,md}\" --log-level warn", - "check": "yarn && yarn prettier && yarn lint && tsc && yarn build", - "link-dev": "./link-dev.sh" + "check": "yarn && yarn prettier && yarn lint && tsc && yarn build" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.1.1", From 2abeb1367f6f4d20b7304da50bac294aa79c4981 Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Fri, 22 Sep 2023 12:58:46 -0700 Subject: [PATCH 14/21] fix: unit tests due to missing ctx. update jest version. --- packages/sdk/cloudflare/example/package.json | 4 ++-- packages/sdk/cloudflare/example/src/index.ts | 21 +++++++------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/packages/sdk/cloudflare/example/package.json b/packages/sdk/cloudflare/example/package.json index 25ab3aa7c..942fa3f94 100644 --- a/packages/sdk/cloudflare/example/package.json +++ b/packages/sdk/cloudflare/example/package.json @@ -9,9 +9,9 @@ }, "devDependencies": { "@cloudflare/workers-types": "^4.20230321.0", - "@types/jest": "^27.5.1", + "@types/jest": "^29.5.5", "esbuild": "^0.14.41", - "jest": "^28.1.0", + "jest": "^29.7.0", "jest-environment-miniflare": "^2.5.0", "miniflare": "^2.5.0", "prettier": "^2.6.2", diff --git a/packages/sdk/cloudflare/example/src/index.ts b/packages/sdk/cloudflare/example/src/index.ts index 49a459a06..49f2eed78 100644 --- a/packages/sdk/cloudflare/example/src/index.ts +++ b/packages/sdk/cloudflare/example/src/index.ts @@ -1,15 +1,9 @@ /* eslint-disable no-console */ import { init as initLD } from '@launchdarkly/cloudflare-server-sdk'; -// handler types ripped from the cloudflare docs: -// https://developers.cloudflare.com/workers/examples/accessing-the-cloudflare-object/ -const handler: ExportedHandler = { - async fetch( - request: Request, - env: Bindings, - executionContext: ExecutionContext, - ): Promise { - const clientSideID = 'client-side-id'; +export default { + async fetch(request: Request, env: Bindings, ctx?: ExecutionContext): Promise { + const sdkKey = 'test-sdk-key'; const flagKey = 'testFlag1'; const { searchParams } = new URL(request.url); @@ -18,7 +12,7 @@ const handler: ExportedHandler = { const context = { kind: 'user', key: 'test-user-key-1', email }; // start using ld - const client = initLD(clientSideID, env.LD_KV, { sendEvents: true }); + const client = initLD(sdkKey, env.LD_KV, { sendEvents: true }); await client.waitForInitialization(); const flagValue = await client.variation(flagKey, context, false); const flagDetail = await client.variationDetail(flagKey, context, false); @@ -34,14 +28,13 @@ const handler: ExportedHandler = { // Gotcha: you must call flush otherwise events will not be sent to LD servers // due to the ephemeral nature of edge workers. // https://developers.cloudflare.com/workers/runtime-apis/fetch-event/#waituntil - executionContext.waitUntil( + ctx?.waitUntil( + // @ts-ignore client.flush((err, res) => { console.log(`flushed events result: ${res}, error: ${err}`); + client.close(); }), ); - return new Response(`${resp}`); }, }; - -export default handler; From 8f1ad45943553d7ea63593a47c64c3edc2a0a98a Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Fri, 22 Sep 2023 12:59:52 -0700 Subject: [PATCH 15/21] chore: fix flush callback arg types. --- packages/sdk/cloudflare/example/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk/cloudflare/example/src/index.ts b/packages/sdk/cloudflare/example/src/index.ts index 49f2eed78..f3a249fc6 100644 --- a/packages/sdk/cloudflare/example/src/index.ts +++ b/packages/sdk/cloudflare/example/src/index.ts @@ -29,12 +29,12 @@ export default { // due to the ephemeral nature of edge workers. // https://developers.cloudflare.com/workers/runtime-apis/fetch-event/#waituntil ctx?.waitUntil( - // @ts-ignore - client.flush((err, res) => { + client.flush((err: Error, res: boolean) => { console.log(`flushed events result: ${res}, error: ${err}`); client.close(); }), ); + return new Response(`${resp}`); }, }; From 8b5c0d9f5ab4d3403e8fd37bef73a4a3fbdf4f15 Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Fri, 22 Sep 2023 14:25:36 -0700 Subject: [PATCH 16/21] fix: close client to fix jest open handle warnings --- .../sdk/cloudflare/example/src/index.test.ts | 25 +++++++++++++++++-- packages/sdk/cloudflare/example/src/index.ts | 6 ++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/sdk/cloudflare/example/src/index.test.ts b/packages/sdk/cloudflare/example/src/index.test.ts index 0ada18236..4ae6b27c8 100644 --- a/packages/sdk/cloudflare/example/src/index.test.ts +++ b/packages/sdk/cloudflare/example/src/index.test.ts @@ -3,20 +3,41 @@ import testData from './testData.json'; describe('test', () => { let env: Bindings; + let mockExecutionContext: ExecutionContext; beforeEach(async () => { + // solves jest complaining about console.log in flush after exiting + // eslint-disable-next-line no-console + console.log = jest.fn(); + + mockExecutionContext = { + waitUntil: jest.fn(), + passThroughOnException: jest.fn(), + }; env = getMiniflareBindings(); const { LD_KV } = env; await LD_KV.put('LD-Env-test-sdk-key', JSON.stringify(testData)); }); + afterEach(() => { + jest.resetAllMocks(); + }); + test('variation true', async () => { - const res = await app.fetch(new Request('http://localhost/?email=truemail'), env); + const res = await app.fetch( + new Request('http://localhost/?email=truemail'), + env, + mockExecutionContext, + ); expect(await res.text()).toContain('testFlag1: true'); }); test('variation false', async () => { - const res = await app.fetch(new Request('http://localhost/?email=falsemail'), env); + const res = await app.fetch( + new Request('http://localhost/?email=falsemail'), + env, + mockExecutionContext, + ); expect(await res.text()).toContain('testFlag1: false'); }); }); diff --git a/packages/sdk/cloudflare/example/src/index.ts b/packages/sdk/cloudflare/example/src/index.ts index f3a249fc6..c2d521add 100644 --- a/packages/sdk/cloudflare/example/src/index.ts +++ b/packages/sdk/cloudflare/example/src/index.ts @@ -2,7 +2,7 @@ import { init as initLD } from '@launchdarkly/cloudflare-server-sdk'; export default { - async fetch(request: Request, env: Bindings, ctx?: ExecutionContext): Promise { + async fetch(request: Request, env: Bindings, ctx: ExecutionContext): Promise { const sdkKey = 'test-sdk-key'; const flagKey = 'testFlag1'; const { searchParams } = new URL(request.url); @@ -28,8 +28,8 @@ export default { // Gotcha: you must call flush otherwise events will not be sent to LD servers // due to the ephemeral nature of edge workers. // https://developers.cloudflare.com/workers/runtime-apis/fetch-event/#waituntil - ctx?.waitUntil( - client.flush((err: Error, res: boolean) => { + ctx.waitUntil( + client.flush((err: Error | null, res: boolean) => { console.log(`flushed events result: ${res}, error: ${err}`); client.close(); }), From aae16d80a475f1af42eb1a2db6e4456d79c547b3 Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Tue, 7 Nov 2023 12:46:54 -0800 Subject: [PATCH 17/21] chore: resolve conflicts. --- .../src/internal/events/EventSender.test.ts | 9 ++++++++- .../common/src/internal/events/EventSender.ts | 17 +++++++++++++---- .../shared/common/src/internal/events/index.ts | 2 +- packages/shared/common/src/utils/http.ts | 16 +++++++++++++--- packages/shared/mocks/src/clientContext.ts | 9 ++++++++- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/packages/shared/common/src/internal/events/EventSender.test.ts b/packages/shared/common/src/internal/events/EventSender.test.ts index d90d4daba..8d06db277 100644 --- a/packages/shared/common/src/internal/events/EventSender.test.ts +++ b/packages/shared/common/src/internal/events/EventSender.test.ts @@ -12,7 +12,14 @@ jest.mock('../../utils', () => { const basicConfig = { tags: new ApplicationTags({ application: { id: 'testApplication1', version: '1.0.0' } }), - serviceEndpoints: { events: 'https://events.fake.com', streaming: '', polling: '' }, + serviceEndpoints: { + events: 'https://events.fake.com', + streaming: '', + polling: '', + analyticsEventPath: '/bulk', + diagnosticEventPath: '/diagnostic', + includeAuthorizationHeader: true, + }, }; const testEventData1 = { eventId: 'test-event-data-1' }; const testEventData2 = { eventId: 'test-event-data-2' }; diff --git a/packages/shared/common/src/internal/events/EventSender.ts b/packages/shared/common/src/internal/events/EventSender.ts index 5a49347d0..c61fe4611 100644 --- a/packages/shared/common/src/internal/events/EventSender.ts +++ b/packages/shared/common/src/internal/events/EventSender.ts @@ -20,12 +20,21 @@ export default class EventSender implements LDEventSender { constructor(clientContext: ClientContext) { const { basicConfiguration, platform } = clientContext; - const { sdkKey, serviceEndpoints, tags } = basicConfiguration; + const { + sdkKey, + serviceEndpoints: { + events, + analyticsEventPath, + diagnosticEventPath, + includeAuthorizationHeader, + }, + tags, + } = basicConfiguration; const { crypto, info, requests } = platform; - this.defaultHeaders = defaultHeaders(sdkKey, info, tags); - this.eventsUri = `${serviceEndpoints.events}/bulk`; - this.diagnosticEventsUri = `${serviceEndpoints.events}/diagnostic`; + this.defaultHeaders = defaultHeaders(sdkKey, info, tags, includeAuthorizationHeader); + this.eventsUri = `${events}${analyticsEventPath}`; + this.diagnosticEventsUri = `${events}${diagnosticEventPath}`; this.requests = requests; this.crypto = crypto; } diff --git a/packages/shared/common/src/internal/events/index.ts b/packages/shared/common/src/internal/events/index.ts index b3c6132fb..27035ef5e 100644 --- a/packages/shared/common/src/internal/events/index.ts +++ b/packages/shared/common/src/internal/events/index.ts @@ -5,9 +5,9 @@ import InputEvalEvent from './InputEvalEvent'; import InputEvent from './InputEvent'; import InputIdentifyEvent from './InputIdentifyEvent'; import InputMigrationEvent from './InputMigrationEvent'; +import type { LDInternalOptions } from './LDInternalOptions'; import NullEventProcessor from './NullEventProcessor'; import shouldSample from './sampling'; -import type { LDInternalOptions } from './LDInternalOptions'; export { ClientMessages, diff --git a/packages/shared/common/src/utils/http.ts b/packages/shared/common/src/utils/http.ts index c29ea4759..a6f8ad2dc 100644 --- a/packages/shared/common/src/utils/http.ts +++ b/packages/shared/common/src/utils/http.ts @@ -2,20 +2,30 @@ import { Info } from '../api'; import { ApplicationTags } from '../options'; export type LDHeaders = { - authorization: string; + authorization?: string; 'user-agent': string; 'x-launchdarkly-wrapper'?: string; 'x-launchdarkly-tags'?: string; }; -export function defaultHeaders(sdkKey: string, info: Info, tags?: ApplicationTags): LDHeaders { +export function defaultHeaders( + sdkKey: string, + info: Info, + tags?: ApplicationTags, + includeAuthorizationHeader: boolean = true, +): LDHeaders { const { userAgentBase, version, wrapperName, wrapperVersion } = info.sdkData(); const headers: LDHeaders = { - authorization: sdkKey, 'user-agent': `${userAgentBase ?? 'NodeJSClient'}/${version}`, }; + // edge sdks sets this to false because they use the clientSideID + // and they don't need the authorization header + if (includeAuthorizationHeader) { + headers.authorization = sdkKey; + } + if (wrapperName) { headers['x-launchdarkly-wrapper'] = wrapperVersion ? `${wrapperName}/${wrapperVersion}` diff --git a/packages/shared/mocks/src/clientContext.ts b/packages/shared/mocks/src/clientContext.ts index aa14729f5..90baaefe7 100644 --- a/packages/shared/mocks/src/clientContext.ts +++ b/packages/shared/mocks/src/clientContext.ts @@ -5,7 +5,14 @@ import basicPlatform from './platform'; const clientContext: ClientContext = { basicConfiguration: { sdkKey: 'testSdkKey', - serviceEndpoints: { events: '', polling: '', streaming: 'https://mockstream.ld.com' }, + serviceEndpoints: { + events: '', + polling: '', + streaming: 'https://mockstream.ld.com', + diagnosticEventPath: '/diagnostic', + analyticsEventPath: '/bulk', + includeAuthorizationHeader: true, + }, }, platform: basicPlatform, }; From 2acad7085393caf60542d01260d86387ab29e4ce Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Tue, 7 Nov 2023 13:05:01 -0800 Subject: [PATCH 18/21] chore: in cloudflare example, rename sdkKey to clientSideID. --- packages/sdk/cloudflare/example/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk/cloudflare/example/src/index.ts b/packages/sdk/cloudflare/example/src/index.ts index c2d521add..8c44228cb 100644 --- a/packages/sdk/cloudflare/example/src/index.ts +++ b/packages/sdk/cloudflare/example/src/index.ts @@ -3,7 +3,7 @@ import { init as initLD } from '@launchdarkly/cloudflare-server-sdk'; export default { async fetch(request: Request, env: Bindings, ctx: ExecutionContext): Promise { - const sdkKey = 'test-sdk-key'; + const clientSideID = 'test-client-side-id'; const flagKey = 'testFlag1'; const { searchParams } = new URL(request.url); @@ -12,7 +12,7 @@ export default { const context = { kind: 'user', key: 'test-user-key-1', email }; // start using ld - const client = initLD(sdkKey, env.LD_KV, { sendEvents: true }); + const client = initLD(clientSideID, env.LD_KV, { sendEvents: true }); await client.waitForInitialization(); const flagValue = await client.variation(flagKey, context, false); const flagDetail = await client.variationDetail(flagKey, context, false); From 5da020e829cc657333db06b04d2cdd0efb7a5460 Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Wed, 8 Nov 2023 12:28:53 -0800 Subject: [PATCH 19/21] chore: added unit tests for edge sdk. --- packages/shared/sdk-server-edge/package.json | 1 + .../sdk-server-edge/src/api/LDClient.test.ts | 41 +++++++++++++++++++ .../sdk-server-edge/src/api/LDClient.ts | 4 +- .../shared/sdk-server-edge/src/api/index.ts | 4 +- .../sdk-server/src/options/Configuration.ts | 1 + 5 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 packages/shared/sdk-server-edge/src/api/LDClient.test.ts diff --git a/packages/shared/sdk-server-edge/package.json b/packages/shared/sdk-server-edge/package.json index 578d201b7..cc8cfae80 100644 --- a/packages/shared/sdk-server-edge/package.json +++ b/packages/shared/sdk-server-edge/package.json @@ -40,6 +40,7 @@ "crypto-js": "^4.1.1" }, "devDependencies": { + "@launchdarkly/private-js-mocks": "0.0.1", "@trivago/prettier-plugin-sort-imports": "^4.1.1", "@types/crypto-js": "^4.1.1", "@types/jest": "^29.5.0", diff --git a/packages/shared/sdk-server-edge/src/api/LDClient.test.ts b/packages/shared/sdk-server-edge/src/api/LDClient.test.ts new file mode 100644 index 000000000..fe7949811 --- /dev/null +++ b/packages/shared/sdk-server-edge/src/api/LDClient.test.ts @@ -0,0 +1,41 @@ +import { internal } from '@launchdarkly/js-server-sdk-common'; +import { basicPlatform } from '@launchdarkly/private-js-mocks'; + +import LDClient from './LDClient'; + +jest.mock('@launchdarkly/js-sdk-common', () => { + const actual = jest.requireActual('@launchdarkly/js-sdk-common'); + return { + ...actual, + ...{ + internal: { + ...actual.internal, + DiagnosticsManager: jest.fn(), + EventProcessor: jest.fn(), + }, + }, + }; +}); + +const mockEventProcessor = internal.EventProcessor as jest.Mock; +describe('Edge LDClient', () => { + it('uses clientSideID endpoints', async () => { + const client = new LDClient('client-side-id', basicPlatform.info, { + sendEvents: true, + }); + await client.waitForInitialization(); + const passedConfig = mockEventProcessor.mock.calls[0][0]; + + expect(passedConfig).toMatchObject({ + sendEvents: true, + serviceEndpoints: { + includeAuthorizationHeader: false, + analyticsEventPath: '/events/bulk/client-side-id', + diagnosticEventPath: '/events/diagnostic/client-side-id', + events: 'https://events.launchdarkly.com', + polling: 'https://sdk.launchdarkly.com', + streaming: 'https://stream.launchdarkly.com', + }, + }); + }); +}); diff --git a/packages/shared/sdk-server-edge/src/api/LDClient.ts b/packages/shared/sdk-server-edge/src/api/LDClient.ts index 90b9ca40f..60a978d4b 100644 --- a/packages/shared/sdk-server-edge/src/api/LDClient.ts +++ b/packages/shared/sdk-server-edge/src/api/LDClient.ts @@ -9,7 +9,7 @@ import createOptions from './createOptions'; /** * The LaunchDarkly SDK edge client object. */ -export class LDClient extends LDClientImpl { +export default class LDClient extends LDClientImpl { emitter: EventEmitter; // clientSideID is only used to query the edge key-value store and send analytics, not to initialize with LD servers @@ -25,5 +25,3 @@ export class LDClient extends LDClientImpl { this.emitter = em; } } - -export default LDClient; diff --git a/packages/shared/sdk-server-edge/src/api/index.ts b/packages/shared/sdk-server-edge/src/api/index.ts index b72690fe3..c4ae612f9 100644 --- a/packages/shared/sdk-server-edge/src/api/index.ts +++ b/packages/shared/sdk-server-edge/src/api/index.ts @@ -1,2 +1,4 @@ +import LDClient from './LDClient'; + export * from './EdgeFeatureStore'; -export * from './LDClient'; +export { LDClient }; diff --git a/packages/shared/sdk-server/src/options/Configuration.ts b/packages/shared/sdk-server/src/options/Configuration.ts index 47c9ff4ef..709216d18 100644 --- a/packages/shared/sdk-server/src/options/Configuration.ts +++ b/packages/shared/sdk-server/src/options/Configuration.ts @@ -229,6 +229,7 @@ export default class Configuration { validatedOptions.eventsUri, internalOptions.analyticsEventPath, internalOptions.diagnosticEventPath, + internalOptions.includeAuthorizationHeader, ); this.eventsCapacity = validatedOptions.capacity; this.timeout = validatedOptions.timeout; From 474cdf706d7625fdc4497a0176f6e28dc139cf98 Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Wed, 8 Nov 2023 12:55:26 -0800 Subject: [PATCH 20/21] chore: update cloudflare sdk dep version --- packages/sdk/cloudflare/example/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/cloudflare/example/package.json b/packages/sdk/cloudflare/example/package.json index 776713a30..7434c52e1 100644 --- a/packages/sdk/cloudflare/example/package.json +++ b/packages/sdk/cloudflare/example/package.json @@ -5,7 +5,7 @@ "module": "./dist/index.mjs", "packageManager": "yarn@3.4.1", "dependencies": { - "@launchdarkly/cloudflare-server-sdk": "^2.1.4" + "@launchdarkly/cloudflare-server-sdk": "2.2.3" }, "devDependencies": { "@cloudflare/workers-types": "^4.20230321.0", From 55bf9afa3d4415f8d41354fdb6c03be2edf7b4ab Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Wed, 8 Nov 2023 13:55:22 -0800 Subject: [PATCH 21/21] chore: set test data trackEvents to true --- packages/sdk/cloudflare/example/src/testData.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/sdk/cloudflare/example/src/testData.json b/packages/sdk/cloudflare/example/src/testData.json index d4fbacd7b..2c02011ec 100644 --- a/packages/sdk/cloudflare/example/src/testData.json +++ b/packages/sdk/cloudflare/example/src/testData.json @@ -18,7 +18,7 @@ "negate": false } ], - "trackEvents": false, + "trackEvents": true, "rollout": { "bucketBy": "bucket", "variations": [{ "variation": 1, "weight": 100 }] @@ -36,7 +36,7 @@ }, "clientSide": true, "salt": "aef830243d6640d0a973be89988e008d", - "trackEvents": false, + "trackEvents": true, "trackEventsFallthrough": false, "debugEventsUntilDate": null, "version": 2, @@ -65,7 +65,7 @@ }, "clientSide": true, "salt": "aef830243d6640d0a973be89988e008d", - "trackEvents": false, + "trackEvents": true, "trackEventsFallthrough": false, "debugEventsUntilDate": null, "version": 2, @@ -87,7 +87,7 @@ "negate": false } ], - "trackEvents": false + "trackEvents": true } ], "fallthrough": { @@ -101,7 +101,7 @@ }, "clientSide": true, "salt": "aef830243d6640d0a973be89988e008d", - "trackEvents": false, + "trackEvents": true, "trackEventsFallthrough": false, "debugEventsUntilDate": null, "version": 2,