@elisra-devops/docgen-data-provider 1.109.1 → 1.110.0

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.
@@ -105,6 +105,8 @@ export default class TicketsDataProvider {
105
105
  * OneHop/tree queries are intentionally excluded for this picker.
106
106
  */
107
107
  private fetchFlatUpstreamQueries;
108
+ private fetchCustomerRequirementQueries;
109
+ private structureCustomerRequirementQueries;
108
110
  private fetchSrsQueries;
109
111
  private fetchSysRsQueries;
110
112
  /**
@@ -27,7 +27,7 @@ const HISTORICAL_WORK_ITEM_FIELDS = [
27
27
  'Custom.TestPhase',
28
28
  ];
29
29
  /** Default fields fetched per work item in tree/flat query parsing. */
30
- const WI_DEFAULT_FIELDS = 'System.Description,System.Title,Microsoft.VSTS.TCM.ReproSteps,Microsoft.VSTS.CMMI.Symptom';
30
+ const WI_DEFAULT_FIELDS = 'System.Description,System.Title,System.WorkItemType,Microsoft.VSTS.TCM.ReproSteps,Microsoft.VSTS.CMMI.Symptom';
31
31
  class TicketsDataProvider {
32
32
  constructor(orgUrl, token) {
33
33
  this.orgUrl = '';
@@ -532,6 +532,42 @@ class TicketsDataProvider {
532
532
  const { tree1: systemRequirementsQueryTree } = await this.structureFetchedQueries(queries, false, null, allowedTypes, [], undefined, undefined, false, excludedFolderNames, true, false);
533
533
  return { systemRequirementsQueryTree };
534
534
  }
535
+ async fetchCustomerRequirementQueries(queries) {
536
+ const systemRequirementsQueryTree = await this.structureCustomerRequirementQueries(queries);
537
+ return { systemRequirementsQueryTree };
538
+ }
539
+ async structureCustomerRequirementQueries(rootQuery, parentId = null) {
540
+ try {
541
+ if (!(rootQuery === null || rootQuery === void 0 ? void 0 : rootQuery.hasChildren)) {
542
+ const isExecutableQuery = !(rootQuery === null || rootQuery === void 0 ? void 0 : rootQuery.isFolder) && ['flat', 'tree', 'oneHop'].includes(rootQuery === null || rootQuery === void 0 ? void 0 : rootQuery.queryType);
543
+ return isExecutableQuery ? this.buildQueryNode(rootQuery, parentId) : null;
544
+ }
545
+ if (!rootQuery.children) {
546
+ const queryUrl = `${rootQuery.url}?$depth=2&$expand=all`;
547
+ const currentQuery = await tfs_1.TFSServices.getItemContent(queryUrl, this.token);
548
+ if (!currentQuery) {
549
+ return null;
550
+ }
551
+ return await this.structureCustomerRequirementQueries(currentQuery, currentQuery.id);
552
+ }
553
+ const childResults = await Promise.all(rootQuery.children.map((child) => this.structureCustomerRequirementQueries(child, rootQuery.id)));
554
+ const children = childResults.filter((child) => child !== null);
555
+ return children.length > 0
556
+ ? {
557
+ id: rootQuery.id,
558
+ pId: parentId,
559
+ value: rootQuery.name,
560
+ title: rootQuery.name,
561
+ children,
562
+ }
563
+ : null;
564
+ }
565
+ catch (err) {
566
+ logger_1.default.error(`Error occurred while constructing the customer query list ${err.message} with query ${JSON.stringify(rootQuery)}`);
567
+ logger_1.default.error(`Error stack ${err.message}`);
568
+ return null;
569
+ }
570
+ }
535
571
  async fetchSrsQueries(rootQueries) {
536
572
  const srsFolder = await this.findQueryFolderByName(rootQueries, 'srs');
537
573
  if (!srsFolder) {
@@ -561,37 +597,30 @@ class TicketsDataProvider {
561
597
  async fetchSysRsQueries(rootQueries) {
562
598
  const { root: sysRsRoot, found: sysRsRootFound } = await this.getDocTypeRoot(rootQueries, 'sysrs');
563
599
  logger_1.default.debug(`[GetSharedQueries][sysrs] using ${sysRsRootFound ? 'dedicated folder' : 'root queries'}`);
564
- const systemToCustomerFolderNames = [
565
- 'system to customer',
566
- 'system-to-customer',
567
- 'system customer',
568
- 'subsystem to system',
569
- 'customer to system',
570
- ];
571
600
  const systemToSubsystemFolderNames = ['system to subsystem', 'system-to-subsystem', 'system subsystem'];
572
- const systemRequirementsQueries = await this.fetchSystemRequirementQueries(sysRsRoot, [
573
- ...systemToCustomerFolderNames,
574
- ...systemToSubsystemFolderNames,
575
- ]);
576
- const systemToCustomerFolder = await this.findChildFolderByPossibleNames(sysRsRoot, systemToCustomerFolderNames);
601
+ const systemRequirementsQueries = await this.fetchSystemRequirementQueries(sysRsRoot, systemToSubsystemFolderNames);
577
602
  const systemToSubsystemFolder = await this.findChildFolderByPossibleNames(sysRsRoot, systemToSubsystemFolderNames);
578
- const subsystemToSystemRequirementsQueries = await this.fetchRequirementsTraceQueriesForFolder(systemToCustomerFolder);
579
603
  const systemToSubsystemRequirementsQueries = await this.fetchRequirementsTraceQueriesForFolder(systemToSubsystemFolder);
580
- // Customer/System requirements (traceability table) picker: only scan the dedicated
581
- // System-to-Customer folder. If it doesn't exist in the tenant, return null
582
- // rather than scanning the whole SysRS root, which would surface unrelated
583
- // flat queries into the picker.
604
+ // Customer/System requirements (traceability table) picker: scan the entire
605
+ // dedicated SysRS folder for executable query nodes. The selected query
606
+ // defines the customer-side candidate set, so discovery does not infer
607
+ // "customer" semantics from WIQL. Scope is intentionally limited to the
608
+ // dedicated `sysrs` folder so unrelated queries from the Shared Queries root
609
+ // do not leak into the picker.
584
610
  let customerRequirementsQueries = null;
585
- if (systemToCustomerFolder) {
586
- customerRequirementsQueries = await this.fetchFlatUpstreamQueries(systemToCustomerFolder, [], ['requirement']);
611
+ if (sysRsRootFound) {
612
+ customerRequirementsQueries = await this.fetchCustomerRequirementQueries(sysRsRoot);
587
613
  }
588
614
  else {
589
- logger_1.default.debug('[GetSharedQueries][sysrs] System-to-Customer folder not found; skipping customer-requirements picker');
615
+ logger_1.default.debug('[GetSharedQueries][sysrs] dedicated sysrs folder not found; skipping customer-requirements picker');
590
616
  }
591
617
  return {
592
618
  systemRequirementsQueries,
593
619
  customerRequirementsQueries,
594
- subsystemToSystemRequirementsQueries,
620
+ // Legacy field retained for backward compatibility with callers/tests.
621
+ // The sub-system -> system trace table is out of scope for v0 and no
622
+ // longer sourced from a hardcoded "System to Customer" folder.
623
+ subsystemToSystemRequirementsQueries: null,
595
624
  systemToSubsystemRequirementsQueries,
596
625
  };
597
626
  }
@@ -1770,21 +1799,25 @@ class TicketsDataProvider {
1770
1799
  };
1771
1800
  }
1772
1801
  async PopulateWorkItemsByIds(workItemsArray = [], projectName = '') {
1773
- let url = `${this.orgUrl}${projectName}/_apis/wit/workitemsbatch`;
1774
- let res = [];
1775
- let divByMax = Math.floor(workItemsArray.length / 200);
1776
- let modulusByMax = workItemsArray.length % 200;
1777
- //iterating
1802
+ const baseUrl = `${this.orgUrl}${projectName}/_apis/wit/workitemsbatch`;
1803
+ // On-prem Azure DevOps Server rejects this POST with HTTP 400 unless an
1804
+ // api-version is explicitly set on the URL. Use the same fallback chain
1805
+ // (7.1 -> 5.1 -> no version) the historical batch hydration already uses,
1806
+ // so the helper stays compatible with ADO cloud and older on-prem tenants.
1807
+ const postBatch = (currentIds) => this.withHistoricalApiVersionFallback('populate-workitems-batch', (apiVersion) => tfs_1.TFSServices.getItemContent(this.appendApiVersion(baseUrl, apiVersion), this.token, 'post', {
1808
+ $expand: 'Relations',
1809
+ ids: currentIds,
1810
+ })).then(({ result }) => result);
1811
+ const res = [];
1812
+ const divByMax = Math.floor(workItemsArray.length / 200);
1813
+ const modulusByMax = workItemsArray.length % 200;
1778
1814
  for (let i = 0; i < divByMax; i++) {
1779
- let from = i * 200;
1780
- let to = (i + 1) * 200;
1781
- let currentIds = workItemsArray.slice(from, to);
1815
+ const from = i * 200;
1816
+ const to = (i + 1) * 200;
1817
+ const currentIds = workItemsArray.slice(from, to);
1782
1818
  try {
1783
- let subRes = await tfs_1.TFSServices.getItemContent(url, this.token, 'post', {
1784
- $expand: 'Relations',
1785
- ids: currentIds,
1786
- });
1787
- res = [...res, ...subRes.value];
1819
+ const subRes = await postBatch(currentIds);
1820
+ res.push(...subRes.value);
1788
1821
  }
1789
1822
  catch (error) {
1790
1823
  logger_1.default.error(`error populating workitems array`);
@@ -1792,22 +1825,18 @@ class TicketsDataProvider {
1792
1825
  return [];
1793
1826
  }
1794
1827
  }
1795
- //compliting the rimainder
1796
1828
  if (modulusByMax !== 0) {
1797
1829
  try {
1798
- let currentIds = workItemsArray.slice(workItemsArray.length - modulusByMax, workItemsArray.length);
1799
- let subRes = await tfs_1.TFSServices.getItemContent(url, this.token, 'post', {
1800
- $expand: 'Relations',
1801
- ids: currentIds,
1802
- });
1803
- res = [...res, ...subRes.value];
1830
+ const currentIds = workItemsArray.slice(workItemsArray.length - modulusByMax, workItemsArray.length);
1831
+ const subRes = await postBatch(currentIds);
1832
+ res.push(...subRes.value);
1804
1833
  }
1805
1834
  catch (error) {
1806
1835
  logger_1.default.error(`error populating workitems array`);
1807
1836
  logger_1.default.error(JSON.stringify(error));
1808
1837
  return [];
1809
1838
  }
1810
- } //if
1839
+ }
1811
1840
  return res;
1812
1841
  }
1813
1842
  async GetModeledQueryResults(results, project) {