@postxl/generator 0.15.4 → 0.15.6
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.
|
@@ -33,119 +33,109 @@ const Types = __importStar(require("../../lib/schema/types"));
|
|
|
33
33
|
*/
|
|
34
34
|
function generateModelBusinessLogic({ model, meta }) {
|
|
35
35
|
const imports = imports_1.ImportsGenerator.from(meta.businessLogic.serviceFilePath);
|
|
36
|
-
const
|
|
37
|
-
imports.addImport({
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const dependencies = new Map();
|
|
37
|
+
imports.addImport({ from: meta.data.importPath, items: [meta.data.repositoryClassName] });
|
|
38
|
+
imports.addImport({ from: meta.types.importPath, items: [model.brandedIdType, meta.types.typeName] });
|
|
39
|
+
/**
|
|
40
|
+
* Set of repositories (one for each referenced model) to be injected into the constructor.
|
|
41
|
+
*/
|
|
42
|
+
const dataRepositoryVariableName = Types.toVariableName('data');
|
|
43
|
+
dependencies.set(meta.data.repositoryClassName, {
|
|
44
|
+
repositoryClassName: meta.data.repositoryClassName,
|
|
45
|
+
localRepositoryVariableName: dataRepositoryVariableName,
|
|
40
46
|
});
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
repos.set(meta.data.repositoryClassName, { name: dataRepo, repoName: meta.data.repositoryClassName });
|
|
47
|
-
const linkedItems = new Map();
|
|
47
|
+
const linkedForeignBacklinkFields = new Map();
|
|
48
|
+
// Converts each relationship to a variable that is pulled from the referenced model's repository - and validates
|
|
49
|
+
// that it exists in case the relationship is required.
|
|
50
|
+
// Variable definition and name of the variable is stored in a Map for each relationship - and is referenced in
|
|
51
|
+
// the `getLinkedItem` function.
|
|
48
52
|
for (const relation of (0, fields_1.getRelationFields)(model)) {
|
|
49
|
-
if (relation.relationToModel.typeName === model.typeName) {
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
53
|
const refModel = relation.relationToModel;
|
|
53
54
|
const refMeta = (0, meta_1.getModelMetadata)({ model: refModel });
|
|
54
|
-
imports.addImport({
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
repos.set(refMeta.data.repositoryClassName, {
|
|
63
|
-
name: refMeta.internalPluralName,
|
|
64
|
-
repoName: refMeta.data.repositoryClassName,
|
|
55
|
+
imports.addImport({ from: refMeta.data.importPath, items: [refMeta.data.repositoryClassName] });
|
|
56
|
+
/**
|
|
57
|
+
* Internal variable pointing to an instance of a referenced repository.
|
|
58
|
+
*/
|
|
59
|
+
const repoReferenceName = relation.relationToModel.typeName !== model.typeName ? refMeta.internalPluralName : dataRepositoryVariableName;
|
|
60
|
+
dependencies.set(refMeta.data.repositoryClassName, {
|
|
61
|
+
localRepositoryVariableName: repoReferenceName,
|
|
62
|
+
repositoryClassName: refMeta.data.repositoryClassName,
|
|
65
63
|
});
|
|
66
|
-
|
|
64
|
+
const variableDefinition = `
|
|
65
|
+
const ${relation.relatedModelBacklinkFieldName} = this.${repoReferenceName}.get(itemRaw.${relation.name})
|
|
66
|
+
${!relation.isRequired
|
|
67
|
+
? ''
|
|
68
|
+
: `if (!${relation.relatedModelBacklinkFieldName}) {
|
|
69
|
+
throw new Error(\`Could not find ${refMeta.types.typeName} with id \${itemRaw.${relation.name}} for ${model.typeName}.${relation.name}!\`)
|
|
70
|
+
}`}
|
|
71
|
+
`;
|
|
72
|
+
linkedForeignBacklinkFields.set(relation.name, {
|
|
67
73
|
fieldName: relation.relatedModelBacklinkFieldName,
|
|
68
|
-
variableDefinition
|
|
69
|
-
const ${relation.relatedModelBacklinkFieldName} = this.${refMeta.internalPluralName}.get(itemRaw.${relation.name})
|
|
70
|
-
${!relation.isRequired
|
|
71
|
-
? ''
|
|
72
|
-
: `
|
|
73
|
-
if (!${relation.relatedModelBacklinkFieldName}) {
|
|
74
|
-
throw new Error(\`Could not find ${refMeta.types.typeName} with id \${itemRaw.${relation.name}} for ${model.typeName}.${relation.name}!\`)
|
|
75
|
-
}`}`,
|
|
74
|
+
variableDefinition,
|
|
76
75
|
});
|
|
77
76
|
}
|
|
78
|
-
const hasLinkedItems =
|
|
77
|
+
const hasLinkedItems = linkedForeignBacklinkFields.size > 0;
|
|
79
78
|
if (hasLinkedItems) {
|
|
80
|
-
imports.addImport({
|
|
81
|
-
items: [meta.types.linkedTypeName],
|
|
82
|
-
from: meta.types.importPath,
|
|
83
|
-
});
|
|
79
|
+
imports.addImport({ from: meta.types.importPath, items: [meta.types.linkedTypeName] });
|
|
84
80
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
81
|
+
const constructorParameters = [...dependencies.values()].map(({ localRepositoryVariableName, repositoryClassName }) => `private readonly ${localRepositoryVariableName}: ${repositoryClassName}`);
|
|
82
|
+
const linkedItemsGetterFn = `
|
|
83
|
+
/**
|
|
84
|
+
* Returns the linked ${meta.userFriendlyName} with the given id or null if it does not exist.
|
|
85
|
+
* Linked: The ${meta.userFriendlyName} contains the linked (raw) items themselves, not only the ids.
|
|
86
|
+
*/
|
|
87
|
+
public getLinkedItem(id: ${model.brandedIdType}): ${meta.types.linkedTypeName} | null {
|
|
88
|
+
const itemRaw = this.${dataRepositoryVariableName}.get(id)
|
|
89
|
+
if (!itemRaw) return null
|
|
90
|
+
${[...linkedForeignBacklinkFields.values()].map(({ variableDefinition }) => variableDefinition).join('\n')}
|
|
91
|
+
const item: ${meta.types.linkedTypeName} = {
|
|
92
|
+
${model.fields
|
|
93
|
+
.map((f) => {
|
|
94
|
+
if (f.kind !== 'relation') {
|
|
95
|
+
return `${f.name}: itemRaw.${f.name}`;
|
|
96
|
+
}
|
|
97
|
+
const linked = linkedForeignBacklinkFields.get(f.name);
|
|
98
|
+
if (!linked) {
|
|
99
|
+
throw new Error(`Could not find linked item for ${model.typeName}.${f.name}`);
|
|
100
|
+
}
|
|
101
|
+
return `${linked.fieldName}`;
|
|
102
|
+
})
|
|
103
|
+
.join(',\n')}
|
|
104
|
+
}
|
|
105
|
+
return item
|
|
91
106
|
}
|
|
107
|
+
`;
|
|
92
108
|
return `
|
|
93
109
|
import { Injectable } from '@nestjs/common'
|
|
94
|
-
|
|
110
|
+
|
|
111
|
+
${imports.generate()}
|
|
95
112
|
|
|
96
113
|
@Injectable()
|
|
97
114
|
export class ${meta.businessLogic.serviceClassName} {
|
|
98
|
-
constructor(
|
|
99
|
-
${[...repos.values()].map(({ name, repoName }) => `private readonly ${name}: ${repoName}`).join(',\n')}
|
|
100
|
-
) {}
|
|
115
|
+
constructor(${constructorParameters.join(',\n')}) {}
|
|
101
116
|
|
|
102
117
|
/**
|
|
103
118
|
* Returns the raw ${meta.userFriendlyName} with the given id or null if it does not exist.
|
|
104
119
|
* Raw: The ${meta.userFriendlyName} only contains linked Ids, not the linked items themselves.
|
|
105
120
|
*/
|
|
106
121
|
public get(id: ${model.brandedIdType}): ${meta.types.typeName} | null {
|
|
107
|
-
return this.${
|
|
122
|
+
return this.${dataRepositoryVariableName}.get(id)
|
|
108
123
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
: `
|
|
112
|
-
/**
|
|
113
|
-
* Returns the linked ${meta.userFriendlyName} with the given id or null if it does not exist.
|
|
114
|
-
* Linked: The ${meta.userFriendlyName} contains the linked (raw) items themselves, not only the ids.
|
|
115
|
-
*/
|
|
116
|
-
public getLinkedItem(id: ${model.brandedIdType}): ${meta.types.linkedTypeName} | null {
|
|
117
|
-
const itemRaw = this.${dataRepo}.get(id)
|
|
118
|
-
if (!itemRaw) return null
|
|
119
|
-
${[...linkedItems.values()].map(({ variableDefinition }) => variableDefinition).join('\n')}
|
|
120
|
-
const item: ${meta.types.linkedTypeName} = {
|
|
121
|
-
${model.fields
|
|
122
|
-
.map((f) => {
|
|
123
|
-
if (f.kind !== 'relation') {
|
|
124
|
-
return `${f.name}: itemRaw.${f.name}`;
|
|
125
|
-
}
|
|
126
|
-
const linked = linkedItems.get(f.name);
|
|
127
|
-
if (!linked) {
|
|
128
|
-
throw new Error(`Could not find linked item for ${model.typeName}.${f.name}`);
|
|
129
|
-
}
|
|
130
|
-
return `${linked.fieldName}`;
|
|
131
|
-
})
|
|
132
|
-
.join(',\n')}
|
|
133
|
-
}
|
|
134
|
-
return item
|
|
135
|
-
}`}
|
|
124
|
+
|
|
125
|
+
${hasLinkedItems ? linkedItemsGetterFn : ''}
|
|
136
126
|
|
|
137
127
|
/**
|
|
138
128
|
* Returns a map of all ${meta.userFriendlyName}s.
|
|
139
129
|
*/
|
|
140
130
|
public getAll(): Map<${meta.types.brandedIdType}, ${model.typeName}> {
|
|
141
|
-
return this.${
|
|
131
|
+
return this.${dataRepositoryVariableName}.getAll()
|
|
142
132
|
}
|
|
143
133
|
|
|
144
134
|
/**
|
|
145
135
|
* Creates a new ${meta.userFriendlyName}.
|
|
146
136
|
*/
|
|
147
137
|
public async create(item: Omit<${model.typeName}, 'id'>): Promise<${model.typeName}> {
|
|
148
|
-
return this.${
|
|
138
|
+
return this.${dataRepositoryVariableName}.create(item)
|
|
149
139
|
}
|
|
150
140
|
|
|
151
141
|
/**
|
|
@@ -154,7 +144,7 @@ export class ${meta.businessLogic.serviceClassName} {
|
|
|
154
144
|
public async update(item: Partial<${model.typeName}> & {
|
|
155
145
|
id: ${model.brandedIdType}
|
|
156
146
|
}): Promise<${model.typeName}> {
|
|
157
|
-
return this.${
|
|
147
|
+
return this.${dataRepositoryVariableName}.update(item)
|
|
158
148
|
}
|
|
159
149
|
|
|
160
150
|
/**
|
|
@@ -164,7 +154,7 @@ export class ${meta.businessLogic.serviceClassName} {
|
|
|
164
154
|
* If the items is a dependency of another item, the deletion will fail!
|
|
165
155
|
*/
|
|
166
156
|
public async delete(id: ${model.brandedIdType}): Promise<void> {
|
|
167
|
-
return this.${
|
|
157
|
+
return this.${dataRepositoryVariableName}.delete(id)
|
|
168
158
|
}
|
|
169
159
|
}
|
|
170
160
|
`;
|
|
@@ -28,10 +28,11 @@ function generateModelLibraryComponents({ model, meta }) {
|
|
|
28
28
|
from: refMeta.react.folderPath,
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
const reactContextProvidersOfDependencies = new Set();
|
|
32
|
+
for (const relation of (0, fields_1.getRelationFields)(model)) {
|
|
33
|
+
const refMeta = (0, meta_1.getModelMetadata)({ model: relation.relationToModel });
|
|
34
|
+
reactContextProvidersOfDependencies.add(refMeta.react.context.contextProviderName);
|
|
35
|
+
}
|
|
35
36
|
return `
|
|
36
37
|
import React, { useState } from 'react'
|
|
37
38
|
|
|
@@ -56,64 +57,76 @@ function generateModelLibraryComponents({ model, meta }) {
|
|
|
56
57
|
/**
|
|
57
58
|
* Show a single ${model.name} entry.
|
|
58
59
|
*/
|
|
59
|
-
export const ${components.cardComponentName} = React.forwardRef<HTMLLIElement, { item: ${model.typeName} }>(
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
export const ${components.cardComponentName} = React.forwardRef<HTMLLIElement, { item: ${model.typeName} }>(
|
|
61
|
+
({ item }, forwardedRef) => {
|
|
62
|
+
const [isEditModalOpen, setIsEditModalOpen] = useState(false)
|
|
63
|
+
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false)
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
65
|
+
return (
|
|
66
|
+
<>
|
|
67
|
+
<Card
|
|
68
|
+
ref={forwardedRef}
|
|
69
|
+
title={item.name}
|
|
70
|
+
actions={[
|
|
71
|
+
{
|
|
72
|
+
label: 'Edit',
|
|
73
|
+
icon: 'pencil-on-paper',
|
|
74
|
+
onClick: () => setIsEditModalOpen(true),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
label: 'Delete',
|
|
78
|
+
icon: 'trash',
|
|
79
|
+
onClick: () => setIsDeleteModalOpen(true),
|
|
80
|
+
},
|
|
81
|
+
]}
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
${nestChildrenInComponents({
|
|
83
85
|
child: `
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
components:
|
|
86
|
+
<${components.modals.editComponentName}
|
|
87
|
+
data={item}
|
|
88
|
+
show={isEditModalOpen}
|
|
89
|
+
onHide={() => setIsEditModalOpen(false)}
|
|
90
|
+
/>
|
|
91
|
+
`,
|
|
92
|
+
components: reactContextProvidersOfDependencies,
|
|
91
93
|
})}
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
<${components.modals.deleteComponentName}
|
|
96
|
+
id={item.id}
|
|
97
|
+
show={isDeleteModalOpen}
|
|
98
|
+
onHide={() => setIsDeleteModalOpen(false)}
|
|
99
|
+
/>
|
|
100
|
+
</>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
)
|
|
101
104
|
|
|
102
105
|
${components.cardComponentName}.displayName = '${components.cardComponentName}'
|
|
103
106
|
`;
|
|
104
107
|
}
|
|
105
108
|
exports.generateModelLibraryComponents = generateModelLibraryComponents;
|
|
106
109
|
/**
|
|
107
|
-
*
|
|
110
|
+
* Deterministifcally nests a given React component in an array of parent wrappers.
|
|
111
|
+
*/
|
|
112
|
+
function nestChildrenInComponents({ child, components }) {
|
|
113
|
+
if (components.size === 0) {
|
|
114
|
+
return child;
|
|
115
|
+
}
|
|
116
|
+
const sortedComponents = [...components.values()].sort((a, b) => a.localeCompare(b));
|
|
117
|
+
return _nestInComponents({ child, components: sortedComponents });
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* DO NOT USE THIS FUNCTION DIRECTLY. Use `nestChildrenInComponents` instead.
|
|
108
121
|
*/
|
|
109
|
-
function
|
|
122
|
+
function _nestInComponents({ child, components }) {
|
|
110
123
|
if (components.length === 0) {
|
|
111
124
|
return child;
|
|
112
125
|
}
|
|
113
126
|
const [parent, ...rest] = components;
|
|
114
127
|
return `
|
|
115
128
|
<${parent}>
|
|
116
|
-
${
|
|
129
|
+
${_nestInComponents({ child, components: rest })}
|
|
117
130
|
</${parent}>
|
|
118
131
|
`;
|
|
119
132
|
}
|
|
@@ -389,7 +389,7 @@ function getFormikCreateMutationData({ model: { fields } }) {
|
|
|
389
389
|
${field.name}: values.${formikFieldName}!.id,
|
|
390
390
|
`;
|
|
391
391
|
}
|
|
392
|
-
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}
|
|
392
|
+
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}.id : null,`;
|
|
393
393
|
case 'enum':
|
|
394
394
|
if (field.isRequired) {
|
|
395
395
|
return `
|
|
@@ -398,7 +398,7 @@ function getFormikCreateMutationData({ model: { fields } }) {
|
|
|
398
398
|
${field.name}: values.${formikFieldName}!.id,
|
|
399
399
|
`;
|
|
400
400
|
}
|
|
401
|
-
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}
|
|
401
|
+
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}.id : null,`;
|
|
402
402
|
default:
|
|
403
403
|
throw new types_1.ExhaustiveSwitchCheck(field);
|
|
404
404
|
}
|
|
@@ -458,12 +458,12 @@ function getEditFormikMutationData({ model: { fields } }) {
|
|
|
458
458
|
if (field.isRequired) {
|
|
459
459
|
return `${field.name}: values.${formikFieldName}.id,`;
|
|
460
460
|
}
|
|
461
|
-
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}
|
|
461
|
+
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}.id : null,`;
|
|
462
462
|
case 'enum':
|
|
463
463
|
if (field.isRequired) {
|
|
464
464
|
return `${field.name}: values.${formikFieldName}.id,`;
|
|
465
465
|
}
|
|
466
|
-
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}
|
|
466
|
+
return `${field.name}: values.${formikFieldName}?.id ? values.${formikFieldName}.id : null,`;
|
|
467
467
|
default:
|
|
468
468
|
throw new types_1.ExhaustiveSwitchCheck(field);
|
|
469
469
|
}
|