@elisra-devops/docgen-data-provider 1.79.0 → 1.81.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.
@@ -1,7 +1,7 @@
1
1
  import type {
2
2
  MewpBugLink,
3
3
  MewpExternalFileRef,
4
- MewpL3L4Link,
4
+ MewpL3L4Pair,
5
5
  } from '../models/mewp-reporting';
6
6
  import logger from './logger';
7
7
  import MewpExternalTableUtils from './mewpExternalTableUtils';
@@ -169,7 +169,7 @@ export default class MewpExternalIngestionUtils {
169
169
  public async loadExternalL3L4ByBaseKey(
170
170
  externalL3L4File: MewpExternalFileRef | null | undefined,
171
171
  adapters: MewpExternalIngestionAdapters
172
- ): Promise<Map<string, MewpL3L4Link[]>> {
172
+ ): Promise<Map<string, MewpL3L4Pair[]>> {
173
173
  const rows = await this.externalTableUtils.loadExternalTableRows(externalL3L4File, 'l3l4');
174
174
  const sourceName = String(
175
175
  externalL3L4File?.name || externalL3L4File?.objectName || externalL3L4File?.text || externalL3L4File?.url || ''
@@ -178,22 +178,36 @@ export default class MewpExternalIngestionUtils {
178
178
  if (sourceName) {
179
179
  logger.warn(`MEWP external L3/L4 ingestion: source '${sourceName}' loaded with 0 data rows.`);
180
180
  }
181
- return new Map<string, MewpL3L4Link[]>();
181
+ return new Map<string, MewpL3L4Pair[]>();
182
182
  }
183
183
  logger.info(`MEWP external L3/L4 ingestion: start source='${sourceName || 'unknown'}' rows=${rows.length}`);
184
184
 
185
- const linksByBaseKey = new Map<string, Map<string, MewpL3L4Link>>();
186
- const addLink = (baseKey: string, level: 'L3' | 'L4', id: number, title: string) => {
187
- if (!baseKey || !id) return;
188
- if (!linksByBaseKey.has(baseKey)) {
189
- linksByBaseKey.set(baseKey, new Map<string, MewpL3L4Link>());
185
+ const pairsByBaseKey = new Map<string, Map<string, MewpL3L4Pair>>();
186
+ const addPair = (baseKey: string, pair: MewpL3L4Pair) => {
187
+ const l3Id = String(pair?.l3Id || '').trim();
188
+ const l4Id = String(pair?.l4Id || '').trim();
189
+ if (!baseKey || (!l3Id && !l4Id)) return;
190
+ if (!pairsByBaseKey.has(baseKey)) {
191
+ pairsByBaseKey.set(baseKey, new Map<string, MewpL3L4Pair>());
190
192
  }
191
- const idKey = String(id).trim();
192
- const dedupeKey = `${level}:${idKey}`;
193
- linksByBaseKey.get(baseKey)!.set(dedupeKey, {
194
- id: idKey,
195
- title: String(title || '').trim(),
196
- level,
193
+ const key = `${l3Id}|${l4Id}`;
194
+ const byPair = pairsByBaseKey.get(baseKey)!;
195
+ const existing = byPair.get(key);
196
+ if (!existing) {
197
+ byPair.set(key, {
198
+ l3Id,
199
+ l3Title: String(pair?.l3Title || '').trim(),
200
+ l4Id,
201
+ l4Title: String(pair?.l4Title || '').trim(),
202
+ });
203
+ return;
204
+ }
205
+
206
+ byPair.set(key, {
207
+ l3Id: existing.l3Id || l3Id,
208
+ l3Title: existing.l3Title || String(pair?.l3Title || '').trim(),
209
+ l4Id: existing.l4Id || l4Id,
210
+ l4Title: existing.l4Title || String(pair?.l4Title || '').trim(),
197
211
  });
198
212
  };
199
213
 
@@ -265,28 +279,50 @@ export default class MewpExternalIngestionUtils {
265
279
  );
266
280
 
267
281
  if (area.includes('level 4')) {
282
+ // AREA 34 = Level 4 supports two modes:
283
+ // 1) Direct L2->L4: only Level-3 target column is populated; treat it as L4.
284
+ // 2) Paired L3+L4 in same row: Level-3 and Level-4 columns are both populated.
268
285
  const effectiveSapWbsLevel3 = targetSapWbsLevel3 || requirementSapWbsFallback;
269
- if (!targetIdLevel3) {
270
- parsedRows += 1;
271
- continue;
272
- }
273
- if (!adapters.isExternalStateInScope(targetStateLevel3, 'requirement')) {
274
- filteredByState += 1;
275
- parsedRows += 1;
276
- continue;
277
- }
278
- if (adapters.isExcludedL3L4BySapWbs(effectiveSapWbsLevel3)) {
279
- filteredBySapWbs += 1;
280
- parsedRows += 1;
281
- continue;
282
- }
283
- if (
284
- targetIdLevel3 &&
285
- adapters.isExternalStateInScope(targetStateLevel3, 'requirement') &&
286
- !adapters.isExcludedL3L4BySapWbs(effectiveSapWbsLevel3)
287
- ) {
288
- addLink(baseKey, 'L4', targetIdLevel3, targetTitleLevel3);
289
- acceptedL4 += 1;
286
+ const effectiveSapWbsLevel4 = targetSapWbsLevel4 || requirementSapWbsFallback;
287
+ const allowLevel3State = adapters.isExternalStateInScope(targetStateLevel3, 'requirement');
288
+ const allowLevel3SapWbs = !adapters.isExcludedL3L4BySapWbs(effectiveSapWbsLevel3);
289
+ const allowLevel4State = adapters.isExternalStateInScope(targetStateLevel4, 'requirement');
290
+ const allowLevel4SapWbs = !adapters.isExcludedL3L4BySapWbs(effectiveSapWbsLevel4);
291
+
292
+ const hasLevel4Columns = !!targetIdLevel4;
293
+ if (hasLevel4Columns) {
294
+ if (!allowLevel3State) filteredByState += 1;
295
+ if (!allowLevel3SapWbs) filteredBySapWbs += 1;
296
+ if (!allowLevel4State) filteredByState += 1;
297
+ if (!allowLevel4SapWbs) filteredBySapWbs += 1;
298
+
299
+ const includeL3 = !!(targetIdLevel3 && allowLevel3State && allowLevel3SapWbs);
300
+ const includeL4 = !!(targetIdLevel4 && allowLevel4State && allowLevel4SapWbs);
301
+ if (includeL3 || includeL4) {
302
+ addPair(baseKey, {
303
+ l3Id: includeL3 ? String(targetIdLevel3) : '',
304
+ l3Title: includeL3 ? targetTitleLevel3 : '',
305
+ l4Id: includeL4 ? String(targetIdLevel4) : '',
306
+ l4Title: includeL4 ? targetTitleLevel4 : '',
307
+ });
308
+ }
309
+ if (includeL3) acceptedL3 += 1;
310
+ if (includeL4) acceptedL4 += 1;
311
+ } else {
312
+ // Direct L2->L4 mode (legacy file semantics): Level-3 target column carries L4 ID/title.
313
+ const allowDirectL4State = adapters.isExternalStateInScope(targetStateLevel3, 'requirement');
314
+ const allowDirectL4SapWbs = !adapters.isExcludedL3L4BySapWbs(effectiveSapWbsLevel3);
315
+ if (!allowDirectL4State) filteredByState += 1;
316
+ if (!allowDirectL4SapWbs) filteredBySapWbs += 1;
317
+ if (targetIdLevel3 && allowDirectL4State && allowDirectL4SapWbs) {
318
+ addPair(baseKey, {
319
+ l3Id: '',
320
+ l3Title: '',
321
+ l4Id: String(targetIdLevel3),
322
+ l4Title: targetTitleLevel3,
323
+ });
324
+ acceptedL4 += 1;
325
+ }
290
326
  }
291
327
  parsedRows += 1;
292
328
  continue;
@@ -297,29 +333,41 @@ export default class MewpExternalIngestionUtils {
297
333
  const allowLevel3SapWbs = !adapters.isExcludedL3L4BySapWbs(effectiveSapWbsLevel3);
298
334
  if (!allowLevel3State) filteredByState += 1;
299
335
  if (!allowLevel3SapWbs) filteredBySapWbs += 1;
300
- if (targetIdLevel3 && allowLevel3State && allowLevel3SapWbs) {
301
- addLink(baseKey, 'L3', targetIdLevel3, targetTitleLevel3);
302
- acceptedL3 += 1;
303
- }
336
+
304
337
  const effectiveSapWbsLevel4 = targetSapWbsLevel4 || requirementSapWbsFallback;
305
338
  const allowLevel4State = adapters.isExternalStateInScope(targetStateLevel4, 'requirement');
306
339
  const allowLevel4SapWbs = !adapters.isExcludedL3L4BySapWbs(effectiveSapWbsLevel4);
307
340
  if (!allowLevel4State) filteredByState += 1;
308
341
  if (!allowLevel4SapWbs) filteredBySapWbs += 1;
309
- if (targetIdLevel4 && allowLevel4State && allowLevel4SapWbs) {
310
- addLink(baseKey, 'L4', targetIdLevel4, targetTitleLevel4);
311
- acceptedL4 += 1;
342
+
343
+ const includeL3 = !!(targetIdLevel3 && allowLevel3State && allowLevel3SapWbs);
344
+ const includeL4 = !!(targetIdLevel4 && allowLevel4State && allowLevel4SapWbs);
345
+ if (includeL3 || includeL4) {
346
+ addPair(baseKey, {
347
+ l3Id: includeL3 ? String(targetIdLevel3) : '',
348
+ l3Title: includeL3 ? targetTitleLevel3 : '',
349
+ l4Id: includeL4 ? String(targetIdLevel4) : '',
350
+ l4Title: includeL4 ? targetTitleLevel4 : '',
351
+ });
312
352
  }
353
+ if (includeL3) acceptedL3 += 1;
354
+ if (includeL4) acceptedL4 += 1;
313
355
  parsedRows += 1;
314
356
  }
315
357
 
316
- const out = new Map<string, MewpL3L4Link[]>();
317
- for (const [baseKey, linksById] of linksByBaseKey.entries()) {
358
+ const out = new Map<string, MewpL3L4Pair[]>();
359
+ for (const [baseKey, byPair] of pairsByBaseKey.entries()) {
318
360
  out.set(
319
361
  baseKey,
320
- [...linksById.values()].sort((a, b) => {
321
- if (a.level !== b.level) return a.level === 'L3' ? -1 : 1;
322
- return a.id.localeCompare(b.id);
362
+ [...byPair.values()].sort((a, b) => {
363
+ const aL3 = String(a?.l3Id || '');
364
+ const bL3 = String(b?.l3Id || '');
365
+ const l3Compare = aL3.localeCompare(bL3);
366
+ if (l3Compare !== 0) return l3Compare;
367
+
368
+ const aL4 = String(a?.l4Id || '');
369
+ const bL4 = String(b?.l4Id || '');
370
+ return aL4.localeCompare(bL4);
323
371
  })
324
372
  );
325
373
  }