@spinajs/orm-http 2.0.481 → 2.0.484
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/README.md +93 -4
- package/lib/cjs/dto-relation.d.ts +36 -0
- package/lib/cjs/dto-relation.d.ts.map +1 -0
- package/lib/cjs/dto-relation.js +87 -0
- package/lib/cjs/dto-relation.js.map +1 -0
- package/lib/cjs/dto.d.ts.map +1 -1
- package/lib/cjs/dto.js +3 -2
- package/lib/cjs/dto.js.map +1 -1
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +7 -1
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/route-arg.d.ts.map +1 -1
- package/lib/cjs/route-arg.js +18 -3
- package/lib/cjs/route-arg.js.map +1 -1
- package/lib/mjs/dto-relation.d.ts +36 -0
- package/lib/mjs/dto-relation.d.ts.map +1 -0
- package/lib/mjs/dto-relation.js +82 -0
- package/lib/mjs/dto-relation.js.map +1 -0
- package/lib/mjs/dto.d.ts.map +1 -1
- package/lib/mjs/dto.js +3 -2
- package/lib/mjs/dto.js.map +1 -1
- package/lib/mjs/index.d.ts +1 -0
- package/lib/mjs/index.d.ts.map +1 -1
- package/lib/mjs/index.js +7 -1
- package/lib/mjs/index.js.map +1 -1
- package/lib/mjs/route-arg.d.ts.map +1 -1
- package/lib/mjs/route-arg.js +18 -3
- package/lib/mjs/route-arg.js.map +1 -1
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.mjs.tsbuildinfo +1 -1
- package/package.json +20 -21
package/README.md
CHANGED
|
@@ -1,11 +1,100 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@spinajs/orm-http`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Glue between [`@spinajs/orm`](../orm) and `@spinajs/http`: route arguments that load or build
|
|
4
|
+
models straight from a request, a declarative filtering layer, and DTO fields that resolve to
|
|
5
|
+
database entities.
|
|
6
|
+
|
|
7
|
+
It does **not** generate controllers or routes. You write ordinary `@spinajs/http` controllers;
|
|
8
|
+
this package removes the boilerplate inside them.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @spinajs/orm-http
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Importing it is enough — its bootstrapper registers itself with DI and runs when the ORM
|
|
17
|
+
resolves.
|
|
4
18
|
|
|
5
19
|
## Usage
|
|
6
20
|
|
|
21
|
+
```ts
|
|
22
|
+
import { BaseController, BasePath, Get, Post, Ok } from '@spinajs/http';
|
|
23
|
+
import { Connection, Model, ModelBase, Primary } from '@spinajs/orm';
|
|
24
|
+
import { FromModel, AsModel, Filter, Filterable, IFilterRequest } from '@spinajs/orm-http';
|
|
25
|
+
|
|
26
|
+
@Connection('default')
|
|
27
|
+
@Model('articles')
|
|
28
|
+
export class Article extends ModelBase<Article> {
|
|
29
|
+
@Primary()
|
|
30
|
+
public Id: number;
|
|
31
|
+
|
|
32
|
+
@Filterable(['eq', 'like'])
|
|
33
|
+
public Title: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@BasePath('articles')
|
|
37
|
+
export class ArticleController extends BaseController {
|
|
38
|
+
/** GET /articles/:article — loaded by primary key, 404 when missing. */
|
|
39
|
+
@Get(':article')
|
|
40
|
+
public one(@FromModel() article: Article) {
|
|
41
|
+
return new Ok(article.dehydrate());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** GET /articles?filter={"op":"and","filters":[{"Column":"Title","Operator":"like","Value":"x"}]} */
|
|
45
|
+
@Get()
|
|
46
|
+
public async list(@Filter(Article) filter: IFilterRequest) {
|
|
47
|
+
const rows = await Article.select().filter(filter.filters, filter.op);
|
|
48
|
+
return new Ok(rows.map((r) => r.dehydrate()));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** POST /articles — hydrated from the body, not fetched or saved. */
|
|
52
|
+
@Post()
|
|
53
|
+
public async create(@AsModel() article: Article) {
|
|
54
|
+
await article.insert();
|
|
55
|
+
return new Ok(article.dehydrate());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
7
58
|
```
|
|
8
|
-
const ormHttp = require('orm-http');
|
|
9
59
|
|
|
10
|
-
|
|
60
|
+
## What it adds
|
|
61
|
+
|
|
62
|
+
| Feature | Entry point |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| Load a model from a route / query / body / header parameter | `@FromModel()` |
|
|
65
|
+
| Build an unsaved model from the request body | `@AsModel()` |
|
|
66
|
+
| Declarative, schema-validated filtering | `@Filterable()` + `@Filter()` |
|
|
67
|
+
| DTO fields that resolve to entities | `@Relation()` |
|
|
68
|
+
| Pagination and ordering DTOs | `PaginationDTO`, `OrderDTO` |
|
|
69
|
+
| `404` for `OrmNotFoundException` | `OrmNotFound` |
|
|
70
|
+
|
|
71
|
+
It also installs `filter()`, `filterColumns()` and `filterSchema()` onto every model class.
|
|
72
|
+
|
|
73
|
+
## Documentation
|
|
74
|
+
|
|
75
|
+
Full documentation lives in **[docs/](docs/)**.
|
|
76
|
+
|
|
77
|
+
| | Page |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| 01 | [Overview](docs/01-overview.md) |
|
|
80
|
+
| 02 | [Route arguments](docs/02-route-args.md) |
|
|
81
|
+
| 03 | [Filtering](docs/03-filtering.md) |
|
|
82
|
+
| 04 | [DTO relations](docs/04-dto-relations.md) |
|
|
83
|
+
| 05 | [Pagination and ordering](docs/05-pagination-and-order.md) |
|
|
84
|
+
| 06 | [Repository middleware](docs/06-repository-middleware.md) |
|
|
85
|
+
|
|
86
|
+
## Known issues
|
|
87
|
+
|
|
88
|
+
- `RepositoryMiddleware` is defined in `src/middleware.ts` but re-exported from neither
|
|
89
|
+
`src/index.ts` nor the `exports` map, so it is currently unreachable. Use `QueryMiddleware`
|
|
90
|
+
from the core instead — see [docs/06](docs/06-repository-middleware.md).
|
|
91
|
+
- `npm test` fails during bootstrap with `No __file_provider_instance__ registered`: the test
|
|
92
|
+
harness does not register `@spinajs/fs`. That is a test-configuration gap, not a defect in the
|
|
93
|
+
package.
|
|
94
|
+
|
|
95
|
+
## Development
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm run build
|
|
99
|
+
npm run docs:check # from the repo root
|
|
11
100
|
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { ModelBase } from '@spinajs/orm';
|
|
3
|
+
import { ArgHydrator, IRouteParameter } from '@spinajs/http';
|
|
4
|
+
/**
|
|
5
|
+
* Global symbol (shared via the global registry) under which a DTO's relation
|
|
6
|
+
* descriptors are stored on its prototype. Global so `http-swagger` can read it
|
|
7
|
+
* without importing this package.
|
|
8
|
+
*/
|
|
9
|
+
export declare const RELATION_SYMBOL: unique symbol;
|
|
10
|
+
export interface IDtoRelationDescriptor {
|
|
11
|
+
field: string;
|
|
12
|
+
target: () => typeof ModelBase;
|
|
13
|
+
by?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface IDtoRelations {
|
|
16
|
+
Relations: Map<string, IDtoRelationDescriptor>;
|
|
17
|
+
}
|
|
18
|
+
export interface IRelationOptions {
|
|
19
|
+
/** Natural-key column on the target model to look the entity up by. Defaults to the target's primary key. */
|
|
20
|
+
by?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Marks a DTO field as a reference to a DB entity. On an incoming request the
|
|
24
|
+
* field value is looked up on `target` by `by` (or the target's primary key),
|
|
25
|
+
* 404 if missing, and the field is replaced with the resolved model instance.
|
|
26
|
+
*/
|
|
27
|
+
export declare function Relation(target: () => typeof ModelBase, options?: IRelationOptions): PropertyDecorator;
|
|
28
|
+
/**
|
|
29
|
+
* Resolves a DTO's @Relation fields against the DB. Enforces that any DTO with a
|
|
30
|
+
* @Relation declares a @Schema, resolves each present relation field to a model
|
|
31
|
+
* instance (404 if not found), and leaves absent/null fields untouched.
|
|
32
|
+
*/
|
|
33
|
+
export declare class RelationResolverHydrator extends ArgHydrator {
|
|
34
|
+
hydrate(input: any, parameter: IRouteParameter): Promise<any>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=dto-relation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dto-relation.d.ts","sourceRoot":"","sources":["../../src/dto-relation.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,SAAS,EAAwB,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAI7D;;;;GAIG;AACH,eAAO,MAAM,eAAe,eAAmC,CAAC;AAEhE,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,OAAO,SAAS,CAAC;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,gBAAgB;IAC/B,6GAA6G;IAC7G,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,SAAS,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,iBAAiB,CAatG;AAcD;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,WAAW;IAC1C,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC;CA4C3E"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RelationResolverHydrator = exports.RELATION_SYMBOL = void 0;
|
|
4
|
+
exports.Relation = Relation;
|
|
5
|
+
require("reflect-metadata");
|
|
6
|
+
const di_1 = require("@spinajs/di");
|
|
7
|
+
const orm_1 = require("@spinajs/orm");
|
|
8
|
+
const http_1 = require("@spinajs/http");
|
|
9
|
+
const validation_1 = require("@spinajs/validation");
|
|
10
|
+
const exceptions_1 = require("@spinajs/exceptions");
|
|
11
|
+
/**
|
|
12
|
+
* Global symbol (shared via the global registry) under which a DTO's relation
|
|
13
|
+
* descriptors are stored on its prototype. Global so `http-swagger` can read it
|
|
14
|
+
* without importing this package.
|
|
15
|
+
*/
|
|
16
|
+
exports.RELATION_SYMBOL = Symbol.for('orm-http:relations');
|
|
17
|
+
/**
|
|
18
|
+
* Marks a DTO field as a reference to a DB entity. On an incoming request the
|
|
19
|
+
* field value is looked up on `target` by `by` (or the target's primary key),
|
|
20
|
+
* 404 if missing, and the field is replaced with the resolved model instance.
|
|
21
|
+
*/
|
|
22
|
+
function Relation(target, options) {
|
|
23
|
+
return (proto, propertyKey) => {
|
|
24
|
+
const field = String(propertyKey);
|
|
25
|
+
const descriptor = (0, di_1.getInheritedDescriptor)(proto, exports.RELATION_SYMBOL, () => ({ Relations: new Map() }));
|
|
26
|
+
descriptor.Relations.set(field, { field, target, by: options?.by });
|
|
27
|
+
// Register the resolver as the DTO's arg hydrator so a plain @Body() picks
|
|
28
|
+
// it up through the existing hydrator hook — unless one is already set.
|
|
29
|
+
const ctor = proto.constructor;
|
|
30
|
+
if (!Reflect.getOwnMetadata('custom:arg_hydrator', ctor)) {
|
|
31
|
+
Reflect.defineMetadata('custom:arg_hydrator', { hydrator: RelationResolverHydrator }, ctor);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const schemaChecked = new WeakSet();
|
|
36
|
+
/** Enforces the rule: a DTO using @Relation must declare a @Schema. */
|
|
37
|
+
function assertDtoHasSchema(ctor) {
|
|
38
|
+
if (schemaChecked.has(ctor))
|
|
39
|
+
return;
|
|
40
|
+
const schema = Reflect.getMetadata(validation_1.SCHEMA_SYMBOL, ctor.prototype) ?? Reflect.getMetadata(validation_1.SCHEMA_SYMBOL, ctor);
|
|
41
|
+
if (!schema) {
|
|
42
|
+
throw new exceptions_1.InvalidOperation(`DTO ${ctor.name} uses @Relation but has no @Schema. All DTOs with @Relation must declare a @Schema.`);
|
|
43
|
+
}
|
|
44
|
+
schemaChecked.add(ctor);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolves a DTO's @Relation fields against the DB. Enforces that any DTO with a
|
|
48
|
+
* @Relation declares a @Schema, resolves each present relation field to a model
|
|
49
|
+
* instance (404 if not found), and leaves absent/null fields untouched.
|
|
50
|
+
*/
|
|
51
|
+
class RelationResolverHydrator extends http_1.ArgHydrator {
|
|
52
|
+
async hydrate(input, parameter) {
|
|
53
|
+
const ctor = parameter.RuntimeType;
|
|
54
|
+
const dto = new ctor(input);
|
|
55
|
+
const relDesc = Reflect.getMetadata(exports.RELATION_SYMBOL, ctor.prototype);
|
|
56
|
+
if (!relDesc || relDesc.Relations.size === 0) {
|
|
57
|
+
return dto;
|
|
58
|
+
}
|
|
59
|
+
// Enforce "every @Relation DTO must have a @Schema" once per class.
|
|
60
|
+
assertDtoHasSchema(ctor);
|
|
61
|
+
await Promise.all([...relDesc.Relations.values()].map(async (rel) => {
|
|
62
|
+
const value = dto[rel.field];
|
|
63
|
+
// Absent / null: optional fields are skipped; required fields were
|
|
64
|
+
// already rejected upstream by @Schema validation.
|
|
65
|
+
if (value === undefined || value === null) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const model = rel.target();
|
|
69
|
+
const descriptor = model.getModelDescriptor();
|
|
70
|
+
// `PrimaryKey` is `string[]` since composite keys landed. A DTO relation field carries
|
|
71
|
+
// ONE value, so it cannot address a composite key — fail with a clear message instead
|
|
72
|
+
// of building a nonsense WHERE. Same contract as @FromModel route params, which ask for
|
|
73
|
+
// `queryField`; here the escape hatch is `by` on the relation decorator.
|
|
74
|
+
if (!rel.by && (descriptor.PrimaryKey?.length ?? 0) !== 1) {
|
|
75
|
+
throw new exceptions_1.InvalidOperation(`model ${descriptor.Name} has a composite primary key (${(descriptor.PrimaryKey ?? []).join(', ')}); set \`by\` on the relation to select a single lookup column`);
|
|
76
|
+
}
|
|
77
|
+
const by = rel.by ?? descriptor.PrimaryKey[0];
|
|
78
|
+
const resolved = await model
|
|
79
|
+
.where({ [by]: value })
|
|
80
|
+
.firstOrThrow(new orm_1.OrmNotFoundException(`${model.name} referenced by '${rel.field}' not found`));
|
|
81
|
+
dto[rel.field] = resolved;
|
|
82
|
+
}));
|
|
83
|
+
return dto;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.RelationResolverHydrator = RelationResolverHydrator;
|
|
87
|
+
//# sourceMappingURL=dto-relation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dto-relation.js","sourceRoot":"","sources":["../../src/dto-relation.ts"],"names":[],"mappings":"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"}
|
package/lib/cjs/dto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dto.d.ts","sourceRoot":"","sources":["../../src/dto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,
|
|
1
|
+
{"version":3,"file":"dto.d.ts","sourceRoot":"","sources":["../../src/dto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,qBAUa,aAAa;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;gBAER,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC;CAGzC;AAED,qBAQa,QAAQ;IACZ,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC;CAGpC"}
|
package/lib/cjs/dto.js
CHANGED
|
@@ -21,10 +21,11 @@ exports.PaginationDTO = PaginationDTO = __decorate([
|
|
|
21
21
|
(0, validation_1.Schema)({
|
|
22
22
|
type: 'object',
|
|
23
23
|
$id: 'arrow.common.PaginationDTO',
|
|
24
|
+
// Must match the class fields below — otherwise `page` is never validated
|
|
25
|
+
// and offset/take validate phantom properties that the class never declares.
|
|
24
26
|
properties: {
|
|
25
27
|
limit: { type: 'number', minimum: 0 },
|
|
26
|
-
|
|
27
|
-
take: { type: 'number', minimum: 0 },
|
|
28
|
+
page: { type: 'number', minimum: 0 },
|
|
28
29
|
},
|
|
29
30
|
}),
|
|
30
31
|
__metadata("design:paramtypes", [Object])
|
package/lib/cjs/dto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dto.js","sourceRoot":"","sources":["../../src/dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,oDAA6C;
|
|
1
|
+
{"version":3,"file":"dto.js","sourceRoot":"","sources":["../../src/dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,oDAA6C;AAYtC,IAAM,aAAa,GAAnB,MAAM,aAAa;IAIxB,YAAY,IAA4B;QACtC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;CACF,CAAA;AAPY,sCAAa;wBAAb,aAAa;IAVzB,IAAA,mBAAM,EAAC;QACN,IAAI,EAAE,QAAQ;QACd,GAAG,EAAE,4BAA4B;QACjC,0EAA0E;QAC1E,6EAA6E;QAC7E,UAAU,EAAE;YACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE;YACrC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE;SACrC;KACF,CAAC;;GACW,aAAa,CAOzB;AAUM,IAAM,QAAQ,GAAd,MAAM,QAAQ;IAInB,YAAY,IAAuB;QACjC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;CACF,CAAA;AAPY,4BAAQ;mBAAR,QAAQ;IARpB,IAAA,mBAAM,EAAC;QACN,IAAI,EAAE,QAAQ;QACd,GAAG,EAAE,uBAAuB;QAC5B,UAAU,EAAE;YACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE;YAC/D,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC3B;KACF,CAAC;;GACW,QAAQ,CAOpB"}
|
package/lib/cjs/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from './extension.js';
|
|
|
9
9
|
export * from './route-arg.js';
|
|
10
10
|
export * from './builders.js';
|
|
11
11
|
export * from './dto.js';
|
|
12
|
+
export * from './dto-relation.js';
|
|
12
13
|
export * from './response-methods/OrmNotFound.js';
|
|
13
14
|
import * as express from 'express';
|
|
14
15
|
/**
|
package/lib/cjs/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAgG,MAAM,cAAc,CAAC;AAC5I,OAAO,EAAE,eAAe,EAAE,UAAU,EAAoB,aAAa,EAAE,WAAW,EAAE,OAAO,IAAI,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAClJ,OAAO,EAAE,UAAU,EAAqC,YAAY,EAAM,MAAM,aAAa,CAAC;AAE9F,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,mCAAmC,CAAC;AAClD,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAEnC;;;;GAIG;AACH,qBACa,SAAU,SAAQ,SAAS;IACtC,IAAW,aAAa,IAAI,MAAM,CAEjC;IAEY,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM;;;;CAU1I;AAED,qBACa,WAAY,SAAQ,SAAS;IAExC,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC;IAGhC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEb,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B,IAAW,aAAa,IAAI,MAAM,CAEjC;IAEY,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,QAAQ;;;;IAWjG,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,gBAAgB,CAAC,OAAO,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ;IAuBjG,SAAS,CAAC,+BAA+B,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,CAAC,gBAAgB,CAAC,OAAO,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAgG,MAAM,cAAc,CAAC;AAC5I,OAAO,EAAE,eAAe,EAAE,UAAU,EAAoB,aAAa,EAAE,WAAW,EAAE,OAAO,IAAI,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAClJ,OAAO,EAAE,UAAU,EAAqC,YAAY,EAAM,MAAM,aAAa,CAAC;AAE9F,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,mCAAmC,CAAC;AAClD,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAEnC;;;;GAIG;AACH,qBACa,SAAU,SAAQ,SAAS;IACtC,IAAW,aAAa,IAAI,MAAM,CAEjC;IAEY,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM;;;;CAU1I;AAED,qBACa,WAAY,SAAQ,SAAS;IAExC,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC;IAGhC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEb,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B,IAAW,aAAa,IAAI,MAAM,CAEjC;IAEY,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,QAAQ;;;;IAWjG,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,gBAAgB,CAAC,OAAO,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ;IAuBjG,SAAS,CAAC,+BAA+B,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,CAAC,gBAAgB,CAAC,OAAO,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ;CAqE5J;AAED,qBAAa,eAAgB,SAAQ,WAAW;IACjC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC;CAS3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,yGAE3D;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,OAAO,SAAS,EAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,yGAElF;AAED,qBACa,mBAAoB,SAAQ,YAAY;IACtC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAgBxC"}
|
package/lib/cjs/index.js
CHANGED
|
@@ -38,6 +38,7 @@ __exportStar(require("./extension.js"), exports);
|
|
|
38
38
|
__exportStar(require("./route-arg.js"), exports);
|
|
39
39
|
__exportStar(require("./builders.js"), exports);
|
|
40
40
|
__exportStar(require("./dto.js"), exports);
|
|
41
|
+
__exportStar(require("./dto-relation.js"), exports);
|
|
41
42
|
__exportStar(require("./response-methods/OrmNotFound.js"), exports);
|
|
42
43
|
/**
|
|
43
44
|
* Route arg to hydrate model from request body
|
|
@@ -100,7 +101,12 @@ let FromDbModel = class FromDbModel extends http_1.RouteArgs {
|
|
|
100
101
|
const pkValue = this._extractValue(param, req);
|
|
101
102
|
const query = param.RuntimeType['query']();
|
|
102
103
|
const descriptor = (0, orm_1.extractModelDescriptor)(param.RuntimeType);
|
|
103
|
-
|
|
104
|
+
// A route parameter carries ONE value, so it cannot address a composite key. Fail with a
|
|
105
|
+
// 400 rather than silently filtering on the first key column and returning the wrong row.
|
|
106
|
+
if (!param?.Options?.queryField && (descriptor.PrimaryKey?.length ?? 0) !== 1) {
|
|
107
|
+
throw new exceptions_1.BadRequest(`model ${descriptor.Name} has a composite primary key (${(descriptor.PrimaryKey ?? []).join(', ')}); pass queryField to select a single lookup column`);
|
|
108
|
+
}
|
|
109
|
+
const queryField = param?.Options?.queryField ?? descriptor.PrimaryKey[0];
|
|
104
110
|
query.setTable(descriptor.TableName, `$${descriptor.TableName}`);
|
|
105
111
|
query.select('*');
|
|
106
112
|
query.where(queryField, pkValue);
|
package/lib/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAsLA,0BAEC;AAQD,8BAEC;AAlMD,sCAA4I;AAC5I,wCAAkJ;AAClJ,oCAA8F;AAC9F,yCAAiD;AAEjD,oDAAkE;AAElE,kDAAgC;AAChC,6CAA2B;AAC3B,kDAAgC;AAChC,iDAA+B;AAC/B,iDAA+B;AAC/B,gDAA8B;AAC9B,2CAAyB;AACzB,oDAAkC;AAClC,oEAAkD;AAGlD;;;;GAIG;AAEI,IAAM,SAAS,GAAf,MAAM,SAAU,SAAQ,gBAAS;IACtC,IAAW,aAAa;QACtB,OAAO,WAAW,CAAC;IACrB,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,QAAoB,EAAE,KAAgB,EAAE,KAAsB,EAAE,GAAa,EAAE,IAAsB,EAAE,KAAa;QAEvI,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,IAAI,uBAAU,CAAC,yDAAyD,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACzH,CAAC;QAED,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAC9J,IAAI,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC3D,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC9C,CAAC;CACF,CAAA;AAfY,8BAAS;oBAAT,SAAS;IADrB,IAAA,eAAU,GAAE;GACA,SAAS,CAerB;AAGM,IAAM,WAAW,GAAjB,MAAM,WAAY,SAAQ,gBAAS;IAOxC,KAAK,CAAC,OAAO,KAAoB,CAAC;IAElC,IAAW,aAAa;QACtB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,QAAoB,EAAE,IAAe,EAAE,KAAsB,EAAE,GAAa;QAC/F,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,IAAI,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;YAC1B,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,0BAAoB,CAAC,oBAAoB,CAAC,CAAC,CAAC;QACxK,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,MAAM,IAAI,CAAC,+BAA+B,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAClF,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC9C,CAAC;IAES,aAAa,CAAC,KAA0D,EAAE,GAAa;QAC/F,IAAI,OAAO,GAAQ,IAAI,CAAC;QACxB,MAAM,KAAK,GAAG,KAAK,EAAE,OAAO,EAAE,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC;QAEvD,QAAQ,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;YAClC,KAAK,oBAAa,CAAC,SAAS;gBAC1B,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC3B,MAAM;YACR,KAAK,oBAAa,CAAC,QAAQ;gBACzB,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC5C,MAAM;YACR,KAAK,oBAAa,CAAC,UAAU;gBAC3B,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;gBAC3C,MAAM;YACR,KAAK,oBAAa,CAAC,UAAU,CAAC;YAC9B;gBACE,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC5B,MAAM;QACV,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAES,+BAA+B,CAAC,QAAoB,EAAE,KAAgB,EAAE,KAA0D,EAAE,GAAa;QACzJ,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,EAAwB,CAAC;QACjE,MAAM,UAAU,GAAG,IAAA,4BAAsB,EAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAE7D,yFAAyF;QACzF,0FAA0F;QAC1F,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,IAAI,CAAC,UAAW,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/E,MAAM,IAAI,uBAAU,CAAC,SAAS,UAAW,CAAC,IAAI,iCAAiC,CAAC,UAAW,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;QACjL,CAAC;QAED,MAAM,UAAU,GAAG,KAAK,EAAE,OAAO,EAAE,UAAU,IAAI,UAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAE3E,KAAK,CAAC,QAAQ,CAAC,UAAW,CAAC,SAAS,EAAE,IAAI,UAAW,CAAC,SAAS,EAAE,CAAC,CAAC;QACnE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClB,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAEjC;;WAEG;QACH,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,UAAW,CAAC,SAAS,EAAE,CAAC;YAC1C,2CAA2C;YAC3C,kDAAkD;YAClD,mEAAmE;YACnE,IAAI,CAAC,CAAC,IAAI,KAAK,kBAAY,CAAC,GAAG,EAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC;gBAE3C,IAAI,IAAI,EAAE,CAAC;oBACT,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;wBAC1B,OAAO,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;oBACpG,CAAC,CAAC,CAAC;oBAEH,IAAI,GAAG,EAAE,CAAC;wBACR,IAAI,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;4BACrC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;wBAC9D,CAAC;6BAAM,CAAC;4BACN,MAAM,IAAI,4BAAe,CAAC,uBAAuB,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC;wBAC1E,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED;;;WAGG;QACH,IAAG,KAAK,CAAC,OAAO,EAAE,OAAO,EAAC,CAAC;YACzB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;QAGD;;WAEG;QACH,IAAI,KAAK,CAAC,OAAO,EAAE,SAAS,KAAK,IAAI,EAAE,CAAC;YACtC,OAAO,KAAK,CAAC,YAAY,CAAC,IAAI,0BAAoB,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAC5E,CAAC;QAED;;WAEG;QACH,IAAI,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;YACtF,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9F,CAAC;QAED,OAAO,KAAK,CAAC,YAAY,CAAC,IAAI,0BAAoB,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAC5E,CAAC;CACF,CAAA;AApHY,kCAAW;AAEZ;IADT,IAAA,eAAU,EAAC,cAAS,CAAC;;8CACU;AAGtB;IADT,IAAA,eAAU,EAAC,SAAG,CAAC;8BACD,SAAG;wCAAC;sBALR,WAAW;IADvB,IAAA,eAAU,GAAE;GACA,WAAW,CAoHvB;AAED,MAAa,eAAgB,SAAQ,kBAAW;IACvC,KAAK,CAAC,OAAO,CAAC,KAAU,EAAE,SAA0B;QACzD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,kBAAY,CAAC,4BAA4B,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,KAAK,GAAc,IAAI,SAAS,CAAC,WAAW,EAAE,CAAC;QACrD,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAVD,0CAUC;AAED;;;;;;;;;GASG;AACH,SAAgB,OAAO,CAAC,KAAc,EAAE,IAAoB;IAC1D,OAAO,IAAA,YAAK,EAAC,IAAA,gBAAS,EAAC,WAAW,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;GAKG;AACH,SAAgB,SAAS,CAA6B,OAA6B;IACjF,OAAO,IAAA,YAAK,EAAC,IAAA,gBAAS,EAAC,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AACxD,CAAC;AAGM,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,iBAAY;IAC5C,KAAK,CAAC,SAAS;QACpB,OAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC,EAAE,GAAQ,EAAE,EAAE;YACzC,6DAA6D;YAC7D,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;gBACvB,OAAO,CAAC,cAAc,CAAC,qBAAqB,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YACvF,CAAC,CAAC,CAAC;YAEH,oBAAoB;YACpB,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;gBACvB,kCAAkC;gBAClC,KAAK,MAAM,KAAK,IAAI,8BAAmB,EAAE,CAAC;oBACxC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAI,8BAA2B,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACnE,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAjBY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,eAAU,EAAC,iBAAY,CAAC;GACZ,mBAAmB,CAiB/B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-arg.d.ts","sourceRoot":"","sources":["../../src/route-arg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAiC,WAAW,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC9F,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"route-arg.d.ts","sourceRoot":"","sources":["../../src/route-arg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAiC,WAAW,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC9F,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAA8B,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE5E,qBAEa,mBAAoB,SAAQ,SAAS;IAChD,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC;gBAEpB,CAAC,EAAE,UAAU;IAMnB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B,IAAW,aAAa,IAAI,aAAa,GAAG,MAAM,CAEjD;IAEY,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,CAAC,WAAW,CAAC,SAAS,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM;;;;CA8DpM"}
|
package/lib/cjs/route-arg.js
CHANGED
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.FilterModelRouteArg = void 0;
|
|
13
13
|
const di_1 = require("@spinajs/di");
|
|
14
14
|
const http_1 = require("@spinajs/http");
|
|
15
|
+
const exceptions_1 = require("@spinajs/exceptions");
|
|
15
16
|
const interfaces_js_1 = require("./interfaces.js");
|
|
16
17
|
let FilterModelRouteArg = class FilterModelRouteArg extends http_1.RouteArgs {
|
|
17
18
|
constructor(c) {
|
|
@@ -52,10 +53,13 @@ let FilterModelRouteArg = class FilterModelRouteArg extends http_1.RouteArgs {
|
|
|
52
53
|
anyOf: param.Options.map((x) => {
|
|
53
54
|
return {
|
|
54
55
|
type: 'object',
|
|
55
|
-
required:
|
|
56
|
+
// Value is intentionally NOT required: valueless operators
|
|
57
|
+
// (isnull/notnull/exists/n-exists) carry no value. Mirror the
|
|
58
|
+
// model-derived schema in model.ts (filterSchema()).
|
|
59
|
+
required: ['Column', 'Operator'],
|
|
56
60
|
properties: {
|
|
57
61
|
Column: { const: x.column },
|
|
58
|
-
Value: { type: ['string', 'integer', 'array'] },
|
|
62
|
+
Value: { type: ['string', 'integer', 'array', 'boolean'] },
|
|
59
63
|
Operator: { type: 'string', enum: x.operators },
|
|
60
64
|
},
|
|
61
65
|
};
|
|
@@ -65,7 +69,18 @@ let FilterModelRouteArg = class FilterModelRouteArg extends http_1.RouteArgs {
|
|
|
65
69
|
},
|
|
66
70
|
};
|
|
67
71
|
}
|
|
68
|
-
let
|
|
72
|
+
let parsed = filter;
|
|
73
|
+
if (typeof filter === 'string') {
|
|
74
|
+
try {
|
|
75
|
+
parsed = JSON.parse(filter);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
// A malformed filter string is a client error (400), not an
|
|
79
|
+
// unhandled 500 out of the route-arg extractor.
|
|
80
|
+
throw new exceptions_1.InvalidArgument(`Filter parameter '${param.Name}' is invalid JSON. Reason: ${err.message}`, err);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
let result = await this.tryHydrateParam(parsed, rParam, route);
|
|
69
84
|
return { CallData: callData, Args: result };
|
|
70
85
|
}
|
|
71
86
|
};
|
package/lib/cjs/route-arg.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-arg.js","sourceRoot":"","sources":["../../src/route-arg.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,oCAAqF;AACrF,wCAA8F;
|
|
1
|
+
{"version":3,"file":"route-arg.js","sourceRoot":"","sources":["../../src/route-arg.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { ModelBase } from '@spinajs/orm';
|
|
3
|
+
import { ArgHydrator, IRouteParameter } from '@spinajs/http';
|
|
4
|
+
/**
|
|
5
|
+
* Global symbol (shared via the global registry) under which a DTO's relation
|
|
6
|
+
* descriptors are stored on its prototype. Global so `http-swagger` can read it
|
|
7
|
+
* without importing this package.
|
|
8
|
+
*/
|
|
9
|
+
export declare const RELATION_SYMBOL: unique symbol;
|
|
10
|
+
export interface IDtoRelationDescriptor {
|
|
11
|
+
field: string;
|
|
12
|
+
target: () => typeof ModelBase;
|
|
13
|
+
by?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface IDtoRelations {
|
|
16
|
+
Relations: Map<string, IDtoRelationDescriptor>;
|
|
17
|
+
}
|
|
18
|
+
export interface IRelationOptions {
|
|
19
|
+
/** Natural-key column on the target model to look the entity up by. Defaults to the target's primary key. */
|
|
20
|
+
by?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Marks a DTO field as a reference to a DB entity. On an incoming request the
|
|
24
|
+
* field value is looked up on `target` by `by` (or the target's primary key),
|
|
25
|
+
* 404 if missing, and the field is replaced with the resolved model instance.
|
|
26
|
+
*/
|
|
27
|
+
export declare function Relation(target: () => typeof ModelBase, options?: IRelationOptions): PropertyDecorator;
|
|
28
|
+
/**
|
|
29
|
+
* Resolves a DTO's @Relation fields against the DB. Enforces that any DTO with a
|
|
30
|
+
* @Relation declares a @Schema, resolves each present relation field to a model
|
|
31
|
+
* instance (404 if not found), and leaves absent/null fields untouched.
|
|
32
|
+
*/
|
|
33
|
+
export declare class RelationResolverHydrator extends ArgHydrator {
|
|
34
|
+
hydrate(input: any, parameter: IRouteParameter): Promise<any>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=dto-relation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dto-relation.d.ts","sourceRoot":"","sources":["../../src/dto-relation.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,SAAS,EAAwB,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAI7D;;;;GAIG;AACH,eAAO,MAAM,eAAe,eAAmC,CAAC;AAEhE,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,OAAO,SAAS,CAAC;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,gBAAgB;IAC/B,6GAA6G;IAC7G,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,SAAS,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,iBAAiB,CAatG;AAcD;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,WAAW;IAC1C,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC;CA4C3E"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { getInheritedDescriptor } from '@spinajs/di';
|
|
3
|
+
import { OrmNotFoundException } from '@spinajs/orm';
|
|
4
|
+
import { ArgHydrator } from '@spinajs/http';
|
|
5
|
+
import { SCHEMA_SYMBOL } from '@spinajs/validation';
|
|
6
|
+
import { InvalidOperation } from '@spinajs/exceptions';
|
|
7
|
+
/**
|
|
8
|
+
* Global symbol (shared via the global registry) under which a DTO's relation
|
|
9
|
+
* descriptors are stored on its prototype. Global so `http-swagger` can read it
|
|
10
|
+
* without importing this package.
|
|
11
|
+
*/
|
|
12
|
+
export const RELATION_SYMBOL = Symbol.for('orm-http:relations');
|
|
13
|
+
/**
|
|
14
|
+
* Marks a DTO field as a reference to a DB entity. On an incoming request the
|
|
15
|
+
* field value is looked up on `target` by `by` (or the target's primary key),
|
|
16
|
+
* 404 if missing, and the field is replaced with the resolved model instance.
|
|
17
|
+
*/
|
|
18
|
+
export function Relation(target, options) {
|
|
19
|
+
return (proto, propertyKey) => {
|
|
20
|
+
const field = String(propertyKey);
|
|
21
|
+
const descriptor = getInheritedDescriptor(proto, RELATION_SYMBOL, () => ({ Relations: new Map() }));
|
|
22
|
+
descriptor.Relations.set(field, { field, target, by: options?.by });
|
|
23
|
+
// Register the resolver as the DTO's arg hydrator so a plain @Body() picks
|
|
24
|
+
// it up through the existing hydrator hook — unless one is already set.
|
|
25
|
+
const ctor = proto.constructor;
|
|
26
|
+
if (!Reflect.getOwnMetadata('custom:arg_hydrator', ctor)) {
|
|
27
|
+
Reflect.defineMetadata('custom:arg_hydrator', { hydrator: RelationResolverHydrator }, ctor);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const schemaChecked = new WeakSet();
|
|
32
|
+
/** Enforces the rule: a DTO using @Relation must declare a @Schema. */
|
|
33
|
+
function assertDtoHasSchema(ctor) {
|
|
34
|
+
if (schemaChecked.has(ctor))
|
|
35
|
+
return;
|
|
36
|
+
const schema = Reflect.getMetadata(SCHEMA_SYMBOL, ctor.prototype) ?? Reflect.getMetadata(SCHEMA_SYMBOL, ctor);
|
|
37
|
+
if (!schema) {
|
|
38
|
+
throw new InvalidOperation(`DTO ${ctor.name} uses @Relation but has no @Schema. All DTOs with @Relation must declare a @Schema.`);
|
|
39
|
+
}
|
|
40
|
+
schemaChecked.add(ctor);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolves a DTO's @Relation fields against the DB. Enforces that any DTO with a
|
|
44
|
+
* @Relation declares a @Schema, resolves each present relation field to a model
|
|
45
|
+
* instance (404 if not found), and leaves absent/null fields untouched.
|
|
46
|
+
*/
|
|
47
|
+
export class RelationResolverHydrator extends ArgHydrator {
|
|
48
|
+
async hydrate(input, parameter) {
|
|
49
|
+
const ctor = parameter.RuntimeType;
|
|
50
|
+
const dto = new ctor(input);
|
|
51
|
+
const relDesc = Reflect.getMetadata(RELATION_SYMBOL, ctor.prototype);
|
|
52
|
+
if (!relDesc || relDesc.Relations.size === 0) {
|
|
53
|
+
return dto;
|
|
54
|
+
}
|
|
55
|
+
// Enforce "every @Relation DTO must have a @Schema" once per class.
|
|
56
|
+
assertDtoHasSchema(ctor);
|
|
57
|
+
await Promise.all([...relDesc.Relations.values()].map(async (rel) => {
|
|
58
|
+
const value = dto[rel.field];
|
|
59
|
+
// Absent / null: optional fields are skipped; required fields were
|
|
60
|
+
// already rejected upstream by @Schema validation.
|
|
61
|
+
if (value === undefined || value === null) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const model = rel.target();
|
|
65
|
+
const descriptor = model.getModelDescriptor();
|
|
66
|
+
// `PrimaryKey` is `string[]` since composite keys landed. A DTO relation field carries
|
|
67
|
+
// ONE value, so it cannot address a composite key — fail with a clear message instead
|
|
68
|
+
// of building a nonsense WHERE. Same contract as @FromModel route params, which ask for
|
|
69
|
+
// `queryField`; here the escape hatch is `by` on the relation decorator.
|
|
70
|
+
if (!rel.by && (descriptor.PrimaryKey?.length ?? 0) !== 1) {
|
|
71
|
+
throw new InvalidOperation(`model ${descriptor.Name} has a composite primary key (${(descriptor.PrimaryKey ?? []).join(', ')}); set \`by\` on the relation to select a single lookup column`);
|
|
72
|
+
}
|
|
73
|
+
const by = rel.by ?? descriptor.PrimaryKey[0];
|
|
74
|
+
const resolved = await model
|
|
75
|
+
.where({ [by]: value })
|
|
76
|
+
.firstOrThrow(new OrmNotFoundException(`${model.name} referenced by '${rel.field}' not found`));
|
|
77
|
+
dto[rel.field] = resolved;
|
|
78
|
+
}));
|
|
79
|
+
return dto;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=dto-relation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dto-relation.js","sourceRoot":"","sources":["../../src/dto-relation.ts"],"names":[],"mappings":"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"}
|
package/lib/mjs/dto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dto.d.ts","sourceRoot":"","sources":["../../src/dto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,
|
|
1
|
+
{"version":3,"file":"dto.d.ts","sourceRoot":"","sources":["../../src/dto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,qBAUa,aAAa;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;gBAER,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC;CAGzC;AAED,qBAQa,QAAQ;IACZ,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC;CAGpC"}
|
package/lib/mjs/dto.js
CHANGED
|
@@ -17,10 +17,11 @@ PaginationDTO = __decorate([
|
|
|
17
17
|
Schema({
|
|
18
18
|
type: 'object',
|
|
19
19
|
$id: 'arrow.common.PaginationDTO',
|
|
20
|
+
// Must match the class fields below — otherwise `page` is never validated
|
|
21
|
+
// and offset/take validate phantom properties that the class never declares.
|
|
20
22
|
properties: {
|
|
21
23
|
limit: { type: 'number', minimum: 0 },
|
|
22
|
-
|
|
23
|
-
take: { type: 'number', minimum: 0 },
|
|
24
|
+
page: { type: 'number', minimum: 0 },
|
|
24
25
|
},
|
|
25
26
|
}),
|
|
26
27
|
__metadata("design:paramtypes", [Object])
|
package/lib/mjs/dto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dto.js","sourceRoot":"","sources":["../../src/dto.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"dto.js","sourceRoot":"","sources":["../../src/dto.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAYtC,IAAM,aAAa,GAAnB,MAAM,aAAa;IAIxB,YAAY,IAA4B;QACtC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;CACF,CAAA;AAPY,aAAa;IAVzB,MAAM,CAAC;QACN,IAAI,EAAE,QAAQ;QACd,GAAG,EAAE,4BAA4B;QACjC,0EAA0E;QAC1E,6EAA6E;QAC7E,UAAU,EAAE;YACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE;YACrC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE;SACrC;KACF,CAAC;;GACW,aAAa,CAOzB;;AAUM,IAAM,QAAQ,GAAd,MAAM,QAAQ;IAInB,YAAY,IAAuB;QACjC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;CACF,CAAA;AAPY,QAAQ;IARpB,MAAM,CAAC;QACN,IAAI,EAAE,QAAQ;QACd,GAAG,EAAE,uBAAuB;QAC5B,UAAU,EAAE;YACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE;YAC/D,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC3B;KACF,CAAC;;GACW,QAAQ,CAOpB"}
|
package/lib/mjs/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from './extension.js';
|
|
|
9
9
|
export * from './route-arg.js';
|
|
10
10
|
export * from './builders.js';
|
|
11
11
|
export * from './dto.js';
|
|
12
|
+
export * from './dto-relation.js';
|
|
12
13
|
export * from './response-methods/OrmNotFound.js';
|
|
13
14
|
import * as express from 'express';
|
|
14
15
|
/**
|
package/lib/mjs/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAgG,MAAM,cAAc,CAAC;AAC5I,OAAO,EAAE,eAAe,EAAE,UAAU,EAAoB,aAAa,EAAE,WAAW,EAAE,OAAO,IAAI,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAClJ,OAAO,EAAE,UAAU,EAAqC,YAAY,EAAM,MAAM,aAAa,CAAC;AAE9F,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,mCAAmC,CAAC;AAClD,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAEnC;;;;GAIG;AACH,qBACa,SAAU,SAAQ,SAAS;IACtC,IAAW,aAAa,IAAI,MAAM,CAEjC;IAEY,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM;;;;CAU1I;AAED,qBACa,WAAY,SAAQ,SAAS;IAExC,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC;IAGhC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEb,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B,IAAW,aAAa,IAAI,MAAM,CAEjC;IAEY,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,QAAQ;;;;IAWjG,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,gBAAgB,CAAC,OAAO,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ;IAuBjG,SAAS,CAAC,+BAA+B,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,CAAC,gBAAgB,CAAC,OAAO,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,SAAS,EAAgG,MAAM,cAAc,CAAC;AAC5I,OAAO,EAAE,eAAe,EAAE,UAAU,EAAoB,aAAa,EAAE,WAAW,EAAE,OAAO,IAAI,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAClJ,OAAO,EAAE,UAAU,EAAqC,YAAY,EAAM,MAAM,aAAa,CAAC;AAE9F,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC;AAClC,cAAc,mCAAmC,CAAC;AAClD,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAEnC;;;;GAIG;AACH,qBACa,SAAU,SAAQ,SAAS;IACtC,IAAW,aAAa,IAAI,MAAM,CAEjC;IAEY,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM;;;;CAU1I;AAED,qBACa,WAAY,SAAQ,SAAS;IAExC,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC;IAGhC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEb,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B,IAAW,aAAa,IAAI,MAAM,CAEjC;IAEY,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,QAAQ;;;;IAWjG,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,gBAAgB,CAAC,OAAO,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ;IAuBjG,SAAS,CAAC,+BAA+B,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,eAAe,CAAC,gBAAgB,CAAC,OAAO,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ;CAqE5J;AAED,qBAAa,eAAgB,SAAQ,WAAW;IACjC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC;CAS3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,yGAE3D;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,OAAO,SAAS,EAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,yGAElF;AAED,qBACa,mBAAoB,SAAQ,YAAY;IACtC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAgBxC"}
|
package/lib/mjs/index.js
CHANGED
|
@@ -19,6 +19,7 @@ export * from './extension.js';
|
|
|
19
19
|
export * from './route-arg.js';
|
|
20
20
|
export * from './builders.js';
|
|
21
21
|
export * from './dto.js';
|
|
22
|
+
export * from './dto-relation.js';
|
|
22
23
|
export * from './response-methods/OrmNotFound.js';
|
|
23
24
|
/**
|
|
24
25
|
* Route arg to hydrate model from request body
|
|
@@ -81,7 +82,12 @@ let FromDbModel = class FromDbModel extends RouteArgs {
|
|
|
81
82
|
const pkValue = this._extractValue(param, req);
|
|
82
83
|
const query = param.RuntimeType['query']();
|
|
83
84
|
const descriptor = extractModelDescriptor(param.RuntimeType);
|
|
84
|
-
|
|
85
|
+
// A route parameter carries ONE value, so it cannot address a composite key. Fail with a
|
|
86
|
+
// 400 rather than silently filtering on the first key column and returning the wrong row.
|
|
87
|
+
if (!param?.Options?.queryField && (descriptor.PrimaryKey?.length ?? 0) !== 1) {
|
|
88
|
+
throw new BadRequest(`model ${descriptor.Name} has a composite primary key (${(descriptor.PrimaryKey ?? []).join(', ')}); pass queryField to select a single lookup column`);
|
|
89
|
+
}
|
|
90
|
+
const queryField = param?.Options?.queryField ?? descriptor.PrimaryKey[0];
|
|
85
91
|
query.setTable(descriptor.TableName, `$${descriptor.TableName}`);
|
|
86
92
|
query.select('*');
|
|
87
93
|
query.where(queryField, pkValue);
|