@@ -9,6 +9,7 @@ import { ResourceType } from '../../../src/models/resource-types.js';
99import { ApimServiceContext } from '../../../src/models/types.js' ;
1010import { FilterConfig } from '../../../src/models/config.js' ;
1111import { extractWorkspaces } from '../../../src/services/workspace-extractor.js' ;
12+ import { resolveWorkspaceFilter } from '../../../src/services/workspace-extractor.js' ;
1213
1314const testContext : ApimServiceContext = {
1415 subscriptionId : 'sub-1' ,
@@ -202,5 +203,188 @@ describe('workspace-extractor', () => {
202203 expect ( results ) . toHaveLength ( 1 ) ;
203204 expect ( results [ 0 ] ?. resourceCount ) . toBeGreaterThan ( 0 ) ;
204205 } ) ;
206+
207+ it ( 'should apply workspace sub-filter to limit extracted resources' , async ( ) => {
208+ const client = createMockClient ( ) ;
209+ const listedTypes : ResourceType [ ] = [ ] ;
210+ client . listResources = async function * ( _ctx : ApimServiceContext , type : ResourceType ) {
211+ listedTypes . push ( type ) ;
212+ if ( type === ResourceType . NamedValue ) {
213+ yield { name : 'ws-nv-1' , properties : { } } ;
214+ yield { name : 'ws-nv-2' , properties : { } } ;
215+ }
216+ } ;
217+ const store = createMockStore ( ) ;
218+ const filter : FilterConfig = {
219+ workspaces : [ 'ws-1' ] ,
220+ workspaceSubFilters : {
221+ 'ws-1' : {
222+ namedValues : [ 'ws-nv-1' ] ,
223+ } ,
224+ } ,
225+ } ;
226+
227+ const results = await extractWorkspaces (
228+ client , store , testContext , '/output' , filter
229+ ) ;
230+
231+ expect ( results ) . toHaveLength ( 1 ) ;
232+ // Only ws-nv-1 should be extracted, ws-nv-2 should be filtered out
233+ expect ( results [ 0 ] ?. resourceCount ) . toBe ( 1 ) ;
234+ } ) ;
235+
236+ it ( 'should extract everything when workspace has no sub-filter' , async ( ) => {
237+ const client = createMockClient ( ) ;
238+ client . listResources = async function * ( _ctx : ApimServiceContext , type : ResourceType ) {
239+ if ( type === ResourceType . NamedValue ) {
240+ yield { name : 'ws-nv-1' , properties : { } } ;
241+ yield { name : 'ws-nv-2' , properties : { } } ;
242+ }
243+ } ;
244+ const store = createMockStore ( ) ;
245+ const filter : FilterConfig = {
246+ workspaces : [ 'ws-1' ] ,
247+ // No workspaceSubFilters — extract everything in the workspace
248+ } ;
249+
250+ const results = await extractWorkspaces (
251+ client , store , testContext , '/output' , filter
252+ ) ;
253+
254+ expect ( results ) . toHaveLength ( 1 ) ;
255+ // Both named values should be extracted
256+ expect ( results [ 0 ] ?. resourceCount ) . toBe ( 2 ) ;
257+ } ) ;
258+
259+ it ( 'should support wildcard patterns in workspace names' , async ( ) => {
260+ const client = createMockClient ( ) ;
261+ // Discovery returns three workspaces
262+ const originalListResources = client . listResources ;
263+ let firstCall = true ;
264+ client . listResources = async function * ( ctx : ApimServiceContext , type : ResourceType ) {
265+ if ( type === ResourceType . Workspace && firstCall ) {
266+ firstCall = false ;
267+ yield { name : 'team-a-workspace' , properties : { } } ;
268+ yield { name : 'team-b-workspace' , properties : { } } ;
269+ yield { name : 'other-workspace' , properties : { } } ;
270+ return ;
271+ }
272+ yield * originalListResources ( ctx , type ) ;
273+ } ;
274+ const store = createMockStore ( ) ;
275+ const filter : FilterConfig = { workspaces : [ 'team-*' ] } ;
276+
277+ const results = await extractWorkspaces (
278+ client , store , testContext , '/output' , filter
279+ ) ;
280+
281+ // Only team-a-workspace and team-b-workspace should match
282+ expect ( results ) . toHaveLength ( 2 ) ;
283+ expect ( results . map ( ( r ) => r . workspaceName ) . sort ( ) ) . toEqual ( [
284+ 'team-a-workspace' ,
285+ 'team-b-workspace' ,
286+ ] ) ;
287+ } ) ;
288+
289+ it ( 'should apply sub-filter with wildcard workspace name patterns' , async ( ) => {
290+ const client = createMockClient ( ) ;
291+ let firstCall = true ;
292+ client . listResources = async function * ( _ctx : ApimServiceContext , type : ResourceType ) {
293+ if ( type === ResourceType . Workspace && firstCall ) {
294+ firstCall = false ;
295+ yield { name : 'team-a' , properties : { } } ;
296+ return ;
297+ }
298+ if ( type === ResourceType . NamedValue ) {
299+ yield { name : 'nv-1' , properties : { } } ;
300+ yield { name : 'nv-2' , properties : { } } ;
301+ }
302+ } ;
303+ const store = createMockStore ( ) ;
304+ const filter : FilterConfig = {
305+ workspaces : [ 'team-*' ] ,
306+ workspaceSubFilters : {
307+ 'team-a' : {
308+ namedValues : [ 'nv-1' ] ,
309+ } ,
310+ } ,
311+ } ;
312+
313+ const results = await extractWorkspaces (
314+ client , store , testContext , '/output' , filter
315+ ) ;
316+
317+ expect ( results ) . toHaveLength ( 1 ) ;
318+ expect ( results [ 0 ] ?. workspaceName ) . toBe ( 'team-a' ) ;
319+ // Only nv-1 should be extracted due to sub-filter
320+ expect ( results [ 0 ] ?. resourceCount ) . toBe ( 1 ) ;
321+ } ) ;
322+ } ) ;
323+
324+ describe ( 'resolveWorkspaceFilter' , ( ) => {
325+ it ( 'should return undefined when no filter provided' , ( ) => {
326+ expect ( resolveWorkspaceFilter ( 'ws-1' ) ) . toBeUndefined ( ) ;
327+ } ) ;
328+
329+ it ( 'should return undefined when no workspaceSubFilters' , ( ) => {
330+ const filter : FilterConfig = { workspaces : [ 'ws-1' ] } ;
331+ expect ( resolveWorkspaceFilter ( 'ws-1' , filter ) ) . toBeUndefined ( ) ;
332+ } ) ;
333+
334+ it ( 'should return undefined when workspace not in sub-filters' , ( ) => {
335+ const filter : FilterConfig = {
336+ workspaces : [ 'ws-1' ] ,
337+ workspaceSubFilters : {
338+ 'ws-2' : { apis : [ 'api-1' ] } ,
339+ } ,
340+ } ;
341+ expect ( resolveWorkspaceFilter ( 'ws-1' , filter ) ) . toBeUndefined ( ) ;
342+ } ) ;
343+
344+ it ( 'should resolve workspace sub-filter case-insensitively' , ( ) => {
345+ const filter : FilterConfig = {
346+ workspaces : [ 'WS-1' ] ,
347+ workspaceSubFilters : {
348+ 'ws-1' : { apis : [ 'api-1' ] , backends : [ 'backend-1' ] } ,
349+ } ,
350+ } ;
351+ const result = resolveWorkspaceFilter ( 'WS-1' , filter ) ;
352+ expect ( result ) . toBeDefined ( ) ;
353+ expect ( result ! . apis ) . toEqual ( [ 'api-1' ] ) ;
354+ expect ( result ! . backends ) . toEqual ( [ 'backend-1' ] ) ;
355+ } ) ;
356+
357+ it ( 'should convert all workspace sub-filter fields to FilterConfig' , ( ) => {
358+ const filter : FilterConfig = {
359+ workspaceSubFilters : {
360+ 'ws-1' : {
361+ apis : [ 'api-1' ] ,
362+ backends : [ 'be-1' ] ,
363+ diagnostics : [ 'diag-1' ] ,
364+ groups : [ 'group-1' ] ,
365+ loggers : [ 'logger-1' ] ,
366+ namedValues : [ 'nv-1' ] ,
367+ policyFragments : [ 'pf-1' ] ,
368+ products : [ 'prod-1' ] ,
369+ subscriptions : [ 'sub-1' ] ,
370+ tags : [ 'tag-1' ] ,
371+ versionSets : [ 'vs-1' ] ,
372+ } ,
373+ } ,
374+ } ;
375+ const result = resolveWorkspaceFilter ( 'ws-1' , filter ) ;
376+ expect ( result ) . toBeDefined ( ) ;
377+ expect ( result ! . apis ) . toEqual ( [ 'api-1' ] ) ;
378+ expect ( result ! . backends ) . toEqual ( [ 'be-1' ] ) ;
379+ expect ( result ! . diagnostics ) . toEqual ( [ 'diag-1' ] ) ;
380+ expect ( result ! . groups ) . toEqual ( [ 'group-1' ] ) ;
381+ expect ( result ! . loggers ) . toEqual ( [ 'logger-1' ] ) ;
382+ expect ( result ! . namedValues ) . toEqual ( [ 'nv-1' ] ) ;
383+ expect ( result ! . policyFragments ) . toEqual ( [ 'pf-1' ] ) ;
384+ expect ( result ! . products ) . toEqual ( [ 'prod-1' ] ) ;
385+ expect ( result ! . subscriptions ) . toEqual ( [ 'sub-1' ] ) ;
386+ expect ( result ! . tags ) . toEqual ( [ 'tag-1' ] ) ;
387+ expect ( result ! . versionSets ) . toEqual ( [ 'vs-1' ] ) ;
388+ } ) ;
205389 } ) ;
206390} ) ;
0 commit comments