@elisra-devops/docgen-data-provider 1.11.0 → 1.12.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.
@@ -14,65 +14,182 @@ export default class PipelinesDataProvider {
14
14
  }
15
15
 
16
16
  public async findPreviousPipeline(
17
- projectName: string,
18
- pipeLineId: string,
17
+ teamProject: string,
18
+ pipelineId: string,
19
19
  toPipelineRunId: number,
20
- toPipeline: any,
21
- searchPrevPipelineFromDifferentCommit: boolean
20
+ targetPipeline: any,
21
+ searchPrevPipelineFromDifferentCommit: boolean,
22
+ fromStage: string = ''
22
23
  ) {
23
- // get pipeline runs:
24
- const pipelineRuns = await this.GetPipelineRunHistory(projectName, pipeLineId);
24
+ const pipelineRuns = await this.GetPipelineRunHistory(teamProject, pipelineId);
25
+ if (!pipelineRuns.value) {
26
+ return undefined;
27
+ }
25
28
 
26
29
  for (const pipelineRun of pipelineRuns.value) {
27
- if (pipelineRun.id >= toPipelineRunId) {
30
+ if (this.isInvalidPipelineRun(pipelineRun, toPipelineRunId, fromStage)) {
28
31
  continue;
29
32
  }
30
- if (pipelineRun.result !== 'succeeded') {
33
+
34
+ if (fromStage && !(await this.isStageSuccessful(pipelineRun, teamProject, fromStage))) {
31
35
  continue;
32
36
  }
33
37
 
34
- const fromPipeline = await this.getPipelineFromPipelineId(projectName, Number(pipeLineId));
38
+ const fromPipeline = await this.getPipelineRunDetails(teamProject, Number(pipelineId), pipelineRun.id);
35
39
  if (!fromPipeline.resources.repositories) {
36
40
  continue;
37
41
  }
38
- const fromPipelineRepositories = fromPipeline.resources.repositories;
39
- logger.debug(`from pipeline repositories ${JSON.stringify(fromPipelineRepositories)}`);
40
- const toPipelineRepositories = toPipeline.resources.repositories;
41
- logger.debug(`to pipeline repositories ${JSON.stringify(toPipelineRepositories)}`);
42
-
43
- const fromPipeLineSelfRepo =
44
- '__designer_repo' in fromPipelineRepositories
45
- ? fromPipelineRepositories['__designer_repo']
46
- : 'self' in fromPipelineRepositories
47
- ? fromPipelineRepositories['self']
48
- : undefined;
49
- const toPipeLineSelfRepo =
50
- '__designer_repo' in toPipelineRepositories
51
- ? toPipelineRepositories['__designer_repo']
52
- : 'self' in toPipelineRepositories
53
- ? toPipelineRepositories['self']
54
- : undefined;
55
- if (
56
- fromPipeLineSelfRepo.repository.id === toPipeLineSelfRepo.repository.id &&
57
- fromPipeLineSelfRepo.version === toPipeLineSelfRepo.version
58
- ) {
59
- if (searchPrevPipelineFromDifferentCommit) {
60
- continue;
61
- }
62
- }
63
42
 
64
- if (
65
- fromPipeLineSelfRepo.repository.id === toPipeLineSelfRepo.repository.id &&
66
- fromPipeLineSelfRepo.refName !== toPipeLineSelfRepo.refName
67
- ) {
68
- continue;
43
+ if (this.isMatchingPipeline(fromPipeline, targetPipeline, searchPrevPipelineFromDifferentCommit)) {
44
+ return pipelineRun.id;
69
45
  }
70
-
71
- return pipelineRun.id;
72
46
  }
73
47
  return undefined;
74
48
  }
75
49
 
50
+ /**
51
+ * Determines if a pipeline run is invalid based on various conditions.
52
+ *
53
+ * @param pipelineRun - The pipeline run object to evaluate.
54
+ * @param toPipelineRunId - The pipeline run ID to compare against.
55
+ * @param fromStage - The stage from which the pipeline run originated.
56
+ * @returns `true` if the pipeline run is considered invalid, `false` otherwise.
57
+ */
58
+ private isInvalidPipelineRun(pipelineRun: any, toPipelineRunId: number, fromStage: string): boolean {
59
+ return (
60
+ pipelineRun.id >= toPipelineRunId ||
61
+ ['canceled', 'failed', 'canceling'].includes(pipelineRun.result) ||
62
+ (pipelineRun.result === 'unknown' && !fromStage) ||
63
+ (pipelineRun.result !== 'succeeded' && !fromStage)
64
+ );
65
+ }
66
+
67
+ /**
68
+ * Checks if a specific stage in a pipeline run was successful.
69
+ *
70
+ * @param pipelineRun - The pipeline run object containing details of the run.
71
+ * @param teamProject - The name of the team project.
72
+ * @param fromStage - The name of the stage to check.
73
+ * @returns A promise that resolves to a boolean indicating whether the stage was successful.
74
+ */
75
+ private async isStageSuccessful(
76
+ pipelineRun: any,
77
+ teamProject: string,
78
+ fromStage: string
79
+ ): Promise<boolean> {
80
+ const fromPipelineStage = await this.getPipelineStageName(pipelineRun, teamProject, fromStage);
81
+ return (
82
+ fromPipelineStage && fromPipelineStage.state === 'completed' && fromPipelineStage.result === 'succeeded'
83
+ );
84
+ }
85
+
86
+ /**
87
+ * Determines if two pipelines match based on their repository and version information.
88
+ *
89
+ * @param fromPipeline - The source pipeline to compare.
90
+ * @param targetPipeline - The target pipeline to compare against.
91
+ * @param searchPrevPipelineFromDifferentCommit - A flag indicating whether to search for a previous pipeline from a different commit.
92
+ * @returns `true` if the pipelines match based on the repository and version criteria; otherwise, `false`.
93
+ */
94
+ private isMatchingPipeline(
95
+ fromPipeline: PipelineRun,
96
+ targetPipeline: PipelineRun,
97
+ searchPrevPipelineFromDifferentCommit: boolean
98
+ ): boolean {
99
+ const fromRepo =
100
+ fromPipeline.resources.repositories[0].self || fromPipeline.resources.repositories.__designer_repo;
101
+ const targetRepo =
102
+ targetPipeline.resources.repositories[0].self || targetPipeline.resources.repositories.__designer_repo;
103
+
104
+ if (fromRepo.repository.id !== targetRepo.repository.id) {
105
+ return false;
106
+ }
107
+
108
+ if (fromRepo.version === targetRepo.version) {
109
+ return !searchPrevPipelineFromDifferentCommit;
110
+ }
111
+
112
+ return fromRepo.refName === targetRepo.refName;
113
+ }
114
+
115
+ /**
116
+ * Retrieves a set of pipeline resources from a given pipeline run object.
117
+ *
118
+ * @param inPipeline - The pipeline run object containing resources.
119
+ * @returns A promise that resolves to an array of unique pipeline resource objects.
120
+ *
121
+ * The function performs the following steps:
122
+ * 1. Initializes an empty set to store unique pipeline resources.
123
+ * 2. Checks if the input pipeline has any resources of type pipelines.
124
+ * 3. Iterates over each pipeline resource and processes it.
125
+ * 4. Fixes the URL of the pipeline resource to match the build API format.
126
+ * 5. Fetches the build details using the fixed URL.
127
+ * 6. If the build response is valid and matches the criteria, adds the pipeline resource to the set.
128
+ * 7. Returns an array of unique pipeline resources.
129
+ *
130
+ * The returned pipeline resource object contains the following properties:
131
+ * - name: The alias name of the resource pipeline.
132
+ * - buildId: The ID of the resource pipeline.
133
+ * - definitionId: The ID of the build definition.
134
+ * - buildNumber: The build number.
135
+ * - teamProject: The name of the team project.
136
+ * - provider: The type of repository provider.
137
+ *
138
+ * @throws Will log an error message if there is an issue fetching the pipeline resource.
139
+ */
140
+ public async getPipelineResourcePipelinesFromObject(inPipeline: PipelineRun) {
141
+ const resourcePipelines: Set<any> = new Set();
142
+
143
+ if (!inPipeline.resources.pipelines) {
144
+ return resourcePipelines;
145
+ }
146
+ const pipelines = inPipeline.resources.pipelines;
147
+
148
+ const pipelineEntries = Object.entries(pipelines);
149
+
150
+ await Promise.all(
151
+ pipelineEntries.map(async ([resourcePipelineAlias, resource]) => {
152
+ const resourcePipelineObj = (resource as any).pipeline;
153
+ const resourcePipelineName = resourcePipelineAlias;
154
+ let urlBeforeFix = resourcePipelineObj.url;
155
+ urlBeforeFix = urlBeforeFix.substring(0, urlBeforeFix.indexOf('?revision'));
156
+ const fixedUrl = urlBeforeFix.replace('/_apis/pipelines/', '/_apis/build/builds/');
157
+ let buildResponse: any;
158
+ try {
159
+ buildResponse = await TFSServices.getItemContent(fixedUrl, this.token, 'get');
160
+ } catch (err: any) {
161
+ logger.error(`Error fetching pipeline ${resourcePipelineName} : ${err.message}`);
162
+ }
163
+ if (
164
+ buildResponse &&
165
+ buildResponse.definition.type === 'build' &&
166
+ buildResponse.repository.type === 'TfsGit'
167
+ ) {
168
+ let resourcePipelineToAdd = {
169
+ name: resourcePipelineName,
170
+ buildId: resourcePipelineObj.id,
171
+ definitionId: buildResponse.definition.id,
172
+ buildNumber: buildResponse.buildNumber,
173
+ teamProject: buildResponse.project.name,
174
+ provider: buildResponse.repository.type,
175
+ };
176
+ if (!resourcePipelines.has(resourcePipelineToAdd)) {
177
+ resourcePipelines.add(resourcePipelineToAdd);
178
+ }
179
+ }
180
+ })
181
+ );
182
+
183
+ return [...resourcePipelines];
184
+ }
185
+
186
+ /**
187
+ * Retrieves a set of resource repositories from a given pipeline object.
188
+ *
189
+ * @param inPipeline - The pipeline run object containing resource information.
190
+ * @param gitDataProviderInstance - An instance of GitDataProvider to fetch repository details.
191
+ * @returns A promise that resolves to an array of unique resource repositories.
192
+ */
76
193
  public async getPipelineResourceRepositoriesFromObject(
77
194
  inPipeline: PipelineRun,
78
195
  gitDataProviderInstance: GitDataProvider
@@ -103,12 +220,27 @@ export default class PipelinesDataProvider {
103
220
  return [...resourceRepositories];
104
221
  }
105
222
 
106
- async getPipelineFromPipelineId(projectName: string, buildId: number) {
223
+ /**
224
+ * Retrieves the details of a specific pipeline build by its build ID.
225
+ *
226
+ * @param projectName - The name of the project that contains the pipeline.
227
+ * @param buildId - The unique identifier of the build to retrieve.
228
+ * @returns A promise that resolves to the content of the build details.
229
+ */
230
+ async getPipelineBuildByBuildId(projectName: string, buildId: number) {
107
231
  let url = `${this.orgUrl}${projectName}/_apis/build/builds/${buildId}`;
108
232
  return TFSServices.getItemContent(url, this.token, 'get');
109
233
  } //GetCommitForPipeline
110
234
 
111
- async getPipelineRunBuildById(projectName: string, pipelineId: number, runId: number) {
235
+ /**
236
+ * Retrieves the details of a specific pipeline run.
237
+ *
238
+ * @param projectName - The name of the project containing the pipeline.
239
+ * @param pipelineId - The ID of the pipeline.
240
+ * @param runId - The ID of the pipeline run.
241
+ * @returns A promise that resolves to the content of the pipeline run.
242
+ */
243
+ async getPipelineRunDetails(projectName: string, pipelineId: number, runId: number): Promise<PipelineRun> {
112
244
  let url = `${this.orgUrl}${projectName}/_apis/pipelines/${pipelineId}/runs/${runId}`;
113
245
  return TFSServices.getItemContent(url, this.token);
114
246
  }
@@ -126,6 +258,19 @@ export default class PipelinesDataProvider {
126
258
  return res;
127
259
  }
128
260
 
261
+ /**
262
+ * Retrieves an artifact by build ID from a specified project.
263
+ *
264
+ * @param {string} projectName - The name of the project.
265
+ * @param {string} buildId - The ID of the build.
266
+ * @param {string} artifactName - The name of the artifact to retrieve.
267
+ * @returns {Promise<any>} A promise that resolves to the artifact data.
268
+ * @throws Will throw an error if the retrieval process fails.
269
+ *
270
+ * @example
271
+ * const artifact = await GetArtifactByBuildId('MyProject', '12345', 'MyArtifact');
272
+ * console.log(artifact);
273
+ */
129
274
  async GetArtifactByBuildId(projectName: string, buildId: string, artifactName: string): Promise<any> {
130
275
  try {
131
276
  logger.info(
@@ -149,6 +294,13 @@ export default class PipelinesDataProvider {
149
294
  }
150
295
  }
151
296
 
297
+ /**
298
+ * Retrieves a release by its release ID for a given project.
299
+ *
300
+ * @param projectName - The name of the project.
301
+ * @param releaseId - The ID of the release to retrieve.
302
+ * @returns A promise that resolves to the release data.
303
+ */
152
304
  async GetReleaseByReleaseId(projectName: string, releaseId: number): Promise<any> {
153
305
  let url = `${this.orgUrl}${projectName}/_apis/release/releases/${releaseId}`;
154
306
  if (url.startsWith('https://dev.azure.com')) {
@@ -157,18 +309,30 @@ export default class PipelinesDataProvider {
157
309
  return TFSServices.getItemContent(url, this.token, 'get', null, null);
158
310
  }
159
311
 
312
+ /**
313
+ * Retrieves the run history of a specified pipeline within a project.
314
+ *
315
+ * @param projectName - The name of the project containing the pipeline.
316
+ * @param pipelineId - The ID of the pipeline to retrieve the run history for.
317
+ * @returns An object containing the count of successful runs and an array of successful run details.
318
+ * @throws Will log an error message if the pipeline run history could not be fetched.
319
+ */
160
320
  async GetPipelineRunHistory(projectName: string, pipelineId: string) {
161
- let url: string = `${this.orgUrl}${projectName}/_apis/pipelines/${pipelineId}/runs`;
162
- let res: any = await TFSServices.getItemContent(url, this.token, 'get', null, null);
163
- //Filter successful builds only
164
- let { value } = res;
165
- if (value) {
166
- const successfulRunHistory = value.filter(
167
- (run: any) => run.result !== 'failed' || run.result !== 'canceled'
168
- );
169
- return { count: successfulRunHistory.length, value: successfulRunHistory };
321
+ try {
322
+ let url: string = `${this.orgUrl}${projectName}/_apis/pipelines/${pipelineId}/runs`;
323
+ let res: any = await TFSServices.getItemContent(url, this.token, 'get', null, null);
324
+ //Filter successful builds only
325
+ let { value } = res;
326
+ if (value) {
327
+ const successfulRunHistory = value.filter(
328
+ (run: any) => run.result !== 'failed' || run.result !== 'canceled'
329
+ );
330
+ return { count: successfulRunHistory.length, value: successfulRunHistory };
331
+ }
332
+ return res;
333
+ } catch (err: any) {
334
+ logger.error(`Could not fetch Pipeline Run History: ${err.message}`);
170
335
  }
171
- return res;
172
336
  }
173
337
 
174
338
  async GetReleaseHistory(projectName: string, definitionId: string) {
@@ -217,4 +381,28 @@ export default class PipelinesDataProvider {
217
381
  }
218
382
  return artifactInfo;
219
383
  }
384
+
385
+ /**
386
+ * Get stage name
387
+ * @param pipelineRunId requested pipeline run id
388
+ * @param teamProject requested team project
389
+ * @param stageName stage name to search for in the pipeline
390
+ * @returns
391
+ */
392
+ private async getPipelineStageName(pipelineRunId: number, teamProject: string, stageName: string) {
393
+ let url = `${this.orgUrl}${teamProject}/_apis/build/builds/${pipelineRunId}/timeline?api-version=6.0`;
394
+ try {
395
+ const getPipelineLogsResponse = await TFSServices.getItemContent(url, this.token, 'get');
396
+
397
+ const { records } = getPipelineLogsResponse;
398
+ for (const record of records) {
399
+ if (record.type === 'Stage' && record.name === stageName) {
400
+ return record;
401
+ }
402
+ }
403
+ } catch (err: any) {
404
+ logger.error(`Error fetching pipeline ${pipelineRunId} with url ${url} : ${err.message}`);
405
+ return undefined;
406
+ }
407
+ }
220
408
  }
@@ -97,7 +97,13 @@ export default class TicketsDataProvider {
97
97
  }
98
98
  return linkList;
99
99
  }
100
- // gets queries recursiv
100
+ /**
101
+ * Getting shared queries
102
+ * @param project project name
103
+ * @param path query path
104
+ * @param docType document type
105
+ * @returns
106
+ */
101
107
  async GetSharedQueries(project: string, path: string, docType: string = ''): Promise<any> {
102
108
  let url;
103
109
  try {
@@ -108,7 +114,9 @@ export default class TicketsDataProvider {
108
114
  logger.debug(`doctype: ${docType}`);
109
115
  switch (docType) {
110
116
  case 'STD':
111
- return await this.fetchOneHopQueriesForStr(queries);
117
+ return await this.fetchLinkedQueries(queries, false);
118
+ case 'STR':
119
+ return await this.fetchLinkedQueries(queries, true);
112
120
  case 'SVD':
113
121
  return await this.fetchAnyQueries(queries);
114
122
  default:
@@ -120,8 +128,17 @@ export default class TicketsDataProvider {
120
128
  }
121
129
  }
122
130
 
123
- private async fetchOneHopQueriesForStr(queries: any) {
124
- const { tree1: reqTestTree, tree2: testReqTree } = await this.structureReqTestQueries(queries);
131
+ /**
132
+ * fetches linked queries
133
+ * @param queries fetched queries
134
+ * @param onlyTestReq get only test req
135
+ * @returns ReqTestTree and TestReqTree
136
+ */
137
+ private async fetchLinkedQueries(queries: any, onlyTestReq: boolean = false) {
138
+ const { tree1: reqTestTree, tree2: testReqTree } = await this.structureReqTestQueries(
139
+ queries,
140
+ onlyTestReq
141
+ );
125
142
  return { reqTestTree, testReqTree };
126
143
  }
127
144
  private async fetchAnyQueries(queries: any) {
@@ -131,7 +148,11 @@ export default class TicketsDataProvider {
131
148
  return { systemOverviewQueryTree, knownBugsQueryTree };
132
149
  }
133
150
 
134
- async GetQueryResultsFromWiql(wiqlHref: string = '', displayAsTable: boolean = false): Promise<any> {
151
+ async GetQueryResultsFromWiql(
152
+ wiqlHref: string = '',
153
+ displayAsTable: boolean = false,
154
+ testCaseToRequirementMap: Map<number, Set<any>>
155
+ ): Promise<any> {
135
156
  try {
136
157
  if (!wiqlHref) {
137
158
  throw new Error('Incorrect WIQL Link');
@@ -145,7 +166,7 @@ export default class TicketsDataProvider {
145
166
  switch (queryResult.queryType) {
146
167
  case QueryType.OneHop:
147
168
  return displayAsTable
148
- ? await this.parseDirectLinkedQueryResultForTableFormat(queryResult)
169
+ ? await this.parseDirectLinkedQueryResultForTableFormat(queryResult, testCaseToRequirementMap)
149
170
  : await this.parseTreeQueryResult(queryResult);
150
171
  case QueryType.Tree:
151
172
  return await this.parseTreeQueryResult(queryResult);
@@ -161,7 +182,10 @@ export default class TicketsDataProvider {
161
182
  }
162
183
  }
163
184
 
164
- private async parseDirectLinkedQueryResultForTableFormat(queryResult: QueryTree) {
185
+ private async parseDirectLinkedQueryResultForTableFormat(
186
+ queryResult: QueryTree,
187
+ testCaseToRequirementMap: Map<number, Set<any>>
188
+ ) {
165
189
  const { columns, workItemRelations } = queryResult;
166
190
 
167
191
  if (workItemRelations?.length === 0) {
@@ -205,8 +229,8 @@ export default class TicketsDataProvider {
205
229
  }
206
230
 
207
231
  // Get relation source from lookup
208
- const sourceRelation = lookupMap.get(relation.source.id);
209
- if (!sourceRelation) {
232
+ const sourceWorkItem = lookupMap.get(relation.source.id);
233
+ if (!sourceWorkItem) {
210
234
  throw new Error('Source relation has no mapping');
211
235
  }
212
236
 
@@ -216,9 +240,14 @@ export default class TicketsDataProvider {
216
240
  columnTargetsMap,
217
241
  true
218
242
  );
219
- const targets: any = sourceTargetsMap.get(sourceRelation) || [];
243
+ //In case if source is a test case
244
+ this.mapTestCaseToRequirement(sourceWorkItem, testCaseToRequirementMap, targetWi);
245
+
246
+ //In case of target is a test case
247
+ this.mapTestCaseToRequirement(targetWi, testCaseToRequirementMap, sourceWorkItem);
248
+ const targets: any = sourceTargetsMap.get(sourceWorkItem) || [];
220
249
  targets.push(targetWi);
221
- sourceTargetsMap.set(sourceRelation, targets);
250
+ sourceTargetsMap.set(sourceWorkItem, targets);
222
251
  }
223
252
  }
224
253
 
@@ -230,6 +259,27 @@ export default class TicketsDataProvider {
230
259
  };
231
260
  }
232
261
 
262
+ private mapTestCaseToRequirement(
263
+ testCaseItem: any,
264
+ testCaseToRequirementMap: Map<number, Set<any>>,
265
+ RequirementWi: any
266
+ ) {
267
+ if (testCaseItem.fields['System.WorkItemType'] == 'Test Case') {
268
+ if (!testCaseToRequirementMap.has(testCaseItem.id)) {
269
+ testCaseToRequirementMap.set(testCaseItem.id, new Set());
270
+ }
271
+ const requirementSet = testCaseToRequirementMap.get(testCaseItem.id);
272
+ if (requirementSet) {
273
+ // Check if there's already an item with the same ID
274
+ const alreadyExists = [...requirementSet].some((reqItem) => reqItem.id === RequirementWi.id);
275
+
276
+ if (!alreadyExists) {
277
+ requirementSet.add(RequirementWi);
278
+ }
279
+ }
280
+ }
281
+ }
282
+
233
283
  private async parseFlatQueryResultForTableFormat(queryResult: QueryTree) {
234
284
  const { columns, workItems } = queryResult;
235
285
 
@@ -539,6 +589,7 @@ export default class TicketsDataProvider {
539
589
  return queryResult;
540
590
  }
541
591
 
592
+ //Build columns
542
593
  BuildColumns(results: any, queryResult: Query) {
543
594
  for (var i = 0; i < results.columns.length; i++) {
544
595
  queryResult.columns[i] = new Column();
@@ -592,12 +643,14 @@ export default class TicketsDataProvider {
592
643
  }
593
644
  }
594
645
 
646
+ //Get work item attachments
595
647
  async GetWorkitemAttachmentsJSONData(project: string, attachmentId: string) {
596
648
  let wiuRL = `${this.orgUrl}${project}/_apis/wit/attachments/${attachmentId}`;
597
649
  let attachment = await TFSServices.getItemContent(wiuRL, this.token);
598
650
  return attachment;
599
651
  }
600
652
 
653
+ //Update work item
601
654
  async UpdateWorkItem(projectName: string, wiBody: any, workItemId: number, byPass: boolean) {
602
655
  let res: any;
603
656
  let url: string = `${this.orgUrl}${projectName}/_apis/wit/workitems/${workItemId}?bypassRules=${String(
@@ -696,15 +749,22 @@ export default class TicketsDataProvider {
696
749
  }
697
750
  }
698
751
 
699
- private async structureReqTestQueries(rootQuery: any, parentId: any = null): Promise<any> {
752
+ /**
753
+ * Recursively structures the query list for the requirement to test case and test case to requirement queries
754
+ */
755
+ private async structureReqTestQueries(
756
+ rootQuery: any,
757
+ onlyTestReq: boolean,
758
+ parentId: any = null
759
+ ): Promise<any> {
700
760
  try {
701
761
  if (!rootQuery.hasChildren) {
702
762
  if (!rootQuery.isFolder && rootQuery.queryType === 'oneHop') {
703
763
  const wiql = rootQuery.wiql;
704
764
  let tree1Node = null;
705
765
  let tree2Node = null;
706
-
707
- if (this.matchesReqTestCondition(wiql)) {
766
+ // Check if the query is a requirement to test case query
767
+ if (!onlyTestReq && this.matchesReqTestCondition(wiql)) {
708
768
  tree1Node = {
709
769
  id: rootQuery.id,
710
770
  pId: parentId,
@@ -731,18 +791,18 @@ export default class TicketsDataProvider {
731
791
  return { tree1: null, tree2: null };
732
792
  }
733
793
  }
734
-
794
+ // If the query has children, but they are not loaded, fetch them
735
795
  if (!rootQuery.children) {
736
796
  const queryUrl = `${rootQuery.url}?$depth=2&$expand=all`;
737
797
  const currentQuery = await TFSServices.getItemContent(queryUrl, this.token);
738
798
  return currentQuery
739
- ? await this.structureReqTestQueries(currentQuery, currentQuery.id)
799
+ ? await this.structureReqTestQueries(currentQuery, onlyTestReq, currentQuery.id)
740
800
  : { tree1: null, tree2: null };
741
801
  }
742
802
 
743
803
  // Process children recursively
744
804
  const childResults = await Promise.all(
745
- rootQuery.children.map((child: any) => this.structureReqTestQueries(child, rootQuery.id))
805
+ rootQuery.children.map((child: any) => this.structureReqTestQueries(child, onlyTestReq, rootQuery.id))
746
806
  );
747
807
 
748
808
  // Build tree1
@@ -782,6 +842,7 @@ export default class TicketsDataProvider {
782
842
  }
783
843
  }
784
844
 
845
+ // check if the query is a requirement to test case query
785
846
  private matchesReqTestCondition(wiql: string): boolean {
786
847
  return (
787
848
  wiql.includes("Source.[System.WorkItemType] = 'Requirement'") &&
@@ -789,6 +850,7 @@ export default class TicketsDataProvider {
789
850
  );
790
851
  }
791
852
 
853
+ // check if the query is a test case to requirement query
792
854
  private matchesTestReqCondition(wiql: string): boolean {
793
855
  return (
794
856
  wiql.includes("Source.[System.WorkItemType] = 'Test Case'") &&
@@ -796,10 +858,12 @@ export default class TicketsDataProvider {
796
858
  );
797
859
  }
798
860
 
861
+ // check if the query is a bug query
799
862
  private matchesBugCondition(wiql: string): boolean {
800
863
  return wiql.includes(`[System.WorkItemType] = 'Bug'`);
801
864
  }
802
865
 
866
+ // Filter the fields based on the columns to filter map
803
867
  private filterFieldsByColumns(
804
868
  item: any,
805
869
  columnsToFilterMap: Map<string, string>,