@@ -115,6 +115,93 @@ describe('Backend Functions - extractConnectionIds', () => {
115115 expect ( extractConnectionIds ( ast , filePath ) ) . toEqual ( [ ] ) ;
116116 } ) ;
117117
118+ // This extractor receives the ESTree Program from Rollup's parser; TS-only
119+ // syntax such as `as const` is outside this helper's parser boundary.
120+ test . each ( [
121+ {
122+ description : 'same-file const string identifiers' ,
123+ code : `
124+ import { request } from '@datadog/action-catalog/http/http';
125+ const CONNECTION_ID = 'same-file-const';
126+ request({ connectionId: CONNECTION_ID });
127+ ` ,
128+ expected : [ 'same-file-const' ] ,
129+ } ,
130+ {
131+ description : 'exported same-file const string identifiers' ,
132+ code : `
133+ import { request } from '@datadog/action-catalog/http/http';
134+ export const CONNECTION_ID = 'exported-same-file-const';
135+ request({ connectionId: CONNECTION_ID });
136+ ` ,
137+ expected : [ 'exported-same-file-const' ] ,
138+ } ,
139+ {
140+ description : 'same-file const-to-const chains' ,
141+ code : `
142+ import { request } from '@datadog/action-catalog/http/http';
143+ const A = 'const-chain';
144+ const B = A;
145+ const C = B;
146+ request({ connectionId: C });
147+ ` ,
148+ expected : [ 'const-chain' ] ,
149+ } ,
150+ {
151+ description : 'inline static template literals' ,
152+ code : `
153+ import { request } from '@datadog/action-catalog/http/http';
154+ request({ connectionId: \`inline-static-template\` });
155+ ` ,
156+ expected : [ 'inline-static-template' ] ,
157+ } ,
158+ {
159+ description : 'same-file const static template literals' ,
160+ code : `
161+ import { request } from '@datadog/action-catalog/http/http';
162+ const CONNECTION_ID = \`const-static-template\`;
163+ request({ connectionId: CONNECTION_ID });
164+ ` ,
165+ expected : [ 'const-static-template' ] ,
166+ } ,
167+ {
168+ description : 'same-file const object members with identifier keys' ,
169+ code : `
170+ import { request } from '@datadog/action-catalog/http/http';
171+ const CONNECTIONS = {
172+ HTTP: 'object-identifier-key',
173+ };
174+ request({ connectionId: CONNECTIONS.HTTP });
175+ ` ,
176+ expected : [ 'object-identifier-key' ] ,
177+ } ,
178+ {
179+ description : 'same-file const object members with string-literal keys' ,
180+ code : `
181+ import { request } from '@datadog/action-catalog/http/http';
182+ const CONNECTIONS = {
183+ 'HTTP': 'object-string-key',
184+ };
185+ request({ connectionId: CONNECTIONS.HTTP });
186+ ` ,
187+ expected : [ 'object-string-key' ] ,
188+ } ,
189+ {
190+ description : 'same-file const object members whose values are const identifiers' ,
191+ code : `
192+ import { request } from '@datadog/action-catalog/http/http';
193+ const HTTP_CONNECTION_ID = 'object-const-value';
194+ const CONNECTIONS = {
195+ HTTP: HTTP_CONNECTION_ID,
196+ };
197+ request({ connectionId: CONNECTIONS.HTTP });
198+ ` ,
199+ expected : [ 'object-const-value' ] ,
200+ } ,
201+ ] ) ( 'Should resolve $description' , ( { code, expected } ) => {
202+ expect ( extractConnectionIds ( parseModule ( code ) , filePath ) ) . toEqual ( expected ) ;
203+ } ) ;
204+
118205 test . each ( [
119206 {
120207 description : 'non-object first arguments' ,
@@ -231,41 +318,153 @@ describe('Backend Functions - extractConnectionIds', () => {
231318
232319 test . each ( [
233320 {
234- description : 'identifier' ,
235- expression : 'CONNECTION_ID' ,
236- expectedType : 'Identifier' ,
321+ description : 'mutable let bindings' ,
322+ code : `
323+ import { request } from '@datadog/action-catalog/http/http';
324+ let CONNECTION_ID = 'mutable-let';
325+ request({ connectionId: CONNECTION_ID });
326+ ` ,
327+ expected : "declared with 'let'" ,
328+ } ,
329+ {
330+ description : 'mutable var bindings' ,
331+ code : `
332+ import { request } from '@datadog/action-catalog/http/http';
333+ var CONNECTION_ID = 'mutable-var';
334+ request({ connectionId: CONNECTION_ID });
335+ ` ,
336+ expected : "declared with 'var'" ,
337+ } ,
338+ {
339+ description : 'unresolved identifiers' ,
340+ code : `
341+ import { request } from '@datadog/action-catalog/http/http';
342+ request({ connectionId: CONNECTION_ID });
343+ ` ,
344+ expected : "identifier 'CONNECTION_ID' is not a top-level same-file const binding" ,
345+ } ,
346+ {
347+ description : 'destructured connection bindings' ,
348+ code : `
349+ import { request } from '@datadog/action-catalog/http/http';
350+ const CONNECTIONS = { HTTP: 'destructured-connection-binding' };
351+ const { HTTP } = CONNECTIONS;
352+ request({ connectionId: HTTP });
353+ ` ,
354+ expected : "identifier 'HTTP' is not a top-level same-file const binding" ,
355+ } ,
356+ {
357+ description : 'imported identifiers' ,
358+ code : `
359+ import { request } from '@datadog/action-catalog/http/http';
360+ import { CONNECTION_ID } from './connections';
361+ request({ connectionId: CONNECTION_ID });
362+ ` ,
363+ expected : "imported identifier 'CONNECTION_ID' cannot be statically analyzed" ,
364+ } ,
365+ {
366+ description : 'imported object members' ,
367+ code : `
368+ import { request } from '@datadog/action-catalog/http/http';
369+ import { CONNECTIONS } from './connections';
370+ request({ connectionId: CONNECTIONS.HTTP });
371+ ` ,
372+ expected : "imported object 'CONNECTIONS' cannot be statically analyzed" ,
373+ } ,
374+ {
375+ description : 'dynamic template literals' ,
376+ code : `
377+ import { request } from '@datadog/action-catalog/http/http';
378+ const prefix = 'conn';
379+ request({ connectionId: \`\${prefix}-dynamic\` });
380+ ` ,
381+ expected : 'template literals with interpolations cannot be statically analyzed' ,
237382 } ,
238383 {
239- description : 'template literal' ,
240- expression : '`conn-template`' ,
241- expectedType : 'TemplateLiteral' ,
384+ description : 'binary expressions' ,
385+ code : `
386+ import { request } from '@datadog/action-catalog/http/http';
387+ request({ connectionId: 'conn-' + suffix });
388+ ` ,
389+ expected : 'got BinaryExpression' ,
242390 } ,
243391 {
244- description : 'member expression' ,
245- expression : 'CONNECTIONS.HTTP' ,
246- expectedType : 'MemberExpression' ,
392+ description : 'function calls' ,
393+ code : `
394+ import { request } from '@datadog/action-catalog/http/http';
395+ request({ connectionId: getConnectionId() });
396+ ` ,
397+ expected : 'got CallExpression' ,
247398 } ,
248399 {
249- description : 'call expression' ,
250- expression : 'getConnectionId()' ,
251- expectedType : 'CallExpression' ,
400+ description : 'env reads' ,
401+ code : `
402+ import { request } from '@datadog/action-catalog/http/http';
403+ request({ connectionId: process.env.CONNECTION_ID });
404+ ` ,
405+ expected : 'nested or non-static member expressions cannot be statically analyzed' ,
252406 } ,
253407 {
254- description : 'binary expression' ,
255- expression : "'conn-' + suffix" ,
256- expectedType : 'BinaryExpression' ,
408+ description : 'computed object properties' ,
409+ code : `
410+ import { request } from '@datadog/action-catalog/http/http';
411+ const key = 'HTTP';
412+ const CONNECTIONS = { [key]: 'computed-object-property' };
413+ request({ connectionId: CONNECTIONS.HTTP });
414+ ` ,
415+ expected : 'computed object properties can hide connectionId object members' ,
416+ } ,
417+ {
418+ description : 'object spreads' ,
419+ code : `
420+ import { request } from '@datadog/action-catalog/http/http';
421+ const BASE = { HTTP: 'spread-object' };
422+ const CONNECTIONS = { ...BASE };
423+ request({ connectionId: CONNECTIONS.HTTP });
424+ ` ,
425+ expected : 'object spreads can hide connectionId object members' ,
426+ } ,
427+ {
428+ description : 'nested member chains' ,
429+ code : `
430+ import { request } from '@datadog/action-catalog/http/http';
431+ const CONNECTIONS = { HTTP: { PROD: 'nested-member-chain' } };
432+ request({ connectionId: CONNECTIONS.HTTP.PROD });
433+ ` ,
434+ expected : 'nested or non-static member expressions cannot be statically analyzed' ,
435+ } ,
436+ {
437+ description : 'computed member reads' ,
438+ code : `
439+ import { request } from '@datadog/action-catalog/http/http';
440+ const CONNECTIONS = { HTTP: 'computed-member-read' };
441+ request({ connectionId: CONNECTIONS['HTTP'] });
442+ ` ,
443+ expected : 'computed member expressions cannot be statically analyzed' ,
444+ } ,
445+ {
446+ description : 'object members missing a static property' ,
447+ code : `
448+ import { request } from '@datadog/action-catalog/http/http';
449+ const CONNECTIONS = { SLACK: 'slack-connection' };
450+ request({ connectionId: CONNECTIONS.HTTP });
451+ ` ,
452+ expected : "object has no static 'HTTP' property" ,
453+ } ,
454+ {
455+ description : 'const object aliases' ,
456+ code : `
457+ import { request } from '@datadog/action-catalog/http/http';
458+ const BASE = { HTTP: 'aliased-object' };
459+ const CONNECTIONS = BASE;
460+ request({ connectionId: CONNECTIONS.HTTP });
461+ ` ,
462+ expected : "object 'CONNECTIONS' must be initialized to an object literal" ,
257463 } ,
258464 ] ) (
259465 'Should fail closed for unsupported connectionId value expressions: $description' ,
260- ( { expression, expectedType } ) => {
261- const ast = parseModule ( `
262- import { request } from '@datadog/action-catalog/http/http';
263- request({ connectionId: ${ expression } });
264- ` ) ;
265-
266- expect ( ( ) => extractConnectionIds ( ast , filePath ) ) . toThrow (
267- `expected an inline string literal, got ${ expectedType } ` ,
268- ) ;
466+ ( { code, expected } ) => {
467+ expect ( ( ) => extractConnectionIds ( parseModule ( code ) , filePath ) ) . toThrow ( expected ) ;
269468 } ,
270469 ) ;
271470} ) ;
0 commit comments