@cap-js-community/common 0.1.6 → 0.1.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js-community/common",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "CAP Node.js Community Common",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "engines": {
@@ -46,7 +46,7 @@
46
46
  "dependencies": {
47
47
  "@cap-js/sqlite": "^1.11.0",
48
48
  "commander": "^13.1.0",
49
- "redis": "^4.7.0",
49
+ "redis": "^4.7.1",
50
50
  "verror": "^1.10.1"
51
51
  },
52
52
  "devDependencies": {
@@ -8,7 +8,7 @@ const crypto = require("crypto");
8
8
  const COMPONENT_NAME = "migrationCheck";
9
9
  const STRING_DEFAULT_LENGTH = 5000;
10
10
 
11
- const Checks = [releasedEntityCheck, newEntityCheck, uniqueIndexCheck];
11
+ const Checks = [releasedEntityCheck, newEntityCheck, uniqueIndexCheck, journalModeCheck];
12
12
  const Messages = {
13
13
  ReleasedEntityCannotBeRemoved: "A released entity cannot be removed",
14
14
  ReleasedEntityDraftEnablementCannotBeChanged: "The draft enablement state of a released entity cannot be changed",
@@ -25,6 +25,8 @@ const Messages = {
25
25
  ReleasedElementCardinalityCannotBeChanged: "The cardinality of a released element cannot be changed",
26
26
  ReleasedElementOnConditionCannotBeChanged: "The ON condition of a released element cannot be changed",
27
27
  ReleasedElementKeysConditionCannotBeChanged: "The keys condition of a released element cannot be changed",
28
+ ReleasedEntityJournalModeAndEntityChangeIsNotAllowed:
29
+ "Enabling journal mode and changing entity in same cycle is not allowed",
28
30
  ReleasedEntityIndexChangeIsNotAllowed: "Changes to the index of a released entity are not allowed",
29
31
  ReleasedEntityIndexChangeIsNotWhitelisted: "Changes to the index of a released entity must be whitelisted",
30
32
  ReleasedElementTypeExtensionIsNotWhitelisted: "Extending the type of a released element requires whitelisting",
@@ -310,200 +312,256 @@ class MigrationCheck {
310
312
 
311
313
  function releasedEntityCheck(csnBuild, csnProd, whitelist, options) {
312
314
  const messages = [];
313
- visitPersistenceEntities(csnProd, (definitionProd) => {
314
- let lookupName = definitionProd.name;
315
- if (lookupName.startsWith("cds.xt.") && !options.checkMtx) {
316
- return;
317
- }
318
- const definitionBuild = csnBuild.definitions[lookupName];
319
- if (!definitionBuild) {
320
- report(messages, MessagesCodes.ReleasedEntityCannotBeRemoved, definitionProd.name);
321
- return;
322
- }
323
- if (definitionProd["@odata.draft.enabled"] !== definitionBuild["@odata.draft.enabled"]) {
324
- report(messages, MessagesCodes.ReleasedEntityDraftEnablementCannotBeChanged, definitionProd.name);
325
- }
326
- const definitionWhitelist = whitelist.definitions && whitelist.definitions[definitionProd.name];
327
- Object.keys(definitionProd.elements || {}).forEach((elementProdName) => {
328
- const elementProd = definitionProd.elements[elementProdName];
329
- const elementBuild = definitionBuild.elements[elementProdName];
330
- const elementWhitelist =
331
- definitionWhitelist && definitionWhitelist.elements && definitionWhitelist.elements[elementProdName];
332
- if (elementBuild) {
333
- if (["cds.Association", "cds.Composition"].includes(elementProd.type)) {
334
- if (!((elementProd.on && elementBuild.on) || (elementProd.keys && elementBuild.keys))) {
335
- report(
336
- messages,
337
- MessagesCodes.ReleasedElementManagedUnmanagedCannotBeChanged,
338
- definitionProd.name,
339
- elementProdName,
340
- );
341
- return;
342
- }
343
- }
315
+ visitPersistenceEntities(
316
+ csnProd,
317
+ (definitionProd) => {
318
+ let lookupName = definitionProd.name;
319
+ if (lookupName.startsWith("cds.xt.") && !options.checkMtx) {
320
+ return;
321
+ }
322
+ const definitionBuild = csnBuild.definitions[lookupName];
323
+ if (!definitionBuild) {
324
+ report(messages, MessagesCodes.ReleasedEntityCannotBeRemoved, definitionProd.name);
325
+ return;
344
326
  }
345
- if (elementProd.on) {
346
- return; // Skip unmanaged association / composition
327
+ if (definitionProd["@odata.draft.enabled"] !== definitionBuild["@odata.draft.enabled"]) {
328
+ report(messages, MessagesCodes.ReleasedEntityDraftEnablementCannotBeChanged, definitionProd.name);
347
329
  }
348
- if (!elementBuild) {
349
- if (!elementProd.virtual) {
350
- report(messages, MessagesCodes.ReleasedElementCannotBeRemoved, definitionProd.name, elementProdName);
330
+ const definitionWhitelist = whitelist.definitions && whitelist.definitions[definitionProd.name];
331
+ Object.keys(definitionProd.elements || {}).forEach((elementProdName) => {
332
+ const elementProd = definitionProd.elements[elementProdName];
333
+ const elementBuild = definitionBuild.elements[elementProdName];
334
+ const elementWhitelist =
335
+ definitionWhitelist && definitionWhitelist.elements && definitionWhitelist.elements[elementProdName];
336
+ if (elementBuild) {
337
+ if (["cds.Association", "cds.Composition"].includes(elementProd.type)) {
338
+ if (!((elementProd.on && elementBuild.on) || (elementProd.keys && elementBuild.keys))) {
339
+ report(
340
+ messages,
341
+ MessagesCodes.ReleasedElementManagedUnmanagedCannotBeChanged,
342
+ definitionProd.name,
343
+ elementProdName,
344
+ );
345
+ return;
346
+ }
347
+ }
351
348
  }
352
- } else if (elementProd.key !== elementBuild.key) {
353
- report(messages, MessagesCodes.ReleasedElementKeyCannotBeChanged, definitionProd.name, elementProdName);
354
- } else if (elementProd.virtual !== elementBuild.virtual) {
355
- report(messages, MessagesCodes.ReleasedElementVirtualCannotBeChanged, definitionProd.name, elementProdName);
356
- } else if (elementProd.localized && !elementBuild.localized) {
357
- report(
358
- messages,
359
- MessagesCodes.ReleasedElementLocalizationCannotBeChanged,
360
- definitionProd.name,
361
- elementProdName,
362
- );
363
- } else if (!elementProd.notNull && elementBuild.notNull) {
364
- report(messages, MessagesCodes.ReleasedElementNullableCannotBeChanged, definitionProd.name, elementProdName);
365
- } else if (normalizeType(csnProd, elementProd.type) !== normalizeType(csnBuild, elementBuild.type)) {
366
- report(messages, MessagesCodes.ReleasedElementTypeCannotBeChanged, definitionProd.name, elementProdName);
367
- } else if ((elementProd.length || STRING_DEFAULT_LENGTH) > (elementBuild.length || STRING_DEFAULT_LENGTH)) {
368
- report(messages, MessagesCodes.ReleasedElementTypeCannotBeShortened, definitionProd.name, elementProdName);
369
- } else if ((elementProd.length || STRING_DEFAULT_LENGTH) < (elementBuild.length || STRING_DEFAULT_LENGTH)) {
370
- if (!elementWhitelist && options.whitelist) {
371
- report(
372
- messages,
373
- MessagesCodes.ReleasedElementTypeExtensionIsNotWhitelisted,
374
- definitionProd.name,
375
- elementProdName,
376
- );
349
+ if (elementProd.on) {
350
+ return; // Skip unmanaged association / composition
377
351
  }
378
- } else if (elementProd.scale > elementBuild.scale || elementProd.precision > elementBuild.precision) {
379
- report(
380
- messages,
381
- MessagesCodes.ReleasedElementScalePrecisionCannotBeLower,
382
- definitionProd.name,
383
- elementProdName,
384
- );
385
- } else if (elementProd.scale < elementBuild.scale || elementProd.precision < elementBuild.precision) {
386
- if (!elementWhitelist && options.whitelist) {
352
+ if (!elementBuild) {
353
+ if (!elementProd.virtual) {
354
+ report(messages, MessagesCodes.ReleasedElementCannotBeRemoved, definitionProd.name, elementProdName);
355
+ }
356
+ } else if (elementProd.key !== elementBuild.key) {
357
+ report(messages, MessagesCodes.ReleasedElementKeyCannotBeChanged, definitionProd.name, elementProdName);
358
+ } else if (elementProd.virtual !== elementBuild.virtual) {
359
+ report(messages, MessagesCodes.ReleasedElementVirtualCannotBeChanged, definitionProd.name, elementProdName);
360
+ } else if (elementProd.localized && !elementBuild.localized) {
387
361
  report(
388
362
  messages,
389
- MessagesCodes.ReleasedElementScalePrecisionExtensionIsNotWhitelisted,
363
+ MessagesCodes.ReleasedElementLocalizationCannotBeChanged,
390
364
  definitionProd.name,
391
365
  elementProdName,
392
366
  );
393
- }
394
- } else if (elementProd.target !== elementBuild.target) {
395
- if (
396
- isPersistenceEntity(csnProd, elementProd.target) ||
397
- isPersistenceEntity(csnBuild, elementBuild.target) ||
398
- JSON.stringify(entityKeyInfo(csnProd, elementProd.target)) !==
399
- JSON.stringify(entityKeyInfo(csnBuild, elementBuild.target))
400
- ) {
401
- report(messages, MessagesCodes.ReleasedElementTargetCannotBeChanged, definitionProd.name, elementProdName);
402
- }
403
- } else if (
404
- (elementProd.cardinality && elementProd.cardinality.max) !==
405
- (elementBuild.cardinality && elementBuild.cardinality.max)
406
- ) {
407
- report(messages, MessagesCodes.ReleasedElementCardinalityCannotBeChanged, definitionProd.name, elementProdName);
408
- } else if (JSON.stringify(elementProd.on) !== JSON.stringify(elementBuild.on)) {
409
- if (isPersistenceEntity(csnProd, elementProd.target) || isPersistenceEntity(csnBuild, elementBuild.target)) {
367
+ } else if (!elementProd.notNull && elementBuild.notNull) {
368
+ report(messages, MessagesCodes.ReleasedElementNullableCannotBeChanged, definitionProd.name, elementProdName);
369
+ } else if (normalizeType(csnProd, elementProd.type) !== normalizeType(csnBuild, elementBuild.type)) {
370
+ report(messages, MessagesCodes.ReleasedElementTypeCannotBeChanged, definitionProd.name, elementProdName);
371
+ } else if ((elementProd.length || STRING_DEFAULT_LENGTH) > (elementBuild.length || STRING_DEFAULT_LENGTH)) {
372
+ report(messages, MessagesCodes.ReleasedElementTypeCannotBeShortened, definitionProd.name, elementProdName);
373
+ } else if ((elementProd.length || STRING_DEFAULT_LENGTH) < (elementBuild.length || STRING_DEFAULT_LENGTH)) {
374
+ if (!elementWhitelist && options.whitelist) {
375
+ report(
376
+ messages,
377
+ MessagesCodes.ReleasedElementTypeExtensionIsNotWhitelisted,
378
+ definitionProd.name,
379
+ elementProdName,
380
+ );
381
+ }
382
+ } else if (elementProd.scale > elementBuild.scale || elementProd.precision > elementBuild.precision) {
410
383
  report(
411
384
  messages,
412
- MessagesCodes.ReleasedElementOnConditionCannotBeChanged,
385
+ MessagesCodes.ReleasedElementScalePrecisionCannotBeLower,
413
386
  definitionProd.name,
414
387
  elementProdName,
415
388
  );
416
- }
417
- } else if (JSON.stringify(elementProd.keys) !== JSON.stringify(elementBuild.keys)) {
418
- if (isPersistenceEntity(csnProd, elementProd.target) || isPersistenceEntity(csnBuild, elementBuild.target)) {
389
+ } else if (elementProd.scale < elementBuild.scale || elementProd.precision < elementBuild.precision) {
390
+ if (!elementWhitelist && options.whitelist) {
391
+ report(
392
+ messages,
393
+ MessagesCodes.ReleasedElementScalePrecisionExtensionIsNotWhitelisted,
394
+ definitionProd.name,
395
+ elementProdName,
396
+ );
397
+ }
398
+ } else if (elementProd.target !== elementBuild.target) {
399
+ if (
400
+ isPersistenceEntity(csnProd, elementProd.target) ||
401
+ isPersistenceEntity(csnBuild, elementBuild.target) ||
402
+ JSON.stringify(entityKeyInfo(csnProd, elementProd.target)) !==
403
+ JSON.stringify(entityKeyInfo(csnBuild, elementBuild.target))
404
+ ) {
405
+ report(messages, MessagesCodes.ReleasedElementTargetCannotBeChanged, definitionProd.name, elementProdName);
406
+ }
407
+ } else if (
408
+ (elementProd.cardinality && elementProd.cardinality.max) !==
409
+ (elementBuild.cardinality && elementBuild.cardinality.max)
410
+ ) {
419
411
  report(
420
412
  messages,
421
- MessagesCodes.ReleasedElementKeysConditionCannotBeChanged,
413
+ MessagesCodes.ReleasedElementCardinalityCannotBeChanged,
422
414
  definitionProd.name,
423
415
  elementProdName,
424
416
  );
417
+ } else if (JSON.stringify(elementProd.on) !== JSON.stringify(elementBuild.on)) {
418
+ if (isPersistenceEntity(csnProd, elementProd.target) || isPersistenceEntity(csnBuild, elementBuild.target)) {
419
+ report(
420
+ messages,
421
+ MessagesCodes.ReleasedElementOnConditionCannotBeChanged,
422
+ definitionProd.name,
423
+ elementProdName,
424
+ );
425
+ }
426
+ } else if (JSON.stringify(elementProd.keys) !== JSON.stringify(elementBuild.keys)) {
427
+ if (isPersistenceEntity(csnProd, elementProd.target) || isPersistenceEntity(csnBuild, elementBuild.target)) {
428
+ report(
429
+ messages,
430
+ MessagesCodes.ReleasedElementKeysConditionCannotBeChanged,
431
+ definitionProd.name,
432
+ elementProdName,
433
+ );
434
+ }
425
435
  }
426
- }
427
- });
428
- });
436
+ });
437
+ },
438
+ options.filter,
439
+ );
429
440
  return messages;
430
441
  }
431
442
 
432
443
  function newEntityCheck(csnBuild, csnProd, whitelist, options) {
433
444
  const messages = [];
434
- visitPersistenceEntities(csnBuild, (definitionBuild, { draft } = {}) => {
435
- let lookupName = definitionBuild.name;
436
- const definitionProd = csnProd.definitions[lookupName];
437
- const definitionWhitelist = whitelist.definitions && whitelist.definitions[definitionBuild.name];
438
- if (!definitionProd && !definitionWhitelist && options.whitelist) {
439
- report(messages, MessagesCodes.NewEntityIsNotWhitelisted, definitionBuild.name);
440
- return;
441
- }
442
- if (definitionProd) {
443
- Object.keys(definitionBuild.elements || {}).forEach((elementBuildName) => {
444
- const elementBuild = definitionBuild.elements[elementBuildName];
445
- if (elementBuild.virtual) {
446
- return;
447
- }
448
- const elementProd = definitionProd.elements[elementBuildName];
449
- const elementWhitelist =
450
- definitionWhitelist && definitionWhitelist.elements && definitionWhitelist.elements[elementBuildName];
451
- if (!elementProd) {
452
- if (!elementWhitelist && options.whitelist) {
453
- report(messages, MessagesCodes.NewEntityElementIsNotWhitelisted, definitionBuild.name, elementBuildName);
445
+ visitPersistenceEntities(
446
+ csnBuild,
447
+ (definitionBuild, { draft } = {}) => {
448
+ let lookupName = definitionBuild.name;
449
+ const definitionProd = csnProd.definitions[lookupName];
450
+ const definitionWhitelist = whitelist.definitions && whitelist.definitions[definitionBuild.name];
451
+ if (!definitionProd && !definitionWhitelist && options.whitelist) {
452
+ report(messages, MessagesCodes.NewEntityIsNotWhitelisted, definitionBuild.name);
453
+ return;
454
+ }
455
+ if (definitionProd) {
456
+ Object.keys(definitionBuild.elements || {}).forEach((elementBuildName) => {
457
+ const elementBuild = definitionBuild.elements[elementBuildName];
458
+ if (elementBuild.virtual) {
459
+ return;
454
460
  }
455
- if (
456
- !draft &&
457
- elementBuild.notNull &&
458
- (elementBuild.default === undefined || elementBuild.default?.val === null)
459
- ) {
460
- report(messages, MessagesCodes.NewEntityElementNotNullableDefault, definitionBuild.name, elementBuildName);
461
+ const elementProd = definitionProd.elements[elementBuildName];
462
+ const elementWhitelist =
463
+ definitionWhitelist && definitionWhitelist.elements && definitionWhitelist.elements[elementBuildName];
464
+ if (!elementProd) {
465
+ if (!elementWhitelist && options.whitelist) {
466
+ report(messages, MessagesCodes.NewEntityElementIsNotWhitelisted, definitionBuild.name, elementBuildName);
467
+ }
468
+ if (
469
+ !draft &&
470
+ elementBuild.notNull &&
471
+ (elementBuild.default === undefined || elementBuild.default?.val === null)
472
+ ) {
473
+ report(
474
+ messages,
475
+ MessagesCodes.NewEntityElementNotNullableDefault,
476
+ definitionBuild.name,
477
+ elementBuildName,
478
+ );
479
+ }
461
480
  }
462
- }
463
- });
464
- }
465
- });
481
+ });
482
+ }
483
+ },
484
+ options.filter,
485
+ );
466
486
  return messages;
467
487
  }
468
488
 
469
489
  function uniqueIndexCheck(csnBuild, csnProd, whitelist, options) {
470
490
  const messages = [];
471
- visitPersistenceEntities(csnBuild, (definitionBuild) => {
472
- const definitionWhitelist = whitelist.definitions && whitelist.definitions[definitionBuild.name];
473
- const definitionProd = csnProd.definitions[definitionBuild.name];
474
- if (definitionProd) {
475
- Object.keys(definitionBuild).forEach((key) => {
476
- if (key.startsWith("@assert.unique.")) {
477
- const uniqueIndexAnnotationBuild = definitionBuild[key];
478
- const uniqueIndexAnnotationProd = definitionProd[key];
479
- if (uniqueIndexAnnotationBuild && !uniqueIndexAnnotationProd && !definitionWhitelist && options.whitelist) {
480
- report(messages, MessagesCodes.NewEntityIndexIsNotWhitelisted, definitionBuild.name);
481
- } else if (uniqueIndexAnnotationBuild && uniqueIndexAnnotationProd) {
482
- const checkProd = uniqueIndexAnnotationProd.every((indexPartProd) => {
483
- return uniqueIndexAnnotationBuild.find((indexPartBuild) => {
484
- return (indexPartProd["="] || indexPartProd) === (indexPartBuild["="] || indexPartBuild);
491
+ visitPersistenceEntities(
492
+ csnBuild,
493
+ (definitionBuild) => {
494
+ const definitionWhitelist = whitelist.definitions && whitelist.definitions[definitionBuild.name];
495
+ const definitionProd = csnProd.definitions[definitionBuild.name];
496
+ if (definitionProd) {
497
+ Object.keys(definitionBuild).forEach((key) => {
498
+ if (key.startsWith("@assert.unique.")) {
499
+ const uniqueIndexAnnotationBuild = definitionBuild[key];
500
+ const uniqueIndexAnnotationProd = definitionProd[key];
501
+ if (uniqueIndexAnnotationBuild && !uniqueIndexAnnotationProd && !definitionWhitelist && options.whitelist) {
502
+ report(messages, MessagesCodes.NewEntityIndexIsNotWhitelisted, definitionBuild.name);
503
+ } else if (uniqueIndexAnnotationBuild && uniqueIndexAnnotationProd) {
504
+ const checkProd = uniqueIndexAnnotationProd.every((indexPartProd) => {
505
+ return uniqueIndexAnnotationBuild.find((indexPartBuild) => {
506
+ return (indexPartProd["="] || indexPartProd) === (indexPartBuild["="] || indexPartBuild);
507
+ });
485
508
  });
486
- });
487
- if (!checkProd) {
488
- report(messages, MessagesCodes.ReleasedEntityIndexChangeIsNotAllowed, definitionBuild.name);
489
- }
490
- const checkBuild = uniqueIndexAnnotationBuild.every((indexPartBuild) => {
491
- return uniqueIndexAnnotationProd.find((indexPartProd) => {
492
- return (indexPartBuild["="] || indexPartBuild) === (indexPartProd["="] || indexPartProd);
509
+ if (!checkProd) {
510
+ report(messages, MessagesCodes.ReleasedEntityIndexChangeIsNotAllowed, definitionBuild.name);
511
+ }
512
+ const checkBuild = uniqueIndexAnnotationBuild.every((indexPartBuild) => {
513
+ return uniqueIndexAnnotationProd.find((indexPartProd) => {
514
+ return (indexPartBuild["="] || indexPartBuild) === (indexPartProd["="] || indexPartProd);
515
+ });
493
516
  });
494
- });
495
- if (!checkBuild && !definitionWhitelist && options.whitelist) {
496
- report(messages, MessagesCodes.ReleasedEntityIndexChangeIsNotWhitelisted, definitionBuild.name);
517
+ if (!checkBuild && !definitionWhitelist && options.whitelist) {
518
+ report(messages, MessagesCodes.ReleasedEntityIndexChangeIsNotWhitelisted, definitionBuild.name);
519
+ }
497
520
  }
498
521
  }
522
+ });
523
+ }
524
+ },
525
+ options.filter,
526
+ );
527
+ return messages;
528
+ }
529
+
530
+ function journalModeCheck(csnBuild, csnProd, whitelist, options) {
531
+ const messages = [];
532
+ if (options.check === "journalModeCheck") {
533
+ // Recursion
534
+ return messages;
535
+ }
536
+ visitPersistenceEntities(
537
+ csnBuild,
538
+ (definitionBuild) => {
539
+ const definitionProd = csnProd.definitions[definitionBuild.name];
540
+ if (definitionProd) {
541
+ if (definitionBuild["@cds.persistence.journal"] && !definitionProd["@cds.persistence.journal"]) {
542
+ const entityMessages = Checks.reduce((messages, check) => {
543
+ messages.push(
544
+ ...check(
545
+ csnBuild,
546
+ csnProd,
547
+ {},
548
+ { ...options, filter: [definitionBuild.name], check: "journalModeCheck" },
549
+ ),
550
+ );
551
+ return messages;
552
+ }, []);
553
+ if (entityMessages.length > 0) {
554
+ report(messages, MessagesCodes.ReleasedEntityJournalModeAndEntityChangeIsNotAllowed, definitionBuild.name);
555
+ }
499
556
  }
500
- });
501
- }
502
- });
557
+ }
558
+ },
559
+ options.filter,
560
+ );
503
561
  return messages;
504
562
  }
505
563
 
506
- function visitPersistenceEntities(csn, onEntity) {
564
+ function visitPersistenceEntities(csn, onEntity, filter) {
507
565
  if (!onEntity) {
508
566
  return;
509
567
  }
@@ -511,6 +569,9 @@ function visitPersistenceEntities(csn, onEntity) {
511
569
  return csn.definitions[name].kind === "service";
512
570
  });
513
571
  return Object.keys(csn.definitions).forEach((name) => {
572
+ if (filter && !filter.includes(name)) {
573
+ return;
574
+ }
514
575
  // Normal persistence entity
515
576
  const definition = csn.definitions[name];
516
577
  if (