@reventlessdev/reventless-core 3.0.0-alpha.236 → 3.0.0-alpha.237

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.
Files changed (30) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/package.json +7 -7
  3. package/src/admin/Platform_Admin_Structure.res +5 -1
  4. package/src/admin/Platform_Admin_Structure.res.mjs +4 -2
  5. package/src/admin/Platform_ComponentDefinitionsApi.res +4 -2
  6. package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +11 -3
  7. package/src/components/Api/GraphQL_FragmentGenerator.res +14 -1
  8. package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +1 -1
  9. package/src/components/Api/QueryDbListQuery.res +25 -1
  10. package/src/components/Api/QueryDbListQuery.res.mjs +11 -4
  11. package/src/components/Api/SuryToJsonSchema.res +39 -0
  12. package/src/components/Api/SuryToJsonSchema.res.mjs +34 -3
  13. package/src/plugin/component/Plugin_Structure.res +112 -15
  14. package/src/plugin/component/Plugin_Structure.res.mjs +77 -9
  15. package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +3 -2
  16. package/tests/admin/Platform_Admin_StructureTest.res +49 -0
  17. package/tests/admin/Platform_Admin_StructureTest.res.mjs +24 -0
  18. package/tests/admin/Platform_BakedManifestTest.res +3 -1
  19. package/tests/admin/Platform_BakedManifestTest.res.mjs +3 -1
  20. package/tests/admin/Platform_ComponentDefinitionsApiTest.res +14 -8
  21. package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +24 -12
  22. package/tests/admin/Platform_PluginStructuresApiTest.res +3 -1
  23. package/tests/admin/Platform_PluginStructuresApiTest.res.mjs +6 -2
  24. package/tests/api/Api_IdsTest.res.mjs +1 -1
  25. package/tests/api/SuryToJsonSchemaTest.res +141 -1
  26. package/tests/api/SuryToJsonSchemaTest.res.mjs +269 -54
  27. package/tests/plugin/PluginStructureTest.res +168 -12
  28. package/tests/plugin/PluginStructureTest.res.mjs +146 -9
  29. package/tests/plugin/StateViewSlice/PsAnnotatedView.res.mjs +3 -2
  30. package/tests/plugin/pluginDefinitionRequiredScalars.txt +2 -0
@@ -31,10 +31,11 @@ let emptySpec: Reventless.StateAnnotations.stateAnnotationSpec = {
31
31
  scanSort: [],
32
32
  semantic: [],
33
33
  metric: [],
34
- status: None,
34
+ lifecycle: None,
35
35
  groupBy: None,
36
36
  visibility: None,
37
37
  live: None,
38
+ retired: None,
38
39
  }
39
40
 
40
41
  describe("SuryToJsonSchema:", () => {
@@ -420,6 +421,145 @@ describe("SuryToJsonSchema:", () => {
420
421
  ))->toEqual((Some("sum"), None))
421
422
  })
422
423
 
424
+ testSync("emits x-reventless-retired {label,showWhenFalse} on the retired field", () => {
425
+ let schema = S.schema(s =>
426
+ {
427
+ "id": s.matches(S.string),
428
+ "archived": s.matches(S.bool),
429
+ }
430
+ )->S.castToUnknown
431
+ let schema' =
432
+ schema->withSpec({
433
+ ...emptySpec,
434
+ retired: Some({field: "archived", label: "Archived", showWhenFalse: true, values: None}),
435
+ })
436
+ let json = SuryToJsonSchema.deriveObjectSchema(schema')
437
+ let retiredObj =
438
+ getPropertyOf(json, "archived")->Option.flatMap(s => getProperty(s, "x-reventless-retired"))
439
+ expect((
440
+ retiredObj->Option.flatMap(r => getProperty(r, "label"))->Option.flatMap(JSON.Decode.string),
441
+ retiredObj
442
+ ->Option.flatMap(r => getProperty(r, "showWhenFalse"))
443
+ ->Option.flatMap(JSON.Decode.bool),
444
+ // Only the named field carries it.
445
+ getPropertyOf(json, "id")->Option.flatMap(s => getProperty(s, "x-reventless-retired")),
446
+ ))->toEqual((Some("Archived"), Some(true), None))
447
+ })
448
+
449
+ // The state form. `values` is what tells a consumer which of the two forms a
450
+ // view declared, so it travels on the same key and only when present.
451
+ let stateFormJson = (~values: array<string>) => {
452
+ let schema = S.schema(s =>
453
+ {
454
+ "customerId": s.matches(S.string),
455
+ "accountStatus": s.matches(S.union([S.literal("Active"), S.literal("Deactivated")])),
456
+ }
457
+ )->S.castToUnknown
458
+ schema
459
+ ->withSpec({
460
+ ...emptySpec,
461
+ lifecycle: Some("accountStatus"),
462
+ retired: Some({
463
+ field: "accountStatus",
464
+ label: "",
465
+ showWhenFalse: false,
466
+ values: Some(values),
467
+ }),
468
+ })
469
+ ->SuryToJsonSchema.deriveObjectSchema
470
+ }
471
+
472
+ let retiredOn = (json, field) =>
473
+ getPropertyOf(json, field)->Option.flatMap(s => getProperty(s, "x-reventless-retired"))
474
+
475
+ let stringsAt = (retiredObj, key) =>
476
+ retiredObj
477
+ ->Option.flatMap(r => getProperty(r, key))
478
+ ->Option.flatMap(JSON.Decode.array)
479
+ ->Option.map(a => a->Array.filterMap(JSON.Decode.string))
480
+
481
+ testSync("carries one retirement state as a one-element set", () => {
482
+ expect(
483
+ stateFormJson(~values=["Deactivated"])->retiredOn("accountStatus")->stringsAt("values"),
484
+ )->toEqual(Some(["Deactivated"]))
485
+ })
486
+
487
+ testSync("carries every retirement state a lifecycle declares", () => {
488
+ expect(
489
+ stateFormJson(~values=["Archived", "Discontinued"])
490
+ ->retiredOn("accountStatus")
491
+ ->stringsAt("values"),
492
+ )->toEqual(Some(["Archived", "Discontinued"]))
493
+ })
494
+
495
+ // The transitional key, for one release. A consumer pinned before the set
496
+ // degrades to nothing at all rather than to a missing badge — and enforcement
497
+ // never depended on it, so the whole of the lag is cosmetic either way.
498
+ testSync("emits the singular value beside a one-element set", () => {
499
+ expect(
500
+ stateFormJson(~values=["Deactivated"])
501
+ ->retiredOn("accountStatus")
502
+ ->Option.flatMap(r => getProperty(r, "value"))
503
+ ->Option.flatMap(JSON.Decode.string),
504
+ )->toEqual(Some("Deactivated"))
505
+ })
506
+
507
+ // There is no singular reading of two states, so the shim simply stops. A
508
+ // consumer that understands only `value` sees no retirement rather than one
509
+ // of the two picked arbitrarily.
510
+ testSync("omits the singular value for a set of two", () => {
511
+ expect(
512
+ stateFormJson(~values=["Archived", "Discontinued"])
513
+ ->retiredOn("accountStatus")
514
+ ->Option.flatMap(r => getProperty(r, "value")),
515
+ )->toEqual(None)
516
+ })
517
+
518
+ // Absent rather than null: a consumer reads the key's absence as "the boolean
519
+ // form", the same way it reads an absent label as "derive one from the name".
520
+ testSync("omits both value and values entirely on the boolean form", () => {
521
+ let schema = S.schema(s => {"archived": s.matches(S.bool)})->S.castToUnknown
522
+ let schema' =
523
+ schema->withSpec({
524
+ ...emptySpec,
525
+ retired: Some({field: "archived", label: "", showWhenFalse: false, values: None}),
526
+ })
527
+ let json = SuryToJsonSchema.deriveObjectSchema(schema')
528
+ let retiredObj = json->retiredOn("archived")
529
+ expect((
530
+ retiredObj->Option.flatMap(r => getProperty(r, "value")),
531
+ retiredObj->Option.flatMap(r => getProperty(r, "values")),
532
+ ))->toEqual((None, None))
533
+ })
534
+
535
+ testSync("omits the retired label when empty but always states showWhenFalse", () => {
536
+ let schema = S.schema(s => {"deactivated": s.matches(S.bool)})->S.castToUnknown
537
+ let schema' =
538
+ schema->withSpec({
539
+ ...emptySpec,
540
+ retired: Some({field: "deactivated", label: "", showWhenFalse: false, values: None}),
541
+ })
542
+ let json = SuryToJsonSchema.deriveObjectSchema(schema')
543
+ let retiredObj =
544
+ getPropertyOf(json, "deactivated")->Option.flatMap(s =>
545
+ getProperty(s, "x-reventless-retired")
546
+ )
547
+ expect((
548
+ retiredObj->Option.flatMap(r => getProperty(r, "label")),
549
+ retiredObj
550
+ ->Option.flatMap(r => getProperty(r, "showWhenFalse"))
551
+ ->Option.flatMap(JSON.Decode.bool),
552
+ ))->toEqual((None, Some(false)))
553
+ })
554
+
555
+ testSync("emits nothing for a schema with no retired annotation", () => {
556
+ let schema = S.schema(s => {"archived": s.matches(S.bool)})->S.castToUnknown
557
+ let json = SuryToJsonSchema.deriveObjectSchema(schema->withSpec(emptySpec))
558
+ expect(
559
+ getPropertyOf(json, "archived")->Option.flatMap(s => getProperty(s, "x-reventless-retired")),
560
+ )->toBe(None)
561
+ })
562
+
423
563
  testSync("emits x-reventless-group-by on the field named by groupBy", () => {
424
564
  let schema = S.schema(s =>
425
565
  {