@elisra-devops/docgen-data-provider 1.78.0 → 1.79.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.
@@ -42,6 +42,7 @@ export interface MewpL2RequirementWorkItem {
42
42
  }
43
43
 
44
44
  export interface MewpL2RequirementFamily {
45
+ workItemId?: number;
45
46
  requirementId: string;
46
47
  baseKey: string;
47
48
  title: string;
@@ -999,12 +999,16 @@ export default class ResultDataProvider {
999
999
  }
1000
1000
 
1001
1001
  private createMewpCoverageRow(
1002
- requirement: Pick<MewpL2RequirementFamily, 'requirementId' | 'title' | 'subSystem' | 'responsibility'>,
1002
+ requirement: Pick<
1003
+ MewpL2RequirementFamily,
1004
+ 'workItemId' | 'requirementId' | 'title' | 'subSystem' | 'responsibility'
1005
+ >,
1003
1006
  runStatus: MewpRunStatus,
1004
1007
  bug: MewpCoverageBugCell,
1005
1008
  linkedL3L4: MewpCoverageL3L4Cell
1006
1009
  ): MewpCoverageRow {
1007
- const l2ReqId = this.formatMewpCustomerId(requirement.requirementId);
1010
+ const l2ReqIdNumeric = Number(requirement?.workItemId || 0);
1011
+ const l2ReqId = l2ReqIdNumeric > 0 ? String(l2ReqIdNumeric) : '';
1008
1012
  const l2ReqTitle = this.toMewpComparableText(requirement.title);
1009
1013
  const l2SubSystem = this.toMewpComparableText(requirement.subSystem);
1010
1014
 
@@ -1050,15 +1054,6 @@ export default class ResultDataProvider {
1050
1054
  return rows;
1051
1055
  }
1052
1056
 
1053
- private formatMewpCustomerId(rawValue: string): string {
1054
- const normalized = this.normalizeMewpRequirementCode(this.toMewpComparableText(rawValue));
1055
- if (normalized) return normalized;
1056
-
1057
- const onlyDigits = String(rawValue || '').replace(/\D/g, '');
1058
- if (onlyDigits) return `SR${onlyDigits}`;
1059
- return '';
1060
- }
1061
-
1062
1057
  private buildMewpCoverageRows(
1063
1058
  requirements: MewpL2RequirementFamily[],
1064
1059
  requirementIndex: MewpRequirementIndex,
@@ -1790,7 +1785,7 @@ export default class ResultDataProvider {
1790
1785
  const workItems = await this.fetchWorkItemsByIds(projectName, requirementIds, true);
1791
1786
  const requirements = workItems.map((wi: any) => {
1792
1787
  const fields = wi?.fields || {};
1793
- const requirementId = this.extractMewpRequirementIdentifier(fields, Number(wi?.id || 0));
1788
+ const requirementId = this.extractMewpRequirementIdentifier(fields);
1794
1789
  const areaPath = this.toMewpComparableText(fields?.['System.AreaPath']);
1795
1790
  return {
1796
1791
  workItemId: Number(wi?.id || 0),
@@ -1878,6 +1873,7 @@ export default class ResultDataProvider {
1878
1873
 
1879
1874
  return [...families.entries()]
1880
1875
  .map(([baseKey, family]) => ({
1876
+ workItemId: Number(family?.representative?.workItemId || 0),
1881
1877
  requirementId: String(family?.representative?.requirementId || baseKey),
1882
1878
  baseKey,
1883
1879
  title: String(family?.representative?.title || ''),
@@ -2153,47 +2149,33 @@ export default class ResultDataProvider {
2153
2149
  return [...out].sort((a, b) => a - b);
2154
2150
  }
2155
2151
 
2156
- private extractMewpRequirementIdentifier(fields: Record<string, any>, fallbackWorkItemId: number): string {
2152
+ private extractMewpRequirementIdentifier(fields: Record<string, any>): string {
2157
2153
  const entries = Object.entries(fields || {});
2158
-
2159
- // First pass: only trusted identifier-like fields.
2160
- const strictHints = [
2154
+ const normalizeFieldKey = (value: string): string =>
2155
+ String(value || '')
2156
+ .toLowerCase()
2157
+ .replace(/[^a-z0-9]/g, '');
2158
+
2159
+ // Strict MEWP mode: only explicit MEWP customer-id fields are accepted.
2160
+ // API display name: "Customer ID"
2161
+ // API reference name: "Custom.CustomerID"
2162
+ const customerIdFieldKeys = new Set<string>([
2161
2163
  'customerid',
2162
- 'customer id',
2163
- 'customerrequirementid',
2164
- 'requirementid',
2165
- 'externalid',
2166
- 'srid',
2167
- 'sapwbsid',
2168
- ];
2169
- for (const [key, value] of entries) {
2170
- const normalizedKey = String(key || '').toLowerCase();
2171
- if (!strictHints.some((hint) => normalizedKey.includes(hint))) continue;
2172
-
2173
- const valueAsString = this.toMewpComparableText(value);
2174
- if (!valueAsString) continue;
2175
- const normalized = this.normalizeMewpRequirementCodeWithSuffix(valueAsString);
2176
- if (normalized) return normalized;
2177
- }
2164
+ 'customcustomerid',
2165
+ ]);
2178
2166
 
2179
- // Second pass: weaker hints, but still key-based only.
2180
- const looseHints = ['customer', 'requirement', 'external', 'sapwbs', 'sr'];
2181
2167
  for (const [key, value] of entries) {
2182
- const normalizedKey = String(key || '').toLowerCase();
2183
- if (!looseHints.some((hint) => normalizedKey.includes(hint))) continue;
2168
+ const normalizedKey = normalizeFieldKey(key);
2169
+ if (!customerIdFieldKeys.has(normalizedKey)) continue;
2184
2170
 
2185
2171
  const valueAsString = this.toMewpComparableText(value);
2186
2172
  if (!valueAsString) continue;
2187
- const normalized = this.normalizeMewpRequirementCodeWithSuffix(valueAsString);
2188
- if (normalized) return normalized;
2189
- }
2190
2173
 
2191
- // Optional fallback from title only (avoid scanning all fields and accidental SR matches).
2192
- const title = this.toMewpComparableText(fields?.['System.Title']);
2193
- const titleCode = this.normalizeMewpRequirementCodeWithSuffix(title);
2194
- if (titleCode) return titleCode;
2174
+ const normalizedRequirementId = this.normalizeMewpRequirementCodeWithSuffix(valueAsString);
2175
+ if (normalizedRequirementId) return normalizedRequirementId;
2176
+ }
2195
2177
 
2196
- return fallbackWorkItemId ? `SR${fallbackWorkItemId}` : '';
2178
+ return '';
2197
2179
  }
2198
2180
 
2199
2181
  private deriveMewpResponsibility(fields: Record<string, any>): string {
@@ -1178,9 +1178,9 @@ describe('ResultDataProvider', () => {
1178
1178
  })
1179
1179
  );
1180
1180
 
1181
- const covered = result.rows.find((row: any) => row['L2 REQ ID'] === 'SR1001');
1182
- const inferredByStepText = result.rows.find((row: any) => row['L2 REQ ID'] === 'SR1002');
1183
- const uncovered = result.rows.find((row: any) => row['L2 REQ ID'] === 'SR1003');
1181
+ const covered = result.rows.find((row: any) => row['L2 REQ ID'] === '5001');
1182
+ const inferredByStepText = result.rows.find((row: any) => row['L2 REQ ID'] === '5002');
1183
+ const uncovered = result.rows.find((row: any) => row['L2 REQ ID'] === '5003');
1184
1184
 
1185
1185
  expect(covered).toEqual(
1186
1186
  expect.objectContaining({
@@ -1297,7 +1297,7 @@ describe('ResultDataProvider', () => {
1297
1297
  [1]
1298
1298
  );
1299
1299
 
1300
- const row = result.rows.find((item: any) => item['L2 REQ ID'] === 'SR2001');
1300
+ const row = result.rows.find((item: any) => item['L2 REQ ID'] === '7001');
1301
1301
  expect(parseSpy).not.toHaveBeenCalled();
1302
1302
  expect(row).toEqual(
1303
1303
  expect.objectContaining({
@@ -1312,11 +1312,10 @@ describe('ResultDataProvider', () => {
1312
1312
  'System.Description': 'random text with SR9999 that is unrelated',
1313
1313
  'Custom.CustomerId': 'customer id unknown',
1314
1314
  'System.Title': 'Requirement without explicit SR code',
1315
- },
1316
- 4321
1315
+ }
1317
1316
  );
1318
1317
 
1319
- expect(requirementId).toBe('SR4321');
1318
+ expect(requirementId).toBe('');
1320
1319
  });
1321
1320
 
1322
1321
  it('should derive responsibility from Custom.SAPWBS when present', () => {