@elisra-devops/docgen-data-provider 1.109.0 → 1.109.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/modules/TicketsDataProvider.d.ts +7 -1
- package/bin/modules/TicketsDataProvider.js +34 -15
- package/bin/modules/TicketsDataProvider.js.map +1 -1
- package/bin/tests/modules/ticketsDataProvider.test.js +64 -5
- package/bin/tests/modules/ticketsDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/TicketsDataProvider.ts +65 -13
- package/src/tests/modules/ticketsDataProvider.test.ts +88 -5
|
@@ -666,6 +666,31 @@ export default class TicketsDataProvider {
|
|
|
666
666
|
return { systemRequirementsQueryTree };
|
|
667
667
|
}
|
|
668
668
|
|
|
669
|
+
/**
|
|
670
|
+
* Fetches flat Customer/System requirement queries for SysRS traceability.
|
|
671
|
+
* OneHop/tree queries are intentionally excluded for this picker.
|
|
672
|
+
*/
|
|
673
|
+
private async fetchFlatUpstreamQueries(
|
|
674
|
+
queries: any,
|
|
675
|
+
excludedFolderNames: string[] = [],
|
|
676
|
+
allowedTypes: string[] = ['requirement'],
|
|
677
|
+
) {
|
|
678
|
+
const { tree1: systemRequirementsQueryTree } = await this.structureFetchedQueries(
|
|
679
|
+
queries,
|
|
680
|
+
false,
|
|
681
|
+
null,
|
|
682
|
+
allowedTypes,
|
|
683
|
+
[],
|
|
684
|
+
undefined,
|
|
685
|
+
undefined,
|
|
686
|
+
false,
|
|
687
|
+
excludedFolderNames,
|
|
688
|
+
true,
|
|
689
|
+
false,
|
|
690
|
+
);
|
|
691
|
+
return { systemRequirementsQueryTree };
|
|
692
|
+
}
|
|
693
|
+
|
|
669
694
|
private async fetchSrsQueries(rootQueries: any) {
|
|
670
695
|
const srsFolder = await this.findQueryFolderByName(rootQueries, 'srs');
|
|
671
696
|
if (!srsFolder) {
|
|
@@ -710,31 +735,54 @@ export default class TicketsDataProvider {
|
|
|
710
735
|
const { root: sysRsRoot, found: sysRsRootFound } = await this.getDocTypeRoot(rootQueries, 'sysrs');
|
|
711
736
|
logger.debug(`[GetSharedQueries][sysrs] using ${sysRsRootFound ? 'dedicated folder' : 'root queries'}`);
|
|
712
737
|
|
|
713
|
-
const
|
|
714
|
-
'System To Customer',
|
|
715
|
-
'System To Subsystem',
|
|
716
|
-
]);
|
|
717
|
-
|
|
718
|
-
const systemToCustomerFolder = await this.findChildFolderByPossibleNames(sysRsRoot, [
|
|
738
|
+
const systemToCustomerFolderNames = [
|
|
719
739
|
'system to customer',
|
|
720
740
|
'system-to-customer',
|
|
721
741
|
'system customer',
|
|
722
742
|
'subsystem to system',
|
|
723
743
|
'customer to system',
|
|
724
|
-
]
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
744
|
+
];
|
|
745
|
+
const systemToSubsystemFolderNames = ['system to subsystem', 'system-to-subsystem', 'system subsystem'];
|
|
746
|
+
|
|
747
|
+
const systemRequirementsQueries = await this.fetchSystemRequirementQueries(sysRsRoot, [
|
|
748
|
+
...systemToCustomerFolderNames,
|
|
749
|
+
...systemToSubsystemFolderNames,
|
|
729
750
|
]);
|
|
730
751
|
|
|
752
|
+
const systemToCustomerFolder = await this.findChildFolderByPossibleNames(
|
|
753
|
+
sysRsRoot,
|
|
754
|
+
systemToCustomerFolderNames,
|
|
755
|
+
);
|
|
756
|
+
const systemToSubsystemFolder = await this.findChildFolderByPossibleNames(
|
|
757
|
+
sysRsRoot,
|
|
758
|
+
systemToSubsystemFolderNames,
|
|
759
|
+
);
|
|
760
|
+
|
|
731
761
|
const subsystemToSystemRequirementsQueries =
|
|
732
762
|
await this.fetchRequirementsTraceQueriesForFolder(systemToCustomerFolder);
|
|
733
763
|
const systemToSubsystemRequirementsQueries =
|
|
734
764
|
await this.fetchRequirementsTraceQueriesForFolder(systemToSubsystemFolder);
|
|
735
765
|
|
|
766
|
+
// Customer/System requirements (traceability table) picker: only scan the dedicated
|
|
767
|
+
// System-to-Customer folder. If it doesn't exist in the tenant, return null
|
|
768
|
+
// rather than scanning the whole SysRS root, which would surface unrelated
|
|
769
|
+
// flat queries into the picker.
|
|
770
|
+
let customerRequirementsQueries: any = null;
|
|
771
|
+
if (systemToCustomerFolder) {
|
|
772
|
+
customerRequirementsQueries = await this.fetchFlatUpstreamQueries(
|
|
773
|
+
systemToCustomerFolder,
|
|
774
|
+
[],
|
|
775
|
+
['requirement'],
|
|
776
|
+
);
|
|
777
|
+
} else {
|
|
778
|
+
logger.debug(
|
|
779
|
+
'[GetSharedQueries][sysrs] System-to-Customer folder not found; skipping customer-requirements picker',
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
|
|
736
783
|
return {
|
|
737
784
|
systemRequirementsQueries,
|
|
785
|
+
customerRequirementsQueries,
|
|
738
786
|
subsystemToSystemRequirementsQueries,
|
|
739
787
|
systemToSubsystemRequirementsQueries,
|
|
740
788
|
};
|
|
@@ -2494,7 +2542,7 @@ export default class TicketsDataProvider {
|
|
|
2494
2542
|
* Recursively structures fetched queries into two hierarchical trees (tree1 and tree2)
|
|
2495
2543
|
* by matching WIQL against allowed Source/Target types and optional area filters.
|
|
2496
2544
|
* Supports leaf queries of type:
|
|
2497
|
-
* - oneHop (
|
|
2545
|
+
* - oneHop (when includeOneHopQueries === true)
|
|
2498
2546
|
* - tree (when includeTreeQueries === true)
|
|
2499
2547
|
* - flat (when includeFlatQueries === true)
|
|
2500
2548
|
*
|
|
@@ -2508,6 +2556,7 @@ export default class TicketsDataProvider {
|
|
|
2508
2556
|
* @param includeTreeQueries - Include 'tree' queries in addition to 'oneHop'. Defaults to `false`.
|
|
2509
2557
|
* @param excludedFolderNames - Optional list of folder names to skip entirely (case-insensitive exact match).
|
|
2510
2558
|
* @param includeFlatQueries - Include 'flat' queries (matched by [System.WorkItemType] and [System.AreaPath]). Defaults to `false`.
|
|
2559
|
+
* @param includeOneHopQueries - Include 'oneHop' queries. Defaults to `true` for legacy callers.
|
|
2511
2560
|
* @returns A promise resolving to an object with `tree1` and `tree2` nodes, or `null` for each when none match.
|
|
2512
2561
|
* @throws Logs an error if an exception occurs during processing.
|
|
2513
2562
|
*/
|
|
@@ -2522,6 +2571,7 @@ export default class TicketsDataProvider {
|
|
|
2522
2571
|
includeTreeQueries: boolean = false,
|
|
2523
2572
|
excludedFolderNames: string[] = [],
|
|
2524
2573
|
includeFlatQueries: boolean = false,
|
|
2574
|
+
includeOneHopQueries: boolean = true,
|
|
2525
2575
|
workItemTypeCache?: Map<string, string | null>,
|
|
2526
2576
|
): Promise<any> {
|
|
2527
2577
|
try {
|
|
@@ -2540,7 +2590,7 @@ export default class TicketsDataProvider {
|
|
|
2540
2590
|
if (!rootQuery.hasChildren) {
|
|
2541
2591
|
const isLeafCandidate =
|
|
2542
2592
|
!rootQuery.isFolder &&
|
|
2543
|
-
(rootQuery.queryType === 'oneHop' ||
|
|
2593
|
+
((includeOneHopQueries && rootQuery.queryType === 'oneHop') ||
|
|
2544
2594
|
(includeTreeQueries && rootQuery.queryType === 'tree') ||
|
|
2545
2595
|
(includeFlatQueries && rootQuery.queryType === 'flat'));
|
|
2546
2596
|
if (isLeafCandidate) {
|
|
@@ -2636,6 +2686,7 @@ export default class TicketsDataProvider {
|
|
|
2636
2686
|
includeTreeQueries,
|
|
2637
2687
|
excludedFolderNames,
|
|
2638
2688
|
includeFlatQueries,
|
|
2689
|
+
includeOneHopQueries,
|
|
2639
2690
|
typeCache,
|
|
2640
2691
|
);
|
|
2641
2692
|
}
|
|
@@ -2654,6 +2705,7 @@ export default class TicketsDataProvider {
|
|
|
2654
2705
|
includeTreeQueries,
|
|
2655
2706
|
excludedFolderNames,
|
|
2656
2707
|
includeFlatQueries,
|
|
2708
|
+
includeOneHopQueries,
|
|
2657
2709
|
typeCache,
|
|
2658
2710
|
),
|
|
2659
2711
|
),
|
|
@@ -93,6 +93,66 @@ describe('TicketsDataProvider', () => {
|
|
|
93
93
|
});
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
+
describe('fetchFlatUpstreamQueries', () => {
|
|
97
|
+
it('should allow only Requirement flat queries and exclude oneHop/tree query types', async () => {
|
|
98
|
+
const structureSpy = jest
|
|
99
|
+
.spyOn(ticketsDataProvider as any, 'structureFetchedQueries')
|
|
100
|
+
.mockResolvedValue({ tree1: { id: 't1' }, tree2: null });
|
|
101
|
+
|
|
102
|
+
await (ticketsDataProvider as any).fetchFlatUpstreamQueries({ hasChildren: false }, []);
|
|
103
|
+
|
|
104
|
+
expect(structureSpy.mock.calls[0][3]).toEqual(['requirement']);
|
|
105
|
+
expect(structureSpy.mock.calls[0][7]).toBe(false);
|
|
106
|
+
expect(structureSpy.mock.calls[0][9]).toBe(true);
|
|
107
|
+
expect(structureSpy.mock.calls[0][10]).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should allow Requirement flat queries for fallback discovery', async () => {
|
|
111
|
+
const structureSpy = jest
|
|
112
|
+
.spyOn(ticketsDataProvider as any, 'structureFetchedQueries')
|
|
113
|
+
.mockResolvedValue({ tree1: { id: 't1' }, tree2: null });
|
|
114
|
+
|
|
115
|
+
await (ticketsDataProvider as any).fetchFlatUpstreamQueries(
|
|
116
|
+
{ hasChildren: false },
|
|
117
|
+
['system to subsystem', 'system-to-subsystem', 'system subsystem'],
|
|
118
|
+
['requirement'],
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
expect(structureSpy.mock.calls[0][3]).toEqual(['requirement']);
|
|
122
|
+
expect(structureSpy.mock.calls[0][8]).toEqual([
|
|
123
|
+
'system to subsystem',
|
|
124
|
+
'system-to-subsystem',
|
|
125
|
+
'system subsystem',
|
|
126
|
+
]);
|
|
127
|
+
expect(structureSpy.mock.calls[0][9]).toBe(true);
|
|
128
|
+
expect(structureSpy.mock.calls[0][10]).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should let structureFetchedQueries exclude oneHop leaves only for flat-only callers', async () => {
|
|
132
|
+
const result = await (ticketsDataProvider as any).structureFetchedQueries(
|
|
133
|
+
{
|
|
134
|
+
id: 'one-hop',
|
|
135
|
+
hasChildren: false,
|
|
136
|
+
isFolder: false,
|
|
137
|
+
queryType: 'oneHop',
|
|
138
|
+
wiql: "[Source].[System.WorkItemType] = 'Requirement'",
|
|
139
|
+
},
|
|
140
|
+
false,
|
|
141
|
+
null,
|
|
142
|
+
['requirement'],
|
|
143
|
+
[],
|
|
144
|
+
undefined,
|
|
145
|
+
undefined,
|
|
146
|
+
false,
|
|
147
|
+
[],
|
|
148
|
+
true,
|
|
149
|
+
false,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
expect(result).toEqual({ tree1: null, tree2: null });
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
96
156
|
describe('fetchSysRsQueries', () => {
|
|
97
157
|
it('should use sysrs root, exclude trace folders from system requirements and resolve both trace directions', async () => {
|
|
98
158
|
const rootQueries = { id: 'root' };
|
|
@@ -106,6 +166,9 @@ describe('TicketsDataProvider', () => {
|
|
|
106
166
|
const fetchSystemRequirementQueriesSpy = jest
|
|
107
167
|
.spyOn(ticketsDataProvider as any, 'fetchSystemRequirementQueries')
|
|
108
168
|
.mockResolvedValue({ systemRequirementsQueryTree: { id: 'system-tree' } });
|
|
169
|
+
const fetchFlatUpstreamQueriesSpy = jest
|
|
170
|
+
.spyOn(ticketsDataProvider as any, 'fetchFlatUpstreamQueries')
|
|
171
|
+
.mockResolvedValue({ systemRequirementsQueryTree: { id: 'customer-tree' } });
|
|
109
172
|
const findChildFolderByPossibleNamesSpy = jest
|
|
110
173
|
.spyOn(ticketsDataProvider as any, 'findChildFolderByPossibleNames')
|
|
111
174
|
.mockResolvedValueOnce(subsystemToSystemFolder)
|
|
@@ -119,8 +182,14 @@ describe('TicketsDataProvider', () => {
|
|
|
119
182
|
|
|
120
183
|
expect(getDocTypeRootSpy).toHaveBeenCalledWith(rootQueries, 'sysrs');
|
|
121
184
|
expect(fetchSystemRequirementQueriesSpy).toHaveBeenCalledWith(sysRsRoot, [
|
|
122
|
-
'
|
|
123
|
-
'
|
|
185
|
+
'system to customer',
|
|
186
|
+
'system-to-customer',
|
|
187
|
+
'system customer',
|
|
188
|
+
'subsystem to system',
|
|
189
|
+
'customer to system',
|
|
190
|
+
'system to subsystem',
|
|
191
|
+
'system-to-subsystem',
|
|
192
|
+
'system subsystem',
|
|
124
193
|
]);
|
|
125
194
|
|
|
126
195
|
expect(findChildFolderByPossibleNamesSpy).toHaveBeenNthCalledWith(
|
|
@@ -136,15 +205,17 @@ describe('TicketsDataProvider', () => {
|
|
|
136
205
|
|
|
137
206
|
expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenNthCalledWith(1, subsystemToSystemFolder);
|
|
138
207
|
expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenNthCalledWith(2, systemToSubsystemFolder);
|
|
208
|
+
expect(fetchFlatUpstreamQueriesSpy).toHaveBeenCalledWith(subsystemToSystemFolder, [], ['requirement']);
|
|
139
209
|
|
|
140
210
|
expect(result).toEqual({
|
|
141
211
|
systemRequirementsQueries: { systemRequirementsQueryTree: { id: 'system-tree' } },
|
|
212
|
+
customerRequirementsQueries: { systemRequirementsQueryTree: { id: 'customer-tree' } },
|
|
142
213
|
subsystemToSystemRequirementsQueries: { id: 'fwd-trace-tree' },
|
|
143
214
|
systemToSubsystemRequirementsQueries: { id: 'rev-trace-tree' },
|
|
144
215
|
});
|
|
145
216
|
});
|
|
146
217
|
|
|
147
|
-
it('should
|
|
218
|
+
it('should return null customer/trace trees when trace folders are missing (no sysRsRoot fallback scan)', async () => {
|
|
148
219
|
const rootQueries = { id: 'root' };
|
|
149
220
|
|
|
150
221
|
jest
|
|
@@ -153,6 +224,9 @@ describe('TicketsDataProvider', () => {
|
|
|
153
224
|
const fetchSystemRequirementQueriesSpy = jest
|
|
154
225
|
.spyOn(ticketsDataProvider as any, 'fetchSystemRequirementQueries')
|
|
155
226
|
.mockResolvedValue({ systemRequirementsQueryTree: { id: 'system-tree' } });
|
|
227
|
+
const fetchFlatUpstreamQueriesSpy = jest
|
|
228
|
+
.spyOn(ticketsDataProvider as any, 'fetchFlatUpstreamQueries')
|
|
229
|
+
.mockResolvedValue({ systemRequirementsQueryTree: { id: 'should-not-be-used' } });
|
|
156
230
|
jest
|
|
157
231
|
.spyOn(ticketsDataProvider as any, 'findChildFolderByPossibleNames')
|
|
158
232
|
.mockResolvedValueOnce(null)
|
|
@@ -165,13 +239,22 @@ describe('TicketsDataProvider', () => {
|
|
|
165
239
|
const result = await (ticketsDataProvider as any).fetchSysRsQueries(rootQueries);
|
|
166
240
|
|
|
167
241
|
expect(fetchSystemRequirementQueriesSpy).toHaveBeenCalledWith(rootQueries, [
|
|
168
|
-
'
|
|
169
|
-
'
|
|
242
|
+
'system to customer',
|
|
243
|
+
'system-to-customer',
|
|
244
|
+
'system customer',
|
|
245
|
+
'subsystem to system',
|
|
246
|
+
'customer to system',
|
|
247
|
+
'system to subsystem',
|
|
248
|
+
'system-to-subsystem',
|
|
249
|
+
'system subsystem',
|
|
170
250
|
]);
|
|
171
251
|
expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenNthCalledWith(1, null);
|
|
172
252
|
expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenNthCalledWith(2, null);
|
|
253
|
+
// No fallback scan of sysRsRoot when the System-to-Customer folder is absent.
|
|
254
|
+
expect(fetchFlatUpstreamQueriesSpy).not.toHaveBeenCalled();
|
|
173
255
|
expect(result).toEqual({
|
|
174
256
|
systemRequirementsQueries: { systemRequirementsQueryTree: { id: 'system-tree' } },
|
|
257
|
+
customerRequirementsQueries: null,
|
|
175
258
|
subsystemToSystemRequirementsQueries: null,
|
|
176
259
|
systemToSubsystemRequirementsQueries: null,
|
|
177
260
|
});
|