@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.
- package/bin/modules/TicketsDataProvider.d.ts +2 -0
- package/bin/modules/TicketsDataProvider.js +72 -43
- package/bin/modules/TicketsDataProvider.js.map +1 -1
- package/bin/tests/modules/ticketsDataProvider.test.js +150 -34
- package/bin/tests/modules/ticketsDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/TicketsDataProvider.ts +94 -51
- package/src/tests/modules/ticketsDataProvider.test.ts +172 -39
|
@@ -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:
|
|
581
|
-
//
|
|
582
|
-
//
|
|
583
|
-
//
|
|
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 (
|
|
586
|
-
customerRequirementsQueries = await this.
|
|
611
|
+
if (sysRsRootFound) {
|
|
612
|
+
customerRequirementsQueries = await this.fetchCustomerRequirementQueries(sysRsRoot);
|
|
587
613
|
}
|
|
588
614
|
else {
|
|
589
|
-
logger_1.default.debug('[GetSharedQueries][sysrs]
|
|
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
|
-
|
|
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
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
//
|
|
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
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1815
|
+
const from = i * 200;
|
|
1816
|
+
const to = (i + 1) * 200;
|
|
1817
|
+
const currentIds = workItemsArray.slice(from, to);
|
|
1782
1818
|
try {
|
|
1783
|
-
|
|
1784
|
-
|
|
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
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
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
|
-
}
|
|
1839
|
+
}
|
|
1811
1840
|
return res;
|
|
1812
1841
|
}
|
|
1813
1842
|
async GetModeledQueryResults(results, project) {
|