1- import { createClient , LDClient , LDLogger , LDOptions } from '@launchdarkly/js-client-sdk' ;
1+ import { createClient , LDOptions } from '@launchdarkly/js-client-sdk' ;
22import {
3- CommandParams ,
4- CommandType ,
3+ ClientEntity ,
54 CreateInstanceParams ,
6- makeLogger ,
7- SDKConfigParams ,
8- ClientSideTestHook as TestHook ,
9- ValueType ,
5+ IClientEntity ,
6+ makeDefaultInitialContext ,
7+ makeSdkConfig ,
108} from '@launchdarkly/js-contract-test-utils/client' ;
119
12- export const badCommandError = new Error ( 'unsupported command' ) ;
13- export const malformedCommand = new Error ( 'command was malformed' ) ;
14-
15- function makeSdkConfig ( options : SDKConfigParams , tag : string ) {
16- if ( ! options . clientSide ) {
17- throw new Error ( 'configuration did not include clientSide options' ) ;
18- }
19-
20- const isSet = ( x ?: unknown ) => x !== null && x !== undefined ;
21- const maybeTime = ( seconds ?: number ) => ( isSet ( seconds ) ? seconds / 1000 : undefined ) ;
22-
23- const cf : LDOptions = {
24- withReasons : options . clientSide . evaluationReasons ,
25- logger : makeLogger ( `${ tag } .sdk` ) ,
26- useReport : options . clientSide . useReport ,
27- } ;
28-
29- if ( options . serviceEndpoints ) {
30- cf . streamUri = options . serviceEndpoints . streaming ;
31- cf . baseUri = options . serviceEndpoints . polling ;
32- cf . eventsUri = options . serviceEndpoints . events ;
33- }
34-
35- if ( options . polling ) {
36- if ( options . polling . baseUri ) {
37- cf . baseUri = options . polling . baseUri ;
38- }
39- }
40-
41- // Can contain streaming and polling, if streaming is set override the initial connection
42- // mode. This can be removed when we add JS specific initialization that uses polling
43- // and then streaming.
44- if ( options . streaming ) {
45- if ( options . streaming . baseUri ) {
46- cf . streamUri = options . streaming . baseUri ;
47- }
48- cf . streaming = true ;
49- cf . streamInitialReconnectDelay = maybeTime ( options . streaming . initialRetryDelayMs ) ;
50- }
51-
52- if ( options . events ) {
53- if ( options . events . baseUri ) {
54- cf . eventsUri = options . events . baseUri ;
55- }
56- cf . allAttributesPrivate = options . events . allAttributesPrivate ;
57- cf . capacity = options . events . capacity ;
58- cf . diagnosticOptOut = ! options . events . enableDiagnostics ;
59- cf . flushInterval = maybeTime ( options . events . flushIntervalMs ) ;
60- cf . privateAttributes = options . events . globalPrivateAttributes ;
61- } else {
62- cf . sendEvents = false ;
63- }
64-
65- if ( options . tags ) {
66- cf . applicationInfo = {
67- id : options . tags . applicationId ,
68- version : options . tags . applicationVersion ,
69- } ;
70- }
71-
72- if ( options . hooks ) {
73- cf . hooks = options . hooks . hooks . map (
74- ( hook ) => new TestHook ( hook . name , hook . callbackUri , hook . data , hook . errors ) ,
75- ) ;
76- }
77-
78- cf . fetchGoals = false ;
79-
80- return cf ;
81- }
82-
83- function makeDefaultInitialContext ( ) {
84- return { kind : 'user' , key : 'key-not-specified' } ;
85- }
86-
87- export class ClientEntity {
88- constructor (
89- private readonly _client : LDClient ,
90- private readonly _logger : LDLogger ,
91- ) { }
92-
93- close ( ) {
94- this . _client . close ( ) ;
95- this . _logger . info ( 'Test ended' ) ;
96- }
97-
98- async doCommand ( params : CommandParams ) {
99- this . _logger . info ( `Received command: ${ params . command } ` ) ;
100- switch ( params . command ) {
101- case CommandType . EvaluateFlag : {
102- const evaluationParams = params . evaluate ;
103- if ( ! evaluationParams ) {
104- throw malformedCommand ;
105- }
106- if ( evaluationParams . detail ) {
107- switch ( evaluationParams . valueType ) {
108- case ValueType . Bool :
109- return this . _client . boolVariationDetail (
110- evaluationParams . flagKey ,
111- evaluationParams . defaultValue as boolean ,
112- ) ;
113- case ValueType . Int : // Intentional fallthrough.
114- case ValueType . Double :
115- return this . _client . numberVariationDetail (
116- evaluationParams . flagKey ,
117- evaluationParams . defaultValue as number ,
118- ) ;
119- case ValueType . String :
120- return this . _client . stringVariationDetail (
121- evaluationParams . flagKey ,
122- evaluationParams . defaultValue as string ,
123- ) ;
124- default :
125- return this . _client . variationDetail (
126- evaluationParams . flagKey ,
127- evaluationParams . defaultValue ,
128- ) ;
129- }
130- }
131- switch ( evaluationParams . valueType ) {
132- case ValueType . Bool :
133- return {
134- value : this . _client . boolVariation (
135- evaluationParams . flagKey ,
136- evaluationParams . defaultValue as boolean ,
137- ) ,
138- } ;
139- case ValueType . Int : // Intentional fallthrough.
140- case ValueType . Double :
141- return {
142- value : this . _client . numberVariation (
143- evaluationParams . flagKey ,
144- evaluationParams . defaultValue as number ,
145- ) ,
146- } ;
147- case ValueType . String :
148- return {
149- value : this . _client . stringVariation (
150- evaluationParams . flagKey ,
151- evaluationParams . defaultValue as string ,
152- ) ,
153- } ;
154- default :
155- return {
156- value : this . _client . variation (
157- evaluationParams . flagKey ,
158- evaluationParams . defaultValue ,
159- ) ,
160- } ;
161- }
162- }
163-
164- case CommandType . EvaluateAllFlags :
165- return { state : this . _client . allFlags ( ) } ;
166-
167- case CommandType . IdentifyEvent : {
168- const identifyParams = params . identifyEvent ;
169- if ( ! identifyParams ) {
170- throw malformedCommand ;
171- }
172- await this . _client . identify ( identifyParams . user || identifyParams . context ) ;
173- return undefined ;
174- }
175-
176- case CommandType . CustomEvent : {
177- const customEventParams = params . customEvent ;
178- if ( ! customEventParams ) {
179- throw malformedCommand ;
180- }
181- this . _client . track (
182- customEventParams . eventKey ,
183- customEventParams . data ,
184- customEventParams . metricValue ,
185- ) ;
186- return undefined ;
187- }
188-
189- case CommandType . FlushEvents :
190- this . _client . flush ( ) ;
191- return undefined ;
192-
193- default :
194- throw badCommandError ;
195- }
196- }
197- }
198-
199- export async function newSdkClientEntity ( options : CreateInstanceParams ) {
200- const logger = makeLogger ( options . tag ) ;
201-
202- logger . info ( `Creating client with configuration: ${ JSON . stringify ( options . configuration ) } ` ) ;
203-
10+ export async function newSdkClientEntity (
11+ _id : string ,
12+ options : CreateInstanceParams ,
13+ ) : Promise < IClientEntity > {
20414 const timeout =
20515 options . configuration . startWaitTimeMs !== null &&
20616 options . configuration . startWaitTimeMs !== undefined
20717 ? options . configuration . startWaitTimeMs
20818 : 5000 ;
209- const sdkConfig = makeSdkConfig ( options . configuration , options . tag ) ;
19+
20+ const sdkConfig = {
21+ ...makeSdkConfig ( options . configuration , options . tag ) ,
22+ fetchGoals : false ,
23+ } as LDOptions ;
21024 const initialContext =
21125 options . configuration . clientSide ?. initialUser ||
21226 options . configuration . clientSide ?. initialContext ||
21327 makeDefaultInitialContext ( ) ;
28+
21429 const client = createClient (
21530 options . configuration . credential || 'unknown-env-id' ,
21631 initialContext ,
21732 sdkConfig ,
21833 ) ;
34+
21935 let failed = false ;
22036 try {
22137 await Promise . race ( [
@@ -225,13 +41,12 @@ export async function newSdkClientEntity(options: CreateInstanceParams) {
22541 } ) ,
22642 ] ) ;
22743 } catch ( _ ) {
228- // we get here if waitForInitialization() rejects or if we timed out
22944 failed = true ;
23045 }
23146 if ( failed && ! options . configuration . initCanFail ) {
23247 client . close ( ) ;
23348 throw new Error ( 'client initialization failed' ) ;
23449 }
23550
236- return new ClientEntity ( client , logger ) ;
51+ return new ClientEntity ( client , options . tag ) ;
23752}
0 commit comments