@jskit-ai/crud-server-generator 0.1.63 → 0.1.65
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.descriptor.mjs +13 -30
- package/package.json +8 -8
- package/src/server/buildTemplateContext.js +449 -496
- package/src/server/subcommands/addField.js +8 -80
- package/src/server/subcommands/resourceAst.js +40 -393
- package/src/shared/crud/crudResource.js +85 -185
- package/templates/src/local-package/package.descriptor.mjs +3 -6
- package/templates/src/local-package/package.json +0 -1
- package/templates/src/local-package/server/CrudProvider.js +28 -21
- package/templates/src/local-package/server/actions.js +42 -54
- package/templates/src/local-package/server/registerRoutes.js +22 -50
- package/templates/src/local-package/server/repository.js +82 -38
- package/templates/src/local-package/server/service.js +45 -73
- package/templates/src/local-package/shared/crudResource.js +15 -140
- package/test/addFieldSubcommand.test.js +100 -77
- package/test/buildTemplateContext.test.js +139 -203
- package/test/crudResource.test.js +26 -31
- package/test/crudServerGuards.test.js +157 -42
- package/test/crudService.test.js +91 -173
- package/test/packageDescriptor.test.js +3 -11
- package/test/routeInputContracts.test.js +77 -8
- package/test/templateSymbolConsistency.test.js +19 -3
- package/test-support/templateServerFixture.js +155 -112
- package/templates/src/local-package/server/actionIds.js +0 -9
- package/templates/src/local-package/server/listConfig.js +0 -5
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
toKnexClientId
|
|
10
10
|
} from "@jskit-ai/database-runtime/shared";
|
|
11
11
|
import { resolveCrudSurfacePolicyFromAppConfig } from "@jskit-ai/crud-core/server/crudModuleConfig";
|
|
12
|
-
import { checkCrudLookupFormControl } from "@jskit-ai/crud-core/shared/
|
|
12
|
+
import { checkCrudLookupFormControl } from "@jskit-ai/crud-core/shared/crudFieldSupport";
|
|
13
13
|
import {
|
|
14
14
|
importFreshModuleFromAbsolutePath,
|
|
15
15
|
loadAppConfigFromModuleUrl,
|
|
@@ -663,322 +663,408 @@ function renderObjectPropertyKey(value) {
|
|
|
663
663
|
return isIdentifier(key) ? key : JSON.stringify(key);
|
|
664
664
|
}
|
|
665
665
|
|
|
666
|
-
function
|
|
667
|
-
const
|
|
668
|
-
if (isIdentifier(normalizedKey)) {
|
|
669
|
-
return `${sourceName}.${normalizedKey}`;
|
|
670
|
-
}
|
|
671
|
-
return `${sourceName}[${JSON.stringify(normalizedKey)}]`;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
function renderIntegerSchema(column) {
|
|
675
|
-
const options = [];
|
|
666
|
+
function renderBoundedNumberEntries(column) {
|
|
667
|
+
const entries = [];
|
|
676
668
|
if (Number.isFinite(column?.minimum)) {
|
|
677
|
-
|
|
678
|
-
} else if (
|
|
679
|
-
|
|
680
|
-
} else if (column.unsigned === true) {
|
|
681
|
-
options.push("minimum: 0");
|
|
669
|
+
entries.push(`min: ${column.minimum}`);
|
|
670
|
+
} else if (column?.unsigned === true) {
|
|
671
|
+
entries.push("min: 0");
|
|
682
672
|
}
|
|
683
673
|
if (Number.isFinite(column?.maximum)) {
|
|
684
|
-
|
|
685
|
-
}
|
|
686
|
-
if (Number.isFinite(column?.exclusiveMaximum)) {
|
|
687
|
-
options.push(`exclusiveMaximum: ${column.exclusiveMaximum}`);
|
|
674
|
+
entries.push(`max: ${column.maximum}`);
|
|
688
675
|
}
|
|
689
|
-
|
|
690
|
-
return `Type.Integer({ ${options.join(", ")} })`;
|
|
691
|
-
}
|
|
692
|
-
return "Type.Integer()";
|
|
676
|
+
return entries;
|
|
693
677
|
}
|
|
694
678
|
|
|
695
|
-
function
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
options.push(`enum: ${JSON.stringify(enumValues)}`);
|
|
679
|
+
function resolveCanonicalResourceFieldRequired(column = {}) {
|
|
680
|
+
return column?.nullable !== true && column?.hasDefault !== true;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function renderCanonicalResourceOperations(column = {}) {
|
|
684
|
+
if (column?.isOwnerColumn === true) {
|
|
685
|
+
return "{}";
|
|
703
686
|
}
|
|
704
|
-
|
|
705
|
-
|
|
687
|
+
|
|
688
|
+
const entries = [
|
|
689
|
+
'output: { required: true }'
|
|
690
|
+
];
|
|
691
|
+
|
|
692
|
+
if (column?.writable === true) {
|
|
693
|
+
entries.push(`create: { required: ${resolveCanonicalResourceFieldRequired(column)} }`);
|
|
694
|
+
entries.push("patch: { required: false }");
|
|
706
695
|
}
|
|
707
|
-
|
|
696
|
+
|
|
697
|
+
return [
|
|
698
|
+
"{",
|
|
699
|
+
...entries.map((entry, index) => ` ${entry}${index < entries.length - 1 ? "," : ""}`),
|
|
700
|
+
"}"
|
|
701
|
+
].join("\n");
|
|
708
702
|
}
|
|
709
703
|
|
|
710
|
-
function
|
|
711
|
-
|
|
712
|
-
const typeKind = String(column
|
|
704
|
+
function renderCanonicalResourceFieldSchema(column, { fieldContractEntry = null } = {}) {
|
|
705
|
+
const entries = [];
|
|
706
|
+
const typeKind = String(column?.typeKind || "");
|
|
707
|
+
const isRequired = resolveCanonicalResourceFieldRequired(column);
|
|
708
|
+
|
|
713
709
|
if (typeKind === "string") {
|
|
714
|
-
|
|
710
|
+
entries.push('type: "string"');
|
|
711
|
+
if (Number.isInteger(column?.maxLength) && column.maxLength > 0) {
|
|
712
|
+
entries.push(`maxLength: ${column.maxLength}`);
|
|
713
|
+
}
|
|
714
|
+
const enumValues = Array.isArray(column?.enumValues) ? column.enumValues.filter((entry) => entry != null) : [];
|
|
715
|
+
if (enumValues.length > 0) {
|
|
716
|
+
entries.push(`enum: ${JSON.stringify(enumValues)}`);
|
|
717
|
+
}
|
|
715
718
|
} else if (typeKind === "integer") {
|
|
716
719
|
if (column?.isRecordIdColumn === true) {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
+
entries.push('type: "id"');
|
|
721
|
+
} else {
|
|
722
|
+
entries.push('type: "integer"');
|
|
723
|
+
entries.push(...renderBoundedNumberEntries(column));
|
|
720
724
|
}
|
|
721
|
-
schemaExpression = renderIntegerSchema(column);
|
|
722
725
|
} else if (typeKind === "number") {
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
options.push(`minimum: ${column.minimum}`);
|
|
726
|
-
}
|
|
727
|
-
if (Number.isFinite(column?.exclusiveMinimum)) {
|
|
728
|
-
options.push(`exclusiveMinimum: ${column.exclusiveMinimum}`);
|
|
729
|
-
}
|
|
730
|
-
if (Number.isFinite(column?.maximum)) {
|
|
731
|
-
options.push(`maximum: ${column.maximum}`);
|
|
732
|
-
}
|
|
733
|
-
if (Number.isFinite(column?.exclusiveMaximum)) {
|
|
734
|
-
options.push(`exclusiveMaximum: ${column.exclusiveMaximum}`);
|
|
735
|
-
}
|
|
736
|
-
schemaExpression = options.length > 0
|
|
737
|
-
? `Type.Number({ ${options.join(", ")} })`
|
|
738
|
-
: "Type.Number()";
|
|
726
|
+
entries.push('type: "number"');
|
|
727
|
+
entries.push(...renderBoundedNumberEntries(column));
|
|
739
728
|
} else if (typeKind === "boolean") {
|
|
740
|
-
|
|
729
|
+
entries.push('type: "boolean"');
|
|
741
730
|
} else if (typeKind === "datetime") {
|
|
742
|
-
|
|
731
|
+
entries.push('type: "dateTime"');
|
|
732
|
+
const normalizedDefault = normalizeText(column?.defaultValue).toLowerCase();
|
|
733
|
+
if (normalizedDefault === "current_timestamp" || normalizedDefault === "current_timestamp()") {
|
|
734
|
+
entries.push('default: "now()"');
|
|
735
|
+
}
|
|
743
736
|
} else if (typeKind === "date") {
|
|
744
|
-
|
|
737
|
+
entries.push('type: "date"');
|
|
745
738
|
} else if (typeKind === "time") {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
} else if (typeKind === "json") {
|
|
750
|
-
schemaExpression = "Type.Any()";
|
|
739
|
+
entries.push('type: "time"');
|
|
740
|
+
} else {
|
|
741
|
+
entries.push('type: "none"');
|
|
751
742
|
}
|
|
752
743
|
|
|
753
|
-
if (
|
|
754
|
-
|
|
744
|
+
if (isRequired) {
|
|
745
|
+
entries.push("required: true");
|
|
746
|
+
}
|
|
747
|
+
if (column?.nullable === true) {
|
|
748
|
+
entries.push("nullable: true");
|
|
749
|
+
}
|
|
750
|
+
if (shouldRenderJsonRestSearch(column)) {
|
|
751
|
+
entries.push("search: true");
|
|
752
|
+
}
|
|
753
|
+
if (column?.isOwnerColumn === true) {
|
|
754
|
+
entries.push("hidden: true");
|
|
755
755
|
}
|
|
756
|
-
return schemaExpression;
|
|
757
|
-
}
|
|
758
756
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
}
|
|
757
|
+
const actualField = normalizeText(fieldContractEntry?.actualField);
|
|
758
|
+
if (actualField) {
|
|
759
|
+
entries.push(`actualField: ${JSON.stringify(actualField)}`);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
const parentRouteParamKey = normalizeText(fieldContractEntry?.parentRouteParamKey);
|
|
763
|
+
if (parentRouteParamKey) {
|
|
764
|
+
entries.push(`parentRouteParamKey: ${JSON.stringify(parentRouteParamKey)}`);
|
|
768
765
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
766
|
+
|
|
767
|
+
const relation = fieldContractEntry?.relation && typeof fieldContractEntry.relation === "object"
|
|
768
|
+
? fieldContractEntry.relation
|
|
769
|
+
: null;
|
|
770
|
+
if (relation) {
|
|
771
|
+
const relationNamespace =
|
|
772
|
+
normalizeCrudLookupNamespace(relation.namespace) ||
|
|
773
|
+
normalizeCrudLookupNamespace(relation.apiPath) ||
|
|
774
|
+
normalizeCrudLookupNamespace(relation?.source?.path) ||
|
|
775
|
+
normalizeCrudLookupNamespace(relation.targetResource);
|
|
776
|
+
if (!relationNamespace) {
|
|
777
|
+
throw new Error(`crud template context field "${normalizeText(column?.key)}" lookup relation requires namespace.`);
|
|
772
778
|
}
|
|
779
|
+
|
|
780
|
+
const relationEntries = [
|
|
781
|
+
`kind: ${JSON.stringify(normalizeText(relation.kind) || "lookup")}`,
|
|
782
|
+
`namespace: ${JSON.stringify(relationNamespace)}`,
|
|
783
|
+
`valueKey: ${JSON.stringify(normalizeText(relation.valueKey) || "id")}`
|
|
784
|
+
];
|
|
785
|
+
const labelKey = normalizeText(relation.labelKey);
|
|
786
|
+
if (labelKey) {
|
|
787
|
+
relationEntries.push(`labelKey: ${JSON.stringify(labelKey)}`);
|
|
788
|
+
}
|
|
789
|
+
entries.push(`relation: { ${relationEntries.join(", ")} }`);
|
|
773
790
|
}
|
|
774
|
-
return `import {\n ${imports.join(",\n ")}\n} from "@jskit-ai/kernel/shared/validators";`;
|
|
775
|
-
}
|
|
776
791
|
|
|
777
|
-
|
|
778
|
-
const
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
792
|
+
const relationshipScopeName = resolveJsonRestRelationshipScopeName(fieldContractEntry);
|
|
793
|
+
const relationshipAlias = resolveJsonRestRelationshipAlias(column);
|
|
794
|
+
if (
|
|
795
|
+
relationshipScopeName &&
|
|
796
|
+
relationshipAlias &&
|
|
797
|
+
column?.isOwnerColumn !== true &&
|
|
798
|
+
column?.isForeignIdColumn === true
|
|
799
|
+
) {
|
|
800
|
+
entries.push(`belongsTo: ${JSON.stringify(relationshipScopeName)}`);
|
|
801
|
+
entries.push(`as: ${JSON.stringify(relationshipAlias)}`);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const fieldUiOptions = normalizeFieldMetaUiOptions(fieldContractEntry?.ui?.options);
|
|
805
|
+
const formControl = checkCrudLookupFormControl(fieldContractEntry?.ui?.formControl, {
|
|
806
|
+
context: `resource schema field "${normalizeText(column?.key)}" ui.formControl`,
|
|
807
|
+
defaultValue: relation ? "autocomplete" : (fieldUiOptions.length > 0 ? "select" : "")
|
|
808
|
+
});
|
|
809
|
+
if (formControl || fieldUiOptions.length > 0) {
|
|
810
|
+
const uiEntries = [];
|
|
811
|
+
if (formControl) {
|
|
812
|
+
uiEntries.push(`formControl: ${JSON.stringify(formControl)}`);
|
|
782
813
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
: "HTML_TIME_STRING_SCHEMA";
|
|
786
|
-
if (!imports.includes(importName)) {
|
|
787
|
-
imports.push(importName);
|
|
814
|
+
if (fieldUiOptions.length > 0) {
|
|
815
|
+
uiEntries.push(`options: ${JSON.stringify(fieldUiOptions)}`);
|
|
788
816
|
}
|
|
817
|
+
entries.push(`ui: { ${uiEntries.join(", ")} }`);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
const storageEntries = [];
|
|
821
|
+
if (fieldContractEntry?.storage?.mode === "virtual") {
|
|
822
|
+
storageEntries.push("virtual: true");
|
|
823
|
+
}
|
|
824
|
+
if (toSnakeCase(normalizeText(column?.key)) !== normalizeText(column?.name)) {
|
|
825
|
+
storageEntries.push(`column: ${JSON.stringify(column.name)}`);
|
|
826
|
+
}
|
|
827
|
+
if (normalizeText(column?.typeKind).toLowerCase() === "datetime") {
|
|
828
|
+
storageEntries.push('writeSerializer: "datetime-utc"');
|
|
789
829
|
}
|
|
790
|
-
|
|
830
|
+
if (storageEntries.length > 0) {
|
|
831
|
+
entries.push(`storage: { ${storageEntries.join(", ")} }`);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const operationsLines = renderCanonicalResourceOperations(column).split("\n");
|
|
835
|
+
const lines = [
|
|
836
|
+
"{",
|
|
837
|
+
...entries.map((entry) => ` ${entry},`),
|
|
838
|
+
` operations: ${operationsLines[0]}`
|
|
839
|
+
];
|
|
840
|
+
|
|
841
|
+
for (const line of operationsLines.slice(1)) {
|
|
842
|
+
lines.push(` ${line}`);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
lines.push("}");
|
|
846
|
+
return lines.join("\n");
|
|
791
847
|
}
|
|
792
848
|
|
|
793
|
-
function
|
|
794
|
-
const
|
|
795
|
-
|
|
796
|
-
|
|
849
|
+
function renderCanonicalResourceSchemaPropertyLines(columns = [], { fieldContractEntries = [] } = {}) {
|
|
850
|
+
const fieldContractByKey = Object.fromEntries(
|
|
851
|
+
(Array.isArray(fieldContractEntries) ? fieldContractEntries : [])
|
|
852
|
+
.map((entry) => [normalizeText(entry?.key), entry])
|
|
853
|
+
.filter(([key]) => key)
|
|
854
|
+
);
|
|
855
|
+
|
|
856
|
+
return (Array.isArray(columns) ? columns : [])
|
|
857
|
+
.filter((column) => column?.isIdColumn !== true)
|
|
858
|
+
.map((column) => {
|
|
859
|
+
const key = renderObjectPropertyKey(column.key);
|
|
860
|
+
const schemaLines = renderCanonicalResourceFieldSchema(column, {
|
|
861
|
+
fieldContractEntry: fieldContractByKey[normalizeText(column?.key)] || null
|
|
862
|
+
}).split("\n");
|
|
863
|
+
const lines = [` ${key}: ${schemaLines[0]}`];
|
|
864
|
+
for (const line of schemaLines.slice(1)) {
|
|
865
|
+
lines.push(` ${line}`);
|
|
866
|
+
}
|
|
867
|
+
lines[lines.length - 1] = `${lines[lines.length - 1]},`;
|
|
868
|
+
return lines.join("\n");
|
|
869
|
+
})
|
|
797
870
|
.join("\n");
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function resolveJsonRestRelationshipScopeName(fieldContractEntry = null) {
|
|
874
|
+
const namespace = normalizeText(fieldContractEntry?.relation?.namespace);
|
|
875
|
+
if (!namespace) {
|
|
876
|
+
return "";
|
|
802
877
|
}
|
|
803
|
-
|
|
878
|
+
|
|
879
|
+
return toCamelCase(namespace.replace(/\//g, "-"));
|
|
804
880
|
}
|
|
805
881
|
|
|
806
|
-
function
|
|
807
|
-
const
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
return "normalizeText";
|
|
882
|
+
function resolveJsonRestRelationshipAlias(column = null) {
|
|
883
|
+
const key = normalizeText(column?.key);
|
|
884
|
+
if (!key) {
|
|
885
|
+
return "";
|
|
811
886
|
}
|
|
812
|
-
if (
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
887
|
+
if (key.endsWith("Id") && key.length > 2) {
|
|
888
|
+
return `${key.slice(0, -2).slice(0, 1).toLowerCase()}${key.slice(0, -2).slice(1)}`;
|
|
889
|
+
}
|
|
890
|
+
return "";
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function resolveJsonRestFieldType(column = {}) {
|
|
894
|
+
if (column?.isRecordIdColumn === true) {
|
|
895
|
+
return "id";
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
const typeKind = normalizeText(column?.typeKind).toLowerCase();
|
|
899
|
+
if (typeKind === "string") {
|
|
900
|
+
return "string";
|
|
817
901
|
}
|
|
818
902
|
if (typeKind === "integer") {
|
|
819
|
-
|
|
820
|
-
if (nullable) {
|
|
821
|
-
return "(value) => normalizeRecordId(value, { fallback: null })";
|
|
822
|
-
}
|
|
823
|
-
return "normalizeRecordId";
|
|
824
|
-
}
|
|
825
|
-
return "normalizeFiniteInteger";
|
|
903
|
+
return "integer";
|
|
826
904
|
}
|
|
827
905
|
if (typeKind === "number") {
|
|
828
|
-
return "
|
|
906
|
+
return "number";
|
|
829
907
|
}
|
|
830
908
|
if (typeKind === "boolean") {
|
|
831
|
-
return "
|
|
909
|
+
return "boolean";
|
|
832
910
|
}
|
|
833
911
|
if (typeKind === "datetime") {
|
|
834
|
-
|
|
835
|
-
return "(value) => { const normalized = normalizeText(value); return normalized ? toIsoString(normalized) : null; }";
|
|
836
|
-
}
|
|
837
|
-
return "toIsoString";
|
|
912
|
+
return "dateTime";
|
|
838
913
|
}
|
|
839
914
|
if (typeKind === "date") {
|
|
840
|
-
|
|
841
|
-
return "(value) => { const normalized = normalizeText(value); return normalized ? toIsoString(normalized).slice(0, 10) : null; }";
|
|
842
|
-
}
|
|
843
|
-
return "(value) => toIsoString(value).slice(0, 10)";
|
|
915
|
+
return "date";
|
|
844
916
|
}
|
|
845
|
-
if (typeKind === "
|
|
846
|
-
return "
|
|
917
|
+
if (typeKind === "time") {
|
|
918
|
+
return "time";
|
|
847
919
|
}
|
|
848
|
-
return "
|
|
920
|
+
return "string";
|
|
849
921
|
}
|
|
850
922
|
|
|
851
|
-
function
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
923
|
+
function shouldRenderJsonRestSearch(column = {}) {
|
|
924
|
+
return column?.isCreatedAtColumn !== true && column?.isUpdatedAtColumn !== true;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
function shouldRenderJsonRestStorage(column = {}) {
|
|
928
|
+
const key = normalizeText(column?.key);
|
|
929
|
+
const columnName = normalizeText(column?.name);
|
|
930
|
+
if (!key || !columnName) {
|
|
931
|
+
return false;
|
|
856
932
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
933
|
+
|
|
934
|
+
if (toSnakeCase(key) !== columnName) {
|
|
935
|
+
return true;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
return normalizeText(column?.typeKind).toLowerCase() === "datetime";
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
function renderJsonRestFieldSchema(column, { fieldContractEntry = null } = {}) {
|
|
942
|
+
const entries = [];
|
|
943
|
+
const type = resolveJsonRestFieldType(column);
|
|
944
|
+
entries.push(`type: ${JSON.stringify(type)}`);
|
|
945
|
+
|
|
946
|
+
if (column?.isIdColumn === true) {
|
|
947
|
+
entries.push("primary: true");
|
|
948
|
+
entries.push("required: true");
|
|
949
|
+
entries.push("search: true");
|
|
950
|
+
} else {
|
|
951
|
+
const required = column?.nullable !== true && column?.hasDefault !== true;
|
|
952
|
+
entries.push(`required: ${required}`);
|
|
953
|
+
if (shouldRenderJsonRestSearch(column)) {
|
|
954
|
+
entries.push("search: true");
|
|
863
955
|
}
|
|
864
|
-
return "normalizeFiniteInteger";
|
|
865
956
|
}
|
|
866
|
-
|
|
867
|
-
|
|
957
|
+
|
|
958
|
+
if (column?.nullable === true) {
|
|
959
|
+
entries.push("nullable: true");
|
|
868
960
|
}
|
|
869
|
-
|
|
870
|
-
|
|
961
|
+
|
|
962
|
+
if (type === "string" && Number.isInteger(column?.maxLength) && column.maxLength > 0) {
|
|
963
|
+
entries.push(`max: ${column.maxLength}`);
|
|
871
964
|
}
|
|
872
|
-
|
|
873
|
-
|
|
965
|
+
|
|
966
|
+
if (column?.isOwnerColumn === true) {
|
|
967
|
+
entries.push("hidden: true");
|
|
874
968
|
}
|
|
875
|
-
|
|
876
|
-
|
|
969
|
+
|
|
970
|
+
const relationshipScopeName = resolveJsonRestRelationshipScopeName(fieldContractEntry);
|
|
971
|
+
const relationshipAlias = resolveJsonRestRelationshipAlias(column);
|
|
972
|
+
if (
|
|
973
|
+
relationshipScopeName &&
|
|
974
|
+
relationshipAlias &&
|
|
975
|
+
column?.isOwnerColumn !== true &&
|
|
976
|
+
column?.isForeignIdColumn === true
|
|
977
|
+
) {
|
|
978
|
+
entries.push(`belongsTo: ${JSON.stringify(relationshipScopeName)}`);
|
|
979
|
+
entries.push(`as: ${JSON.stringify(relationshipAlias)}`);
|
|
877
980
|
}
|
|
878
|
-
|
|
879
|
-
|
|
981
|
+
|
|
982
|
+
if (shouldRenderJsonRestStorage(column)) {
|
|
983
|
+
const storageEntries = [];
|
|
984
|
+
if (toSnakeCase(normalizeText(column?.key)) !== normalizeText(column?.name)) {
|
|
985
|
+
storageEntries.push(`column: ${JSON.stringify(column.name)}`);
|
|
986
|
+
}
|
|
987
|
+
if (normalizeText(column?.typeKind).toLowerCase() === "datetime") {
|
|
988
|
+
storageEntries.push("serialize: serializeNullableDateTime");
|
|
989
|
+
}
|
|
990
|
+
entries.push(`storage: { ${storageEntries.join(", ")} }`);
|
|
880
991
|
}
|
|
881
|
-
return "";
|
|
882
|
-
}
|
|
883
992
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
const schemaExpression = renderResourceFieldSchema(column, { forOutput });
|
|
890
|
-
return ` ${key}: ${schemaExpression},`;
|
|
891
|
-
})
|
|
892
|
-
.join("\n");
|
|
993
|
+
return [
|
|
994
|
+
"{",
|
|
995
|
+
...entries.map((entry, index) => ` ${entry}${index < entries.length - 1 ? "," : ""}`),
|
|
996
|
+
"}"
|
|
997
|
+
].join("\n");
|
|
893
998
|
}
|
|
894
999
|
|
|
895
|
-
function
|
|
896
|
-
const
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
return ` normalizeIfInSource(source, normalized, ${keyLiteral}, ${normalizer});`;
|
|
902
|
-
})
|
|
903
|
-
.join("\n");
|
|
904
|
-
}
|
|
1000
|
+
function renderJsonRestSchemaPropertyLines(columns = [], { fieldContractEntries = [] } = {}) {
|
|
1001
|
+
const fieldContractByKey = Object.fromEntries(
|
|
1002
|
+
(Array.isArray(fieldContractEntries) ? fieldContractEntries : [])
|
|
1003
|
+
.map((entry) => [normalizeText(entry?.key), entry])
|
|
1004
|
+
.filter(([key]) => key)
|
|
1005
|
+
);
|
|
905
1006
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
return sourceColumns
|
|
1007
|
+
return (Array.isArray(columns) ? columns : [])
|
|
1008
|
+
.filter((column) => column?.isIdColumn !== true)
|
|
909
1009
|
.map((column) => {
|
|
910
1010
|
const key = renderObjectPropertyKey(column.key);
|
|
911
|
-
const
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1011
|
+
const schemaLines = renderJsonRestFieldSchema(column, {
|
|
1012
|
+
fieldContractEntry: fieldContractByKey[normalizeText(column?.key)] || null
|
|
1013
|
+
}).split("\n");
|
|
1014
|
+
const lines = [` ${key}: ${schemaLines[0]}`];
|
|
1015
|
+
for (const line of schemaLines.slice(1)) {
|
|
1016
|
+
lines.push(` ${line}`);
|
|
915
1017
|
}
|
|
916
|
-
|
|
917
|
-
return
|
|
1018
|
+
lines[lines.length - 1] = `${lines[lines.length - 1]},`;
|
|
1019
|
+
return lines.join("\n");
|
|
918
1020
|
})
|
|
919
1021
|
.join("\n");
|
|
920
1022
|
}
|
|
921
1023
|
|
|
922
|
-
function
|
|
923
|
-
const
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
return `import {\n ${imports.join(",\n ")}\n} from "@jskit-ai/database-runtime/shared";`;
|
|
934
|
-
}
|
|
1024
|
+
function renderJsonRestSearchSchemaLines(columns = []) {
|
|
1025
|
+
const searchableStringKeys = (Array.isArray(columns) ? columns : [])
|
|
1026
|
+
.filter((column) =>
|
|
1027
|
+
normalizeText(column?.typeKind).toLowerCase() === "string" &&
|
|
1028
|
+
column?.isOwnerColumn !== true &&
|
|
1029
|
+
column?.isIdColumn !== true &&
|
|
1030
|
+
column?.isCreatedAtColumn !== true &&
|
|
1031
|
+
column?.isUpdatedAtColumn !== true
|
|
1032
|
+
)
|
|
1033
|
+
.map((column) => normalizeText(column?.key))
|
|
1034
|
+
.filter(Boolean);
|
|
935
1035
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1036
|
+
const lines = [
|
|
1037
|
+
' id: { type: "id", actualField: "id" },'
|
|
1038
|
+
];
|
|
1039
|
+
|
|
1040
|
+
if (searchableStringKeys.length > 0) {
|
|
1041
|
+
lines.push(
|
|
1042
|
+
` q: { type: "string", oneOf: ${JSON.stringify(searchableStringKeys)}, filterOperator: "like", splitBy: " ", matchAll: true },`
|
|
1043
|
+
);
|
|
939
1044
|
}
|
|
940
|
-
|
|
1045
|
+
|
|
1046
|
+
return lines.join("\n");
|
|
941
1047
|
}
|
|
942
1048
|
|
|
943
|
-
function
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
needsNormalizeRecordId = false,
|
|
949
|
-
needsNormalizeIfInSource = false,
|
|
950
|
-
needsNormalizeIfPresent = false,
|
|
951
|
-
needsNormalizeOrNull = false
|
|
952
|
-
} = {}) {
|
|
953
|
-
const imports = [];
|
|
954
|
-
if (needsNormalizeText) {
|
|
955
|
-
imports.push("normalizeText");
|
|
956
|
-
}
|
|
957
|
-
if (needsNormalizeBoolean) {
|
|
958
|
-
imports.push("normalizeBoolean");
|
|
959
|
-
}
|
|
960
|
-
if (needsNormalizeFiniteNumber) {
|
|
961
|
-
imports.push("normalizeFiniteNumber");
|
|
962
|
-
}
|
|
963
|
-
if (needsNormalizeFiniteInteger) {
|
|
964
|
-
imports.push("normalizeFiniteInteger");
|
|
965
|
-
}
|
|
966
|
-
if (needsNormalizeRecordId) {
|
|
967
|
-
imports.push("normalizeRecordId");
|
|
968
|
-
}
|
|
969
|
-
if (needsNormalizeIfInSource) {
|
|
970
|
-
imports.push("normalizeIfInSource");
|
|
971
|
-
}
|
|
972
|
-
if (needsNormalizeIfPresent) {
|
|
973
|
-
imports.push("normalizeIfPresent");
|
|
974
|
-
}
|
|
975
|
-
if (needsNormalizeOrNull) {
|
|
976
|
-
imports.push("normalizeOrNull");
|
|
1049
|
+
function renderJsonRestDefaultSortLine(columns = []) {
|
|
1050
|
+
const sourceColumns = Array.isArray(columns) ? columns : [];
|
|
1051
|
+
const createdAtColumn = sourceColumns.find((column) => column?.isCreatedAtColumn === true);
|
|
1052
|
+
if (createdAtColumn?.key) {
|
|
1053
|
+
return ` defaultSort: ${JSON.stringify([`-${createdAtColumn.key}`])},`;
|
|
977
1054
|
}
|
|
978
|
-
|
|
979
|
-
|
|
1055
|
+
|
|
1056
|
+
const idColumn = sourceColumns.find((column) => column?.isIdColumn === true);
|
|
1057
|
+
if (idColumn?.key) {
|
|
1058
|
+
return ` defaultSort: ${JSON.stringify([`-${idColumn.key}`])},`;
|
|
980
1059
|
}
|
|
981
|
-
|
|
1060
|
+
|
|
1061
|
+
return "";
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function renderResourceDefaultSortLiteral(columns = []) {
|
|
1065
|
+
const sortLine = renderJsonRestDefaultSortLine(columns);
|
|
1066
|
+
const match = sortLine.match(/defaultSort:\s*(.+),$/);
|
|
1067
|
+
return match?.[1] || "[]";
|
|
982
1068
|
}
|
|
983
1069
|
|
|
984
1070
|
function renderMigrationDefaultClause(column) {
|
|
@@ -1411,7 +1497,7 @@ function resolveEnumFieldMetaUiOptions(enumValues = []) {
|
|
|
1411
1497
|
return normalizeFieldMetaUiOptions(options);
|
|
1412
1498
|
}
|
|
1413
1499
|
|
|
1414
|
-
function
|
|
1500
|
+
function buildFieldContractEntries({ outputColumns = [], writableColumns = [], snapshot = {} } = {}) {
|
|
1415
1501
|
const fieldColumns = [...outputColumns, ...writableColumns];
|
|
1416
1502
|
const fieldColumnsByName = new Map();
|
|
1417
1503
|
const fieldColumnsByKey = new Map();
|
|
@@ -1509,138 +1595,6 @@ function buildFieldMetaEntries({ outputColumns = [], writableColumns = [], snaps
|
|
|
1509
1595
|
return mergeFieldMetaEntries(repositoryEntries, relationEntries, enumEntries);
|
|
1510
1596
|
}
|
|
1511
1597
|
|
|
1512
|
-
function renderFieldMetaEntryLines(entry = {}) {
|
|
1513
|
-
const lines = ["RESOURCE_FIELD_META.push({"];
|
|
1514
|
-
const topLevelProperties = [`key: ${JSON.stringify(entry.key)}`];
|
|
1515
|
-
const repositoryColumn = normalizeText(entry?.repository?.column);
|
|
1516
|
-
const repositoryWriteSerializer = normalizeText(entry?.repository?.writeSerializer);
|
|
1517
|
-
if (repositoryColumn || repositoryWriteSerializer) {
|
|
1518
|
-
const repositoryLines = [
|
|
1519
|
-
"repository: {",
|
|
1520
|
-
...(repositoryColumn ? [` column: ${JSON.stringify(repositoryColumn)}`] : []),
|
|
1521
|
-
...(repositoryWriteSerializer ? [` writeSerializer: ${JSON.stringify(repositoryWriteSerializer)}`] : [])
|
|
1522
|
-
];
|
|
1523
|
-
if (repositoryLines.length > 2) {
|
|
1524
|
-
repositoryLines[repositoryLines.length - 1] = repositoryLines[repositoryLines.length - 1].replace(/,$/, "");
|
|
1525
|
-
}
|
|
1526
|
-
repositoryLines.push("}");
|
|
1527
|
-
for (let index = 1; index < repositoryLines.length - 1; index += 1) {
|
|
1528
|
-
if (index < repositoryLines.length - 2) {
|
|
1529
|
-
repositoryLines[index] = `${repositoryLines[index]},`;
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
topLevelProperties.push(repositoryLines.join("\n"));
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
const relation = entry.relation && typeof entry.relation === "object" ? entry.relation : null;
|
|
1536
|
-
if (relation) {
|
|
1537
|
-
const targetResourceNamespace = normalizeCrudLookupNamespace(relation.targetResource);
|
|
1538
|
-
const relationNamespace =
|
|
1539
|
-
normalizeCrudLookupNamespace(relation.namespace) ||
|
|
1540
|
-
normalizeCrudLookupNamespace(relation.apiPath) ||
|
|
1541
|
-
normalizeCrudLookupNamespace(relation?.source?.path) ||
|
|
1542
|
-
targetResourceNamespace;
|
|
1543
|
-
if (!relationNamespace) {
|
|
1544
|
-
throw new Error(`crud template context fieldMeta["${normalizeText(entry.key)}"] lookup relation requires namespace.`);
|
|
1545
|
-
}
|
|
1546
|
-
const relationLines = [
|
|
1547
|
-
"relation: {",
|
|
1548
|
-
` kind: ${JSON.stringify(normalizeText(relation.kind) || "lookup")},`,
|
|
1549
|
-
` namespace: ${JSON.stringify(relationNamespace)},`,
|
|
1550
|
-
` valueKey: ${JSON.stringify(normalizeText(relation.valueKey) || "id")},`
|
|
1551
|
-
];
|
|
1552
|
-
const labelKey = normalizeText(relation.labelKey);
|
|
1553
|
-
if (labelKey) {
|
|
1554
|
-
relationLines.push(` labelKey: ${JSON.stringify(labelKey)}`);
|
|
1555
|
-
} else {
|
|
1556
|
-
relationLines[relationLines.length - 1] = relationLines[relationLines.length - 1].replace(/,$/, "");
|
|
1557
|
-
}
|
|
1558
|
-
relationLines.push("}");
|
|
1559
|
-
topLevelProperties.push(relationLines.join("\n"));
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
|
-
const fieldUiOptions = normalizeFieldMetaUiOptions(entry?.ui?.options);
|
|
1563
|
-
const formControl = checkCrudLookupFormControl(entry?.ui?.formControl, {
|
|
1564
|
-
context: `resource.fieldMeta["${normalizeText(entry.key)}"].ui.formControl`,
|
|
1565
|
-
defaultValue: relation ? "autocomplete" : (fieldUiOptions.length > 0 ? "select" : "")
|
|
1566
|
-
});
|
|
1567
|
-
if (formControl || fieldUiOptions.length > 0) {
|
|
1568
|
-
const uiPropertyBlocks = [];
|
|
1569
|
-
if (formControl) {
|
|
1570
|
-
uiPropertyBlocks.push([
|
|
1571
|
-
`formControl: ${JSON.stringify(formControl)}${relation ? " // or \"select\"" : ""}`
|
|
1572
|
-
]);
|
|
1573
|
-
}
|
|
1574
|
-
if (fieldUiOptions.length > 0) {
|
|
1575
|
-
const optionsJsonLines = JSON.stringify(fieldUiOptions, null, 2).split("\n");
|
|
1576
|
-
const optionPropertyLines = [`options: ${optionsJsonLines[0]}`];
|
|
1577
|
-
for (const jsonLine of optionsJsonLines.slice(1)) {
|
|
1578
|
-
optionPropertyLines.push(jsonLine);
|
|
1579
|
-
}
|
|
1580
|
-
uiPropertyBlocks.push(optionPropertyLines);
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
const uiLines = ["ui: {"];
|
|
1584
|
-
for (const [propertyIndex, propertyLines] of uiPropertyBlocks.entries()) {
|
|
1585
|
-
const isLastProperty = propertyIndex >= uiPropertyBlocks.length - 1;
|
|
1586
|
-
const propertySuffix = isLastProperty ? "" : ",";
|
|
1587
|
-
for (const [lineIndex, line] of propertyLines.entries()) {
|
|
1588
|
-
const isLastLine = lineIndex >= propertyLines.length - 1;
|
|
1589
|
-
uiLines.push(` ${line}${isLastLine ? propertySuffix : ""}`);
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
uiLines.push("}");
|
|
1593
|
-
topLevelProperties.push(
|
|
1594
|
-
uiLines.join("\n")
|
|
1595
|
-
);
|
|
1596
|
-
}
|
|
1597
|
-
|
|
1598
|
-
for (const [index, propertyBlock] of topLevelProperties.entries()) {
|
|
1599
|
-
const blockLines = String(propertyBlock || "").split("\n");
|
|
1600
|
-
const isLastProperty = index >= topLevelProperties.length - 1;
|
|
1601
|
-
const propertySuffix = isLastProperty ? "" : ",";
|
|
1602
|
-
for (const [lineIndex, line] of blockLines.entries()) {
|
|
1603
|
-
const isLastLine = lineIndex >= blockLines.length - 1;
|
|
1604
|
-
lines.push(` ${line}${isLastLine ? propertySuffix : ""}`);
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
lines.push("});");
|
|
1609
|
-
return lines.join("\n");
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
function renderResourceFieldMetaPushLines(entries = []) {
|
|
1613
|
-
const sourceEntries = Array.isArray(entries) ? entries : [];
|
|
1614
|
-
if (sourceEntries.length < 1) {
|
|
1615
|
-
return "";
|
|
1616
|
-
}
|
|
1617
|
-
|
|
1618
|
-
return sourceEntries.map((entry) => renderFieldMetaEntryLines(entry)).join("\n\n");
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
function renderRepositoryListConfigLines(snapshot = {}) {
|
|
1622
|
-
const commentLines = [
|
|
1623
|
-
" // defaultLimit: 20,",
|
|
1624
|
-
" // maxLimit: 100,",
|
|
1625
|
-
" // searchColumns: [\"name\"],"
|
|
1626
|
-
];
|
|
1627
|
-
const sourceColumns = Array.isArray(snapshot?.columns) ? snapshot.columns : [];
|
|
1628
|
-
const hasCreatedAtColumn = sourceColumns.some((column = {}) => normalizeText(column?.name) === "created_at");
|
|
1629
|
-
if (!hasCreatedAtColumn) {
|
|
1630
|
-
return commentLines.join("\n");
|
|
1631
|
-
}
|
|
1632
|
-
|
|
1633
|
-
return [
|
|
1634
|
-
...commentLines,
|
|
1635
|
-
" orderBy: [",
|
|
1636
|
-
" {",
|
|
1637
|
-
" column: \"created_at\",",
|
|
1638
|
-
" direction: \"desc\"",
|
|
1639
|
-
" }",
|
|
1640
|
-
" ]"
|
|
1641
|
-
].join("\n");
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
1598
|
function buildCrudPermissionIds(namespace = "") {
|
|
1645
1599
|
const permissionNamespace = toSnakeCase(namespace);
|
|
1646
1600
|
if (!permissionNamespace) {
|
|
@@ -1739,14 +1693,10 @@ function renderRouteParamsValidatorLine(operation = "", { surfaceRequiresWorkspa
|
|
|
1739
1693
|
if (!surfaceRequiresWorkspace) {
|
|
1740
1694
|
return "";
|
|
1741
1695
|
}
|
|
1742
|
-
return "
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
|
-
if (!surfaceRequiresWorkspace) {
|
|
1746
|
-
return " paramsValidator: recordIdParamsValidator,";
|
|
1696
|
+
return " params: routeParamsValidator,";
|
|
1747
1697
|
}
|
|
1748
1698
|
|
|
1749
|
-
return "
|
|
1699
|
+
return " params: recordRouteParamsValidator,";
|
|
1750
1700
|
}
|
|
1751
1701
|
|
|
1752
1702
|
function renderRouteInputLines(operation = "", { surfaceRequiresWorkspace = true } = {}) {
|
|
@@ -1769,13 +1719,13 @@ function renderRouteInputLines(operation = "", { surfaceRequiresWorkspace = true
|
|
|
1769
1719
|
}
|
|
1770
1720
|
|
|
1771
1721
|
if (normalizedOperation === "create") {
|
|
1772
|
-
lines.push("
|
|
1722
|
+
lines.push(" ...(request.input.body || {})");
|
|
1773
1723
|
return lines.join("\n");
|
|
1774
1724
|
}
|
|
1775
1725
|
|
|
1776
1726
|
if (normalizedOperation === "update") {
|
|
1777
1727
|
lines.push(" recordId: request.input.params.recordId,");
|
|
1778
|
-
lines.push("
|
|
1728
|
+
lines.push(" ...(request.input.body || {})");
|
|
1779
1729
|
return lines.join("\n");
|
|
1780
1730
|
}
|
|
1781
1731
|
|
|
@@ -1783,32 +1733,89 @@ function renderRouteInputLines(operation = "", { surfaceRequiresWorkspace = true
|
|
|
1783
1733
|
return lines.join("\n");
|
|
1784
1734
|
}
|
|
1785
1735
|
|
|
1786
|
-
function
|
|
1787
|
-
const
|
|
1788
|
-
|
|
1736
|
+
function renderObjectSchemaDefinition(lines = [], { mode = "patch" } = {}) {
|
|
1737
|
+
const entries = (Array.isArray(lines) ? lines : [])
|
|
1738
|
+
.map((line) => String(line || "").trim())
|
|
1739
|
+
.filter(Boolean)
|
|
1740
|
+
.map((line) => line.endsWith(",") ? line.slice(0, -1) : line);
|
|
1789
1741
|
|
|
1790
|
-
if (
|
|
1791
|
-
|
|
1742
|
+
if (entries.length < 1) {
|
|
1743
|
+
throw new TypeError("renderObjectSchemaDefinition requires at least one schema definition.");
|
|
1792
1744
|
}
|
|
1793
1745
|
|
|
1794
|
-
if (
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1746
|
+
if (entries.length === 1) {
|
|
1747
|
+
return entries[0];
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
if (normalizeText(mode).toLowerCase() === "patch") {
|
|
1751
|
+
return [
|
|
1752
|
+
"composeSchemaDefinitions([",
|
|
1753
|
+
...entries.map((line) => ` ${line},`),
|
|
1754
|
+
"])"
|
|
1755
|
+
].join("\n");
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
return [
|
|
1759
|
+
"composeSchemaDefinitions([",
|
|
1760
|
+
...entries.map((line) => ` ${line},`),
|
|
1761
|
+
"], {",
|
|
1762
|
+
` mode: ${JSON.stringify(mode)}`,
|
|
1763
|
+
"})"
|
|
1764
|
+
].join("\n");
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
function renderActionInputExpressions({ surfaceRequiresWorkspace = true } = {}) {
|
|
1768
|
+
const listLines = [];
|
|
1769
|
+
const viewLines = [];
|
|
1770
|
+
const createLines = [];
|
|
1771
|
+
const updateLines = [];
|
|
1772
|
+
const deleteLines = [];
|
|
1773
|
+
|
|
1774
|
+
if (surfaceRequiresWorkspace) {
|
|
1775
|
+
listLines.push("workspaceSlugParamsValidator,");
|
|
1776
|
+
viewLines.push("workspaceSlugParamsValidator,");
|
|
1777
|
+
createLines.push("workspaceSlugParamsValidator,");
|
|
1778
|
+
updateLines.push("workspaceSlugParamsValidator,");
|
|
1779
|
+
deleteLines.push("workspaceSlugParamsValidator,");
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
listLines.push(
|
|
1783
|
+
"listCursorPaginationQueryValidator,",
|
|
1784
|
+
"listSearchQueryValidator,",
|
|
1785
|
+
"listParentFilterQueryValidator,",
|
|
1786
|
+
"lookupIncludeQueryValidator,"
|
|
1787
|
+
);
|
|
1788
|
+
viewLines.push(
|
|
1789
|
+
"recordIdParamsValidator,",
|
|
1790
|
+
"lookupIncludeQueryValidator,"
|
|
1791
|
+
);
|
|
1792
|
+
createLines.push("resource.operations.create.body,");
|
|
1793
|
+
updateLines.push(
|
|
1794
|
+
"recordIdParamsValidator,",
|
|
1795
|
+
"resource.operations.patch.body,"
|
|
1796
|
+
);
|
|
1797
|
+
deleteLines.push("recordIdParamsValidator,");
|
|
1798
|
+
|
|
1799
|
+
return Object.freeze({
|
|
1800
|
+
list: renderObjectSchemaDefinition(listLines),
|
|
1801
|
+
view: renderObjectSchemaDefinition(viewLines),
|
|
1802
|
+
create: renderObjectSchemaDefinition(createLines, { mode: "create" }),
|
|
1803
|
+
update: renderObjectSchemaDefinition(updateLines),
|
|
1804
|
+
delete: renderObjectSchemaDefinition(deleteLines)
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
function renderRouteValidatorConstants({ surfaceRequiresWorkspace = true } = {}) {
|
|
1809
|
+
if (!surfaceRequiresWorkspace) {
|
|
1810
|
+
return "";
|
|
1809
1811
|
}
|
|
1810
1812
|
|
|
1811
|
-
return
|
|
1813
|
+
return [
|
|
1814
|
+
"const recordRouteParamsValidator = composeSchemaDefinitions([",
|
|
1815
|
+
" routeParamsValidator,",
|
|
1816
|
+
" recordIdParamsValidator",
|
|
1817
|
+
"]);"
|
|
1818
|
+
].join("\n");
|
|
1812
1819
|
}
|
|
1813
1820
|
|
|
1814
1821
|
function buildReplacementsFromSnapshot({
|
|
@@ -1822,38 +1829,25 @@ function buildReplacementsFromSnapshot({
|
|
|
1822
1829
|
const scaffoldColumns = resolveScaffoldColumns(snapshot);
|
|
1823
1830
|
const outputColumns = scaffoldColumns.filter((column) => !column.isOwnerColumn);
|
|
1824
1831
|
const writableColumns = scaffoldColumns.filter((column) => column.writable);
|
|
1825
|
-
const
|
|
1826
|
-
.filter((column) => !column.nullable && column.hasDefault !== true)
|
|
1827
|
-
.map((column) => column.key);
|
|
1828
|
-
const resourceColumns = [...outputColumns, ...writableColumns];
|
|
1829
|
-
const fieldMetaEntries = buildFieldMetaEntries({
|
|
1832
|
+
const fieldContractEntries = buildFieldContractEntries({
|
|
1830
1833
|
outputColumns,
|
|
1831
1834
|
writableColumns,
|
|
1832
1835
|
snapshot
|
|
1833
1836
|
});
|
|
1834
|
-
const
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
const
|
|
1838
|
-
const
|
|
1839
|
-
|
|
1840
|
-
);
|
|
1841
|
-
const
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
const
|
|
1847
|
-
const
|
|
1848
|
-
column.typeKind === "string" || column.typeKind === "time"
|
|
1849
|
-
) || needsNullableDateTimeInput || needsNullableDateInput;
|
|
1850
|
-
const needsNormalizeBoolean = resourceColumns.some((column) => column.typeKind === "boolean");
|
|
1851
|
-
const needsNormalizeIfInSource = writableColumns.length > 0;
|
|
1852
|
-
const outputColumnsWithNormalizer = outputColumns.filter(
|
|
1853
|
-
(column) => Boolean(renderOutputNormalizerExpression(column))
|
|
1854
|
-
);
|
|
1855
|
-
const needsNormalizeIfPresent = outputColumnsWithNormalizer.some((column) => column.nullable !== true);
|
|
1856
|
-
const needsNormalizeOrNull = outputColumnsWithNormalizer.some((column) => column.nullable === true);
|
|
1837
|
+
const actionInputExpressions = renderActionInputExpressions({
|
|
1838
|
+
surfaceRequiresWorkspace
|
|
1839
|
+
});
|
|
1840
|
+
const resourceSchemaColumns = scaffoldColumns;
|
|
1841
|
+
const resourceSchemaPropertyLines = renderCanonicalResourceSchemaPropertyLines(resourceSchemaColumns, {
|
|
1842
|
+
fieldContractEntries
|
|
1843
|
+
});
|
|
1844
|
+
const resourceSearchSchemaLines = renderJsonRestSearchSchemaLines(resourceSchemaColumns);
|
|
1845
|
+
const resourceDefaultSortLiteral = renderResourceDefaultSortLiteral(resourceSchemaColumns);
|
|
1846
|
+
const jsonRestSchemaPropertyLines = renderJsonRestSchemaPropertyLines(resourceSchemaColumns, {
|
|
1847
|
+
fieldContractEntries
|
|
1848
|
+
});
|
|
1849
|
+
const jsonRestSearchSchemaLines = renderJsonRestSearchSchemaLines(resourceSchemaColumns);
|
|
1850
|
+
const jsonRestDefaultSortLine = renderJsonRestDefaultSortLine(resourceSchemaColumns);
|
|
1857
1851
|
|
|
1858
1852
|
const replacements = Object.freeze({
|
|
1859
1853
|
__JSKIT_CRUD_TABLE_NAME__: JSON.stringify(snapshot.tableName),
|
|
@@ -1866,36 +1860,26 @@ function buildReplacementsFromSnapshot({
|
|
|
1866
1860
|
__JSKIT_CRUD_ACTION_WORKSPACE_VALIDATOR_IMPORT__: renderActionWorkspaceValidatorImport({
|
|
1867
1861
|
surfaceRequiresWorkspace
|
|
1868
1862
|
}),
|
|
1863
|
+
__JSKIT_CRUD_LIST_ACTION_INPUT__: actionInputExpressions.list,
|
|
1864
|
+
__JSKIT_CRUD_VIEW_ACTION_INPUT__: actionInputExpressions.view,
|
|
1865
|
+
__JSKIT_CRUD_CREATE_ACTION_INPUT__: actionInputExpressions.create,
|
|
1866
|
+
__JSKIT_CRUD_UPDATE_ACTION_INPUT__: actionInputExpressions.update,
|
|
1867
|
+
__JSKIT_CRUD_DELETE_ACTION_INPUT__: actionInputExpressions.delete,
|
|
1869
1868
|
__JSKIT_CRUD_LIST_ACTION_PERMISSION__: renderActionPermissionExpression("list", {
|
|
1870
1869
|
requiresNamedPermissions
|
|
1871
1870
|
}),
|
|
1872
|
-
__JSKIT_CRUD_LIST_ACTION_INPUT_VALIDATOR__: renderActionInputValidatorExpression("list", {
|
|
1873
|
-
surfaceRequiresWorkspace
|
|
1874
|
-
}),
|
|
1875
1871
|
__JSKIT_CRUD_VIEW_ACTION_PERMISSION__: renderActionPermissionExpression("view", {
|
|
1876
1872
|
requiresNamedPermissions
|
|
1877
1873
|
}),
|
|
1878
|
-
__JSKIT_CRUD_VIEW_ACTION_INPUT_VALIDATOR__: renderActionInputValidatorExpression("view", {
|
|
1879
|
-
surfaceRequiresWorkspace
|
|
1880
|
-
}),
|
|
1881
1874
|
__JSKIT_CRUD_CREATE_ACTION_PERMISSION__: renderActionPermissionExpression("create", {
|
|
1882
1875
|
requiresNamedPermissions
|
|
1883
1876
|
}),
|
|
1884
|
-
__JSKIT_CRUD_CREATE_ACTION_INPUT_VALIDATOR__: renderActionInputValidatorExpression("create", {
|
|
1885
|
-
surfaceRequiresWorkspace
|
|
1886
|
-
}),
|
|
1887
1877
|
__JSKIT_CRUD_UPDATE_ACTION_PERMISSION__: renderActionPermissionExpression("update", {
|
|
1888
1878
|
requiresNamedPermissions
|
|
1889
1879
|
}),
|
|
1890
|
-
__JSKIT_CRUD_UPDATE_ACTION_INPUT_VALIDATOR__: renderActionInputValidatorExpression("update", {
|
|
1891
|
-
surfaceRequiresWorkspace
|
|
1892
|
-
}),
|
|
1893
1880
|
__JSKIT_CRUD_DELETE_ACTION_PERMISSION__: renderActionPermissionExpression("delete", {
|
|
1894
1881
|
requiresNamedPermissions
|
|
1895
1882
|
}),
|
|
1896
|
-
__JSKIT_CRUD_DELETE_ACTION_INPUT_VALIDATOR__: renderActionInputValidatorExpression("delete", {
|
|
1897
|
-
surfaceRequiresWorkspace
|
|
1898
|
-
}),
|
|
1899
1883
|
__JSKIT_CRUD_ROLE_CATALOG_PERMISSION_GRANTS__: renderRoleCatalogPermissionGrants(namespace, {
|
|
1900
1884
|
requiresNamedPermissions
|
|
1901
1885
|
}),
|
|
@@ -1904,6 +1888,10 @@ function buildReplacementsFromSnapshot({
|
|
|
1904
1888
|
__JSKIT_CRUD_ROUTE_WORKSPACE_SUPPORT_IMPORTS__: renderRouteWorkspaceSupportImports({
|
|
1905
1889
|
surfaceRequiresWorkspace
|
|
1906
1890
|
}),
|
|
1891
|
+
__JSKIT_CRUD_ROUTE_CONTRACTS_RESOURCE_ARGS__: surfaceRequiresWorkspace ? ",\n routeParamsValidator" : "",
|
|
1892
|
+
__JSKIT_CRUD_ROUTE_VALIDATOR_CONSTANTS__: renderRouteValidatorConstants({
|
|
1893
|
+
surfaceRequiresWorkspace
|
|
1894
|
+
}),
|
|
1907
1895
|
__JSKIT_CRUD_LIST_ROUTE_PARAMS_VALIDATOR_LINE__: renderRouteParamsValidatorLine("list", {
|
|
1908
1896
|
surfaceRequiresWorkspace
|
|
1909
1897
|
}),
|
|
@@ -1934,45 +1922,15 @@ function buildReplacementsFromSnapshot({
|
|
|
1934
1922
|
__JSKIT_CRUD_DELETE_ROUTE_INPUT_LINES__: renderRouteInputLines("delete", {
|
|
1935
1923
|
surfaceRequiresWorkspace
|
|
1936
1924
|
}),
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
)
|
|
1947
|
-
}),
|
|
1948
|
-
__JSKIT_CRUD_RESOURCE_DATABASE_RUNTIME_IMPORT__: renderResourceDatabaseRuntimeImport({
|
|
1949
|
-
needsToIsoString: needsDateTimeOutput || needsDate || writableColumns.some((column) => column.typeKind === "datetime"),
|
|
1950
|
-
needsToDatabaseDateTimeUtc: false
|
|
1951
|
-
}),
|
|
1952
|
-
__JSKIT_CRUD_RESOURCE_NORMALIZE_SUPPORT_IMPORT__: renderResourceNormalizeSupportImport({
|
|
1953
|
-
needsNormalizeText,
|
|
1954
|
-
needsNormalizeBoolean,
|
|
1955
|
-
needsNormalizeFiniteNumber: needsFiniteNumber,
|
|
1956
|
-
needsNormalizeFiniteInteger: needsFiniteInteger,
|
|
1957
|
-
needsNormalizeRecordId: needsRecordIdSchemas,
|
|
1958
|
-
needsNormalizeIfInSource,
|
|
1959
|
-
needsNormalizeIfPresent,
|
|
1960
|
-
needsNormalizeOrNull
|
|
1961
|
-
}),
|
|
1962
|
-
__JSKIT_CRUD_RESOURCE_JSON_IMPORT__: renderResourceJsonImport({
|
|
1963
|
-
needsJson
|
|
1964
|
-
}),
|
|
1965
|
-
__JSKIT_CRUD_RESOURCE_OUTPUT_SCHEMA_PROPERTIES__: renderResourceSchemaPropertyLines(outputColumns, {
|
|
1966
|
-
forOutput: true
|
|
1967
|
-
}),
|
|
1968
|
-
__JSKIT_CRUD_RESOURCE_CREATE_SCHEMA_PROPERTIES__: renderResourceSchemaPropertyLines(writableColumns, {
|
|
1969
|
-
forOutput: false
|
|
1970
|
-
}),
|
|
1971
|
-
__JSKIT_CRUD_RESOURCE_INPUT_NORMALIZATION_LINES__: renderResourceInputNormalizationLines(writableColumns),
|
|
1972
|
-
__JSKIT_CRUD_RESOURCE_OUTPUT_NORMALIZATION_LINES__: renderResourceOutputNormalizationLines(outputColumns),
|
|
1973
|
-
__JSKIT_CRUD_RESOURCE_CREATE_REQUIRED_FIELDS__: JSON.stringify(createRequiredFieldKeys),
|
|
1974
|
-
__JSKIT_CRUD_RESOURCE_FIELD_META_PUSH_LINES__: renderResourceFieldMetaPushLines(fieldMetaEntries),
|
|
1975
|
-
__JSKIT_CRUD_LIST_CONFIG_LINES__: renderRepositoryListConfigLines(snapshot),
|
|
1925
|
+
__JSKIT_CRUD_RESOURCE_SCHEMA_PROPERTIES__: resourceSchemaPropertyLines,
|
|
1926
|
+
__JSKIT_CRUD_RESOURCE_SEARCH_SCHEMA_LINES__: resourceSearchSchemaLines,
|
|
1927
|
+
__JSKIT_CRUD_RESOURCE_DEFAULT_SORT__: resourceDefaultSortLiteral,
|
|
1928
|
+
__JSKIT_CRUD_RESOURCE_AUTOFILTER__: JSON.stringify(resolvedOwnershipFilter),
|
|
1929
|
+
__JSKIT_CRUD_JSONREST_SCOPE_NAME__: JSON.stringify(toCamelCase(namespace)),
|
|
1930
|
+
__JSKIT_CRUD_JSONREST_AUTOFILTER__: JSON.stringify(resolvedOwnershipFilter),
|
|
1931
|
+
__JSKIT_CRUD_JSONREST_SEARCH_SCHEMA_LINES__: jsonRestSearchSchemaLines,
|
|
1932
|
+
__JSKIT_CRUD_JSONREST_SCHEMA_PROPERTIES__: jsonRestSchemaPropertyLines,
|
|
1933
|
+
__JSKIT_CRUD_JSONREST_DEFAULT_SORT_LINE__: jsonRestDefaultSortLine,
|
|
1976
1934
|
__JSKIT_CRUD_MIGRATION_COLUMN_LINES__: renderMigrationColumnLines(snapshot),
|
|
1977
1935
|
__JSKIT_CRUD_MIGRATION_INDEX_LINES__: renderMigrationIndexLines(snapshot),
|
|
1978
1936
|
__JSKIT_CRUD_MIGRATION_FOREIGN_KEY_LINES__: renderMigrationForeignKeyLines(snapshot),
|
|
@@ -2086,13 +2044,10 @@ const __testables = Object.freeze({
|
|
|
2086
2044
|
renderMigrationCheckConstraintLines,
|
|
2087
2045
|
renderMigrationForeignKeyLine,
|
|
2088
2046
|
resolveScaffoldColumns,
|
|
2089
|
-
renderPropertyAccess,
|
|
2090
|
-
renderResourceFieldSchema,
|
|
2091
|
-
renderInputNormalizer,
|
|
2092
|
-
renderOutputNormalizerExpression,
|
|
2093
2047
|
resolveCrudGenerationTableName,
|
|
2094
2048
|
resolveGenerationSnapshot,
|
|
2095
|
-
|
|
2049
|
+
buildFieldContractEntries,
|
|
2050
|
+
renderCanonicalResourceFieldSchema,
|
|
2096
2051
|
resolveDefaultCrudSurfaceIdFromAppConfig,
|
|
2097
2052
|
resolveCrudGenerationSurfaceId,
|
|
2098
2053
|
resolveCrudSurfaceRequiresWorkspace,
|
|
@@ -2100,7 +2055,8 @@ const __testables = Object.freeze({
|
|
|
2100
2055
|
renderRoleCatalogPermissionGrants,
|
|
2101
2056
|
renderActionPermissionSupport,
|
|
2102
2057
|
renderActionPermissionExpression,
|
|
2103
|
-
|
|
2058
|
+
renderActionInputExpressions,
|
|
2059
|
+
renderRouteValidatorConstants,
|
|
2104
2060
|
renderRouteParamsValidatorLine,
|
|
2105
2061
|
renderRouteInputLines
|
|
2106
2062
|
});
|
|
@@ -2108,12 +2064,9 @@ const __testables = Object.freeze({
|
|
|
2108
2064
|
export {
|
|
2109
2065
|
buildTemplateContext,
|
|
2110
2066
|
resolveScaffoldColumns,
|
|
2111
|
-
renderPropertyAccess,
|
|
2112
2067
|
resolveGenerationSnapshot,
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
renderOutputNormalizerExpression,
|
|
2116
|
-
buildFieldMetaEntries,
|
|
2068
|
+
renderCanonicalResourceFieldSchema,
|
|
2069
|
+
buildFieldContractEntries,
|
|
2117
2070
|
resolveCrudGenerationSurfaceId,
|
|
2118
2071
|
__testables
|
|
2119
2072
|
};
|