@neohm/nh-cli 1.1.3 → 1.1.5
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 +1 -1
- package/src/lib/templates.js +66 -14
package/package.json
CHANGED
package/src/lib/templates.js
CHANGED
|
@@ -85,17 +85,31 @@ export class ${n.controllerClass} {
|
|
|
85
85
|
}
|
|
86
86
|
return `import { Body, Controller, Post } from '@nestjs/common';
|
|
87
87
|
import { ApiTags } from '@nestjs/swagger';
|
|
88
|
-
import {
|
|
88
|
+
import {
|
|
89
|
+
AuthService,
|
|
90
|
+
CommonListFilterDto,
|
|
91
|
+
ListFilterService,
|
|
92
|
+
} from '@neohm/nestend';
|
|
89
93
|
import { ${n.listProcessorClass} } from '../libraries/${n.dot}.list.processor';
|
|
90
94
|
|
|
91
95
|
@ApiTags('${n.kebab}')
|
|
92
96
|
@Controller('api/v1/${n.kebab}')
|
|
93
97
|
export class ${n.controllerClass} {
|
|
94
|
-
constructor(
|
|
98
|
+
constructor(
|
|
99
|
+
private readonly listFilterService: ListFilterService,
|
|
100
|
+
private readonly authService: AuthService,
|
|
101
|
+
) {}
|
|
95
102
|
|
|
96
103
|
@Post('list')
|
|
97
104
|
async list(@Body() body: CommonListFilterDto) {
|
|
98
|
-
|
|
105
|
+
// Open by default: authenticate explicitly. validateAccess() gates the
|
|
106
|
+
// feature on the caller's organization, and guarantees the
|
|
107
|
+
// organization_id the processor scopes on — a tenantless user is
|
|
108
|
+
// refused here rather than reaching the query with a null scope.
|
|
109
|
+
// TODO: replace with the sys_scopes identifier this endpoint sits behind.
|
|
110
|
+
const { user } = await this.authService.validateAccess('add-scope', 'add-role');
|
|
111
|
+
|
|
112
|
+
return new ${n.listProcessorClass}(this.listFilterService, user).process(body);
|
|
99
113
|
}
|
|
100
114
|
}
|
|
101
115
|
`;
|
|
@@ -145,27 +159,56 @@ function listProcessor(base, tableName) {
|
|
|
145
159
|
CommonListFilterConfig,
|
|
146
160
|
CommonListFilterDto,
|
|
147
161
|
CommonListProcessor,
|
|
162
|
+
FilterValueTypeEnum,
|
|
163
|
+
ListFilterService,
|
|
148
164
|
SortDirection,
|
|
149
|
-
|
|
165
|
+
UserEntity,
|
|
150
166
|
} from '@neohm/nestend';
|
|
151
167
|
|
|
152
168
|
/**
|
|
153
|
-
* Lists / filters ${n.pascal}.
|
|
154
|
-
*
|
|
155
|
-
*
|
|
169
|
+
* Lists / filters ${n.pascal}.
|
|
170
|
+
*
|
|
171
|
+
* Two separate surfaces, and it matters which one a rule belongs to:
|
|
172
|
+
*
|
|
173
|
+
* - \`columns\` is the caller's allowlist. An identifier must appear here
|
|
174
|
+
* before a request can filter or sort on it, and \`searchable: true\` adds
|
|
175
|
+
* it to the free-text \`str\` sweep. Everything here is public. Declare a
|
|
176
|
+
* \`type\`: it decides how a caller's value is cast and which operators the
|
|
177
|
+
* column accepts — add \`operators\` to narrow that further.
|
|
178
|
+
*
|
|
179
|
+
* - \`predicates\` and \`applyCustomPredicates()\` are yours. Both compile
|
|
180
|
+
* inside the relation — before any join aggregate or LATERAL runs — and
|
|
181
|
+
* carry no public identifier, so a payload cannot name, weaken or compete
|
|
182
|
+
* with them. Lifecycle and tenant scoping belong here, never in
|
|
183
|
+
* \`columns\`. Standing literal clauses go in \`predicates\`; anything
|
|
184
|
+
* carrying a value goes in the method, where add() binds it.
|
|
156
185
|
*/
|
|
157
186
|
export class ${n.listProcessorClass} extends CommonListProcessor {
|
|
158
187
|
protected config: CommonListFilterConfig = {
|
|
159
188
|
query: '${tableName} a',
|
|
160
189
|
columns: [
|
|
161
|
-
{ key: 'a.id', identifier: 'id' },
|
|
162
|
-
{
|
|
163
|
-
|
|
190
|
+
{ key: 'a.id', identifier: 'id', type: FilterValueTypeEnum.UUID },
|
|
191
|
+
{
|
|
192
|
+
key: 'a.attributes',
|
|
193
|
+
identifier: 'attributes',
|
|
194
|
+
type: FilterValueTypeEnum.JSON,
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
key: 'a.created_at',
|
|
198
|
+
identifier: 'created_at',
|
|
199
|
+
type: FilterValueTypeEnum.DATETIME,
|
|
200
|
+
},
|
|
164
201
|
],
|
|
165
|
-
|
|
202
|
+
// Soft-deleted rows are never listed. \`deleted_at\` is deliberately not a
|
|
203
|
+
// declared column: there is nothing a caller should be doing with it.
|
|
204
|
+
predicates: ['a.deleted_at is null'],
|
|
205
|
+
defaultSort: { field: 'created_at', direction: SortDirection.DESC },
|
|
166
206
|
};
|
|
167
207
|
|
|
168
|
-
constructor(
|
|
208
|
+
constructor(
|
|
209
|
+
protected readonly listFilterService: ListFilterService,
|
|
210
|
+
private readonly user: UserEntity,
|
|
211
|
+
) {
|
|
169
212
|
super();
|
|
170
213
|
}
|
|
171
214
|
|
|
@@ -174,6 +217,14 @@ export class ${n.listProcessorClass} extends CommonListProcessor {
|
|
|
174
217
|
|
|
175
218
|
return this.handle();
|
|
176
219
|
}
|
|
220
|
+
|
|
221
|
+
protected applyCustomPredicates() {
|
|
222
|
+
// Filter for specific user's tenant/organization
|
|
223
|
+
this.predicates.add(
|
|
224
|
+
'a.organization_id = :organization_id',
|
|
225
|
+
this.user.organization_id,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
177
228
|
}
|
|
178
229
|
`;
|
|
179
230
|
}
|
|
@@ -205,9 +256,10 @@ export class ${n.subscriberClass} extends CommonSubscriber<${n.entityClass}> {
|
|
|
205
256
|
|
|
206
257
|
function job(base) {
|
|
207
258
|
const n = names(base);
|
|
208
|
-
return `import { CommonJob, QueueService } from '@neohm/nestend';
|
|
259
|
+
return `import { CommonJob, QueueService, DatabaseEventDto } from '@neohm/nestend';
|
|
209
260
|
import { Injectable } from '@nestjs/common';
|
|
210
261
|
import { JobConstants } from '../../constants/job.constants';
|
|
262
|
+
import { ${n.entityClass} } from '../entities/${n.dot}.entity';
|
|
211
263
|
|
|
212
264
|
@Injectable()
|
|
213
265
|
export class ${n.jobClass} extends CommonJob {
|
|
@@ -215,7 +267,7 @@ export class ${n.jobClass} extends CommonJob {
|
|
|
215
267
|
super(JobConstants.${n.camel}Job);
|
|
216
268
|
}
|
|
217
269
|
|
|
218
|
-
async handle(event:
|
|
270
|
+
async handle(event: DatabaseEventDto<${n.entityClass}>): Promise<void> {
|
|
219
271
|
return;
|
|
220
272
|
}
|
|
221
273
|
}
|