diff --git a/.github/workflows/server-node.yml b/.github/workflows/server-node.yml index 461f20d702..772f8404c9 100644 --- a/.github/workflows/server-node.yml +++ b/.github/workflows/server-node.yml @@ -38,16 +38,12 @@ jobs: - name: Launch the test service in the background run: yarn contract-test-service 2>&1 & - name: Clone and run contract tests from feat/fdv2 branch - # NOTE: This is a temporary workaround to run the contract tests from the feat/fdv2 branch. - # The commit hash is a few commits before the HEAD of the feat/fdv2 branch. We do this because - # the HEAD of the feat/fdv2 branch requires the synchronizers be passed as a list which is not - # implemented yet. (SDK-1798) run: | mkdir -p /tmp/sdk-test-harness git clone https://github.com/launchdarkly/sdk-test-harness.git /tmp/sdk-test-harness cp ./contract-tests/testharness-suppressions-fdv2.txt /tmp/sdk-test-harness/testharness-suppressions-fdv2.txt cd /tmp/sdk-test-harness - git checkout de833af990da23a89b66c5366809b5be8c27e3f8 + git checkout feat/fdv2 go build -o test-harness . ./test-harness -url http://localhost:8000 -debug --skip-from=testharness-suppressions-fdv2.txt env: diff --git a/contract-tests/src/sdkClientEntity.ts b/contract-tests/src/sdkClientEntity.ts index 01c4d26891..2c061f05f3 100644 --- a/contract-tests/src/sdkClientEntity.ts +++ b/contract-tests/src/sdkClientEntity.ts @@ -14,6 +14,8 @@ import ld, { LDOptions, LDSerialExecution, LDUser, + PollingDataSourceConfiguration, + StreamingDataSourceConfiguration, } from '@launchdarkly/node-server-sdk'; import BigSegmentTestStore from './BigSegmentTestStore.js'; @@ -36,7 +38,7 @@ interface SdkConfigOptions { }; dataSystem?: { initializers?: SDKDataSystemInitializerParams[]; - synchronizers?: SDKDataSystemSynchronizerParams; + synchronizers?: SDKDataSystemSynchronizerParams[]; payloadFilter?: string; }; events?: { @@ -74,14 +76,8 @@ interface SdkConfigOptions { } export interface SDKDataSystemSynchronizerParams { - primary?: { - streaming?: SDKDataSourceStreamingParams; - polling?: SDKDataSourcePollingParams; - }; - secondary?: { - streaming?: SDKDataSourceStreamingParams; - polling?: SDKDataSourcePollingParams; - }; + streaming?: SDKDataSourceStreamingParams; + polling?: SDKDataSourcePollingParams; } export interface SDKDataSystemInitializerParams { @@ -222,52 +218,54 @@ export function makeSdkConfig(options: SdkConfigOptions, tag: string): LDOptions } if (options.dataSystem) { - const dataSourceStreamingOptions: SDKDataSourceStreamingParams | undefined = - options.dataSystem.synchronizers?.primary?.streaming ?? - options.dataSystem.synchronizers?.secondary?.streaming; - const dataSourcePollingOptions: SDKDataSourcePollingParams | undefined = - options.dataSystem.initializers?.[0]?.polling ?? - options.dataSystem.synchronizers?.primary?.polling ?? - options.dataSystem.synchronizers?.secondary?.polling; - - if (dataSourceStreamingOptions) { - cf.streamUri = dataSourceStreamingOptions.baseUri; - cf.streamInitialReconnectDelay = maybeTime(dataSourceStreamingOptions.initialRetryDelayMs); - } - if (dataSourcePollingOptions) { - cf.stream = false; - cf.baseUri = dataSourcePollingOptions.baseUri; - cf.pollInterval = maybeTime(dataSourcePollingOptions.pollIntervalMs); + const dataSourceOptions: DataSourceOptions = { + dataSourceOptionsType: 'custom', + initializers: [], + synchronizers: [], + }; + + if (options.dataSystem.initializers) { + options.dataSystem.initializers.forEach((initializer) => { + if (initializer.polling) { + cf.baseUri = initializer.polling.baseUri; + cf.pollInterval = maybeTime(initializer.polling.pollIntervalMs); + + const initializerOptions: PollingDataSourceConfiguration = { + type: 'polling', + pollInterval: maybeTime(initializer.polling.pollIntervalMs), + }; + + dataSourceOptions.initializers.push(initializerOptions); + } + }); } - let dataSourceOptions: DataSourceOptions | undefined; - if (dataSourceStreamingOptions && dataSourcePollingOptions) { - dataSourceOptions = { - dataSourceOptionsType: 'standard', - ...(dataSourceStreamingOptions.initialRetryDelayMs != null && { - streamInitialReconnectDelay: maybeTime(dataSourceStreamingOptions.initialRetryDelayMs), - }), - ...(dataSourcePollingOptions.pollIntervalMs != null && { - pollInterval: dataSourcePollingOptions.pollIntervalMs, - }), - }; - } else if (dataSourceStreamingOptions) { - dataSourceOptions = { - dataSourceOptionsType: 'streamingOnly', - ...(dataSourceStreamingOptions.initialRetryDelayMs != null && { - streamInitialReconnectDelay: maybeTime(dataSourceStreamingOptions.initialRetryDelayMs), - }), - }; - } else if (dataSourcePollingOptions) { - dataSourceOptions = { - dataSourceOptionsType: 'pollingOnly', - ...(dataSourcePollingOptions.pollIntervalMs != null && { - pollInterval: dataSourcePollingOptions.pollIntervalMs, - }), - }; - } else { - // No data source options were specified - dataSourceOptions = undefined; + if (options.dataSystem.synchronizers) { + options.dataSystem.synchronizers.forEach((synchronizer) => { + if (synchronizer.streaming) { + cf.streamUri = synchronizer.streaming.baseUri; + + const synchronizerOptions: StreamingDataSourceConfiguration = { + type: 'streaming', + }; + + synchronizerOptions.streamInitialReconnectDelay = maybeTime( + synchronizer.streaming.initialRetryDelayMs, + ); + + dataSourceOptions.synchronizers.push(synchronizerOptions); + } else if (synchronizer.polling) { + cf.baseUri = synchronizer.polling.baseUri; + cf.pollInterval = maybeTime(synchronizer.polling.pollIntervalMs); + + const synchronizerOptions: PollingDataSourceConfiguration = { + type: 'polling', + pollInterval: maybeTime(synchronizer.polling.pollIntervalMs), + }; + + dataSourceOptions.synchronizers.push(synchronizerOptions); + } + }); } if (options.dataSystem.payloadFilter) { diff --git a/contract-tests/testharness-suppressions-fdv2.txt b/contract-tests/testharness-suppressions-fdv2.txt index 173fb7d30d..8215293c1f 100644 --- a/contract-tests/testharness-suppressions-fdv2.txt +++ b/contract-tests/testharness-suppressions-fdv2.txt @@ -5,7 +5,6 @@ streaming/requests/URL path is computed correctly/environment_filter_key="encodi polling/requests/URL path is computed correctly/environment_filter_key="encoding_not_necessary"/base URI has no trailing slash/GET polling/requests/URL path is computed correctly/environment_filter_key="encoding_not_necessary"/base URI has a trailing slash/GET -streaming/fdv2/reconnection state management/initializes from polling initializer streaming/fdv2/reconnection state management/initializes from 2 polling initializers streaming/fdv2/reconnection state management/saves previously known state streaming/fdv2/reconnection state management/replaces previously known state diff --git a/packages/shared/sdk-server/src/LDClientImpl.ts b/packages/shared/sdk-server/src/LDClientImpl.ts index 97205d287b..0ea543efb9 100644 --- a/packages/shared/sdk-server/src/LDClientImpl.ts +++ b/packages/shared/sdk-server/src/LDClientImpl.ts @@ -437,19 +437,19 @@ function constructFDv2( ), ); } - - // This is short term handling and will be removed once FDv2 adoption is sufficient. - fdv1FallbackSynchronizers.push( - () => - new PollingProcessorFDv2( - new Requestor(config, platform.requests, baseHeaders, '/sdk/latest-all', config.logger), - pollingInterval, - config.logger, - true, - ), - ); } + // This is short term handling and will be removed once FDv2 adoption is sufficient. + fdv1FallbackSynchronizers.push( + () => + new PollingProcessorFDv2( + new Requestor(config, platform.requests, baseHeaders, '/sdk/latest-all', config.logger), + config.pollInterval ?? DEFAULT_POLL_INTERVAL, + config.logger, + true, + ), + ); + dataSource = new CompositeDataSource( initializers, synchronizers,