@nestledjs/api 0.0.15 → 0.0.16

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": "@nestledjs/api",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "generators": "./generators.json",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -8,14 +8,136 @@ import * as dto from './dto'
8
8
  <% if (!model.modelName || !model.pluralModelName) { throw new Error('EJS template error: modelName or pluralModelName missing for model at index ' + idx + ': ' + JSON.stringify(model)); } %>
9
9
  <% }) %>
10
10
 
11
+ <%
12
+ // Helper function to get relation fields for a model
13
+ function getRelationFields(model) {
14
+ return model.fields.filter(field => field.relationName && field.relationName.length > 0);
15
+ }
16
+
17
+ // Helper function to get virtual relation fields for a model
18
+ function getVirtualRelationFields(model) {
19
+ const relationFields = getRelationFields(model);
20
+ const virtualFields = [];
21
+
22
+ relationFields.forEach(relationField => {
23
+ // Only generate virtual fields for relations that don't have explicit foreign keys
24
+ if (!relationField.relationFromFields || relationField.relationFromFields.length === 0) {
25
+ if (relationField.isList) {
26
+ // This is a multi-relation without foreign keys (many-to-many or one-to-many from parent)
27
+ // Generate a virtual foreign key field for connecting
28
+ const virtualFieldName = relationField.name + 'Ids';
29
+ // Check if there's already a scalar field with this name
30
+ const existingField = model.fields.find(f => f.name === virtualFieldName && (!f.relationName || f.relationName.length === 0));
31
+ if (!existingField) {
32
+ virtualFields.push({
33
+ name: virtualFieldName,
34
+ type: 'String',
35
+ isList: true,
36
+ isOptional: true,
37
+ isVirtual: true,
38
+ relationName: relationField.relationName,
39
+ relatedField: relationField.name
40
+ });
41
+ }
42
+ } else {
43
+ // This is a single relation without foreign keys (one-to-one or one-to-many from parent)
44
+ // Generate a virtual foreign key field for connecting
45
+ const virtualFieldName = relationField.name + 'Id';
46
+ // Check if there's already a scalar field with this name
47
+ const existingField = model.fields.find(f => f.name === virtualFieldName && (!f.relationName || f.relationName.length === 0));
48
+ if (!existingField) {
49
+ virtualFields.push({
50
+ name: virtualFieldName,
51
+ type: 'String',
52
+ isList: false,
53
+ isOptional: true,
54
+ isVirtual: true,
55
+ relationName: relationField.relationName,
56
+ relatedField: relationField.name
57
+ });
58
+ }
59
+ }
60
+ }
61
+ });
62
+
63
+ return virtualFields;
64
+ }
65
+
66
+ // Helper function to get foreign key fields that need relation handling
67
+ function getForeignKeyRelationFields(model) {
68
+ const relationFields = getRelationFields(model);
69
+ const foreignKeyFields = [];
70
+
71
+ relationFields.forEach(relationField => {
72
+ if (relationField.relationFromFields && relationField.relationFromFields.length > 0) {
73
+ // This relation has explicit foreign key fields
74
+ relationField.relationFromFields.forEach(fkFieldName => {
75
+ const fkField = model.fields.find(f => f.name === fkFieldName);
76
+ if (fkField) {
77
+ foreignKeyFields.push({
78
+ fieldName: fkFieldName,
79
+ relationName: relationField.name,
80
+ isRequired: !fkField.isOptional
81
+ });
82
+ }
83
+ });
84
+ }
85
+ });
86
+
87
+ return foreignKeyFields;
88
+ }
89
+
90
+ // Helper function to generate relation handling code
91
+ function generateRelationHandling(model, operation) {
92
+ const virtualRelationFields = getVirtualRelationFields(model);
93
+ const foreignKeyFields = getForeignKeyRelationFields(model);
94
+ const allRelationFields = [...virtualRelationFields, ...foreignKeyFields];
95
+
96
+ if (allRelationFields.length === 0) {
97
+ return ' const data = input;';
98
+ }
99
+
100
+ let code = ' const { ';
101
+ code += virtualRelationFields.map(field => field.name).join(', ');
102
+ if (foreignKeyFields.length > 0) {
103
+ if (virtualRelationFields.length > 0) code += ', ';
104
+ code += foreignKeyFields.map(field => field.fieldName).join(', ');
105
+ }
106
+ code += ', ...regularFields } = input;\n';
107
+ code += ' const data: any = regularFields;\n\n';
108
+
109
+ // Handle virtual relation fields (many-to-many without explicit FKs)
110
+ virtualRelationFields.forEach(field => {
111
+ code += ` if (${field.name}) {\n`;
112
+ if (field.isList) {
113
+ code += ` data.${field.relatedField} = { ${operation === 'create' ? 'connect' : 'set'}: ${field.name}.map(id => ({ id })) };\n`;
114
+ } else {
115
+ code += ` data.${field.relatedField} = { connect: { id: ${field.name} } };\n`;
116
+ }
117
+ code += ' }\n';
118
+ });
119
+
120
+ // Handle foreign key fields (explicit FK relations)
121
+ foreignKeyFields.forEach(field => {
122
+ code += ` if (${field.fieldName}) {\n`;
123
+ code += ` data.${field.relationName} = { connect: { id: ${field.fieldName} } };\n`;
124
+ code += ' }\n';
125
+ });
126
+
127
+ return code;
128
+ }
129
+ %>
130
+
11
131
  @Injectable()
12
132
  export class ApiCrudDataAccessService {
13
133
  constructor(private readonly data: ApiCoreDataAccessService) {}
14
134
 
15
135
  <% for (const model of models) { %>
16
136
  async create<%= model.modelName.charAt(0).toUpperCase() + model.modelName.slice(1) %>(info: GraphQLResolveInfo, input: dto.Create<%= model.modelName %>Input) {
137
+ <%- generateRelationHandling(model, 'create') %>
138
+
17
139
  return this.data['<%= model.modelPropertyName %>'].create({
18
- data: input,
140
+ data,
19
141
  select: createSelect(info),
20
142
  });
21
143
  }
@@ -52,9 +174,11 @@ export class ApiCrudDataAccessService {
52
174
  }
53
175
 
54
176
  async update<%= model.modelName.charAt(0).toUpperCase() + model.modelName.slice(1) %>(info: GraphQLResolveInfo, id: string, input: dto.Update<%= model.modelName %>Input) {
177
+ <%- generateRelationHandling(model, 'update') %>
178
+
55
179
  return this.data['<%= model.modelPropertyName %>'].update({
56
180
  where: { id },
57
- data: input,
181
+ data,
58
182
  select: createSelect(info),
59
183
  });
60
184
  }
@@ -9,10 +9,64 @@ let enumNames = new Set();
9
9
  let usesPartialType = false;
10
10
  const alwaysOptionalFields = ['id', 'createdAt', 'updatedAt'];
11
11
 
12
+ // Helper function to get relation fields for a model
13
+ function getRelationFields(model) {
14
+ return model.fields.filter(field => field.relationName && field.relationName.length > 0);
15
+ }
16
+
17
+ // Helper function to get virtual relation fields for a model
18
+ function getVirtualRelationFields(model) {
19
+ const relationFields = getRelationFields(model);
20
+ const virtualFields = [];
21
+
22
+ relationFields.forEach(relationField => {
23
+ // Only generate virtual fields for relations that don't have explicit foreign keys
24
+ if (!relationField.relationFromFields || relationField.relationFromFields.length === 0) {
25
+ if (relationField.isList) {
26
+ // This is a multi-relation without foreign keys (many-to-many or one-to-many from parent)
27
+ // Generate a virtual foreign key field for connecting
28
+ const virtualFieldName = relationField.name + 'Ids';
29
+ // Check if there's already a scalar field with this name
30
+ const existingField = model.fields.find(f => f.name === virtualFieldName && (!f.relationName || f.relationName.length === 0));
31
+ if (!existingField) {
32
+ virtualFields.push({
33
+ name: virtualFieldName,
34
+ type: 'String',
35
+ isList: true,
36
+ isOptional: true,
37
+ isVirtual: true, // Mark as virtual so we know it's generated
38
+ relationName: relationField.relationName,
39
+ relatedField: relationField.name
40
+ });
41
+ }
42
+ } else {
43
+ // This is a single relation without foreign keys (one-to-one or one-to-many from parent)
44
+ // Generate a virtual foreign key field for connecting
45
+ const virtualFieldName = relationField.name + 'Id';
46
+ // Check if there's already a scalar field with this name
47
+ const existingField = model.fields.find(f => f.name === virtualFieldName && (!f.relationName || f.relationName.length === 0));
48
+ if (!existingField) {
49
+ virtualFields.push({
50
+ name: virtualFieldName,
51
+ type: 'String',
52
+ isList: false,
53
+ isOptional: true,
54
+ isVirtual: true, // Mark as virtual so we know it's generated
55
+ relationName: relationField.relationName,
56
+ relatedField: relationField.name
57
+ });
58
+ }
59
+ }
60
+ }
61
+ });
62
+
63
+ return virtualFields;
64
+ }
65
+
12
66
  // First pass to determine necessary imports and enums
13
67
  for (const model of models) {
14
68
  for (const field of model.fields) {
15
- if (field.kind === 'object') continue; // Skip relation fields entirely
69
+ if (field.relationName && field.relationName.length > 0) continue; // Skip relation object fields
16
70
  if (field.type === 'Int') usesInt = true;
17
71
  if (field.type === 'Float' || field.type === 'Decimal') usesFloat = true;
18
72
  if (field.type === 'Json') usesGraphQLJSON = true;
@@ -34,10 +88,15 @@ import { <%= Array.from(gqlImports).join(', ') %> } from '@nestjs/graphql'
34
88
  import { CorePagingInput } from '@<%= npmScope %>/api/core/data-access'
35
89
 
36
90
  <% for (const model of models) { %>
91
+ <%
92
+ // Get all regular fields (non-relation) and virtual relation fields
93
+ const regularFields = model.fields.filter(field => !field.relationName || field.relationName.length === 0);
94
+ const virtualRelationFields = getVirtualRelationFields(model);
95
+ const allFields = [...regularFields, ...virtualRelationFields];
96
+ %>
37
97
  @InputType()
38
98
  export class Create<%= model.modelName %>Input {
39
- <% for (const field of model.fields) { %>
40
- <% if (field.kind === 'object') { continue; } %>
99
+ <% for (const field of allFields) { %>
41
100
  <%
42
101
  let baseGqlType;
43
102
  let tsType;
@@ -66,11 +125,11 @@ export class Create<%= model.modelName %>Input {
66
125
  fieldDecoratorTypeArg = `() => ${baseGqlType}`;
67
126
  }
68
127
  }
69
- // Always optional for id, createdAt, updatedAt
70
- if (alwaysOptionalFields.includes(field.name)) {
128
+ // Always optional for id, createdAt, updatedAt, and virtual fields
129
+ if (alwaysOptionalFields.includes(field.name) || field.isVirtual) {
71
130
  isOptional = true;
72
131
  } else {
73
- isOptional = !field.isRequired;
132
+ isOptional = field.isOptional;
74
133
  }
75
134
  %>
76
135
  @Field(<% if (fieldDecoratorTypeArg) { %><%- fieldDecoratorTypeArg %>, <% } %>{ nullable: <%= isOptional ? 'true' : 'false' %> })
@@ -80,8 +139,7 @@ export class Create<%= model.modelName %>Input {
80
139
 
81
140
  @InputType()
82
141
  export class Update<%= model.modelName %>Input {
83
- <% for (const field of model.fields) { %>
84
- <% if (field.kind === 'object') { continue; } %>
142
+ <% for (const field of allFields) { %>
85
143
  <%
86
144
  let baseGqlType;
87
145
  let tsType;
@@ -117,8 +175,7 @@ export class Update<%= model.modelName %>Input {
117
175
 
118
176
  @InputType()
119
177
  export class List<%= model.modelName %>Input extends CorePagingInput {
120
- <% for (const field of model.fields) { %>
121
- <% if (field.kind === 'object') { continue; } %>
178
+ <% for (const field of allFields) { %>
122
179
  <%
123
180
  let baseGqlType;
124
181
  let tsType;