diff --git a/packages/core/src/rest/restdomainservice.ts b/packages/core/src/rest/restdomainservice.ts index 856c32431..d09f2a815 100644 --- a/packages/core/src/rest/restdomainservice.ts +++ b/packages/core/src/rest/restdomainservice.ts @@ -1,8 +1,10 @@ +import { QueryValidator } from "@webda/ql"; import { CoreModelDefinition, ModelAction } from "../models/coremodel"; import { DomainServiceParameters, ModelsOperationsService } from "../services/domainservice"; import { DeepPartial } from "../services/service"; import { WebContext } from "../utils/context"; import { OpenAPIWebdaDefinition } from "./router"; +import { WebdaError } from "../index"; /** * Swagger static html @@ -203,23 +205,26 @@ export class RESTDomainService< `${prefix}${this.parameters.queryMethod === "GET" ? "{?q?}" : ""}`, [this.parameters.queryMethod], async (context: WebContext) => { - let query = ""; + let queryString = ""; const parentId = `pid.${depth - 1}`; if (context.getHttpContext().getMethod() === "PUT") { - query = (await context.getInput()).q ?? ""; + queryString = (await context.getInput()).q ?? ""; context.clearInput(); } else { - query = context.parameter("q", ""); + queryString = context.parameter("q", ""); } + let query: QueryValidator; + try { + query = new QueryValidator(queryString); + } catch (err) { + throw new WebdaError.BadRequest(`Invalid query ${queryString}`); + } + // Inject parent attribute if (injectAttribute) { - if (query.trim() === "") { - query = `${injectAttribute} = '${context.parameter(parentId)}'`; - } else { - query = `${injectAttribute} = '${context.parameter(parentId)}' AND (${query})`; - } + query.merge(`${injectAttribute} = '${context.parameter(parentId)}'`); } - context.getParameters().query = query; + context.getParameters().query = query.toString(); return this._webda.callOperation(context, `${plurial}.Query`); }, openapi diff --git a/packages/core/src/services/domainservice.spec.ts b/packages/core/src/services/domainservice.spec.ts index 225853dcc..6b66fafd0 100644 --- a/packages/core/src/services/domainservice.spec.ts +++ b/packages/core/src/services/domainservice.spec.ts @@ -232,10 +232,37 @@ class DomainServiceTest extends WebdaTest { }, context }); + // We have 5 items so 5+4+3+2+1=15 assert.strictEqual( result.results.reduce((total, u) => parseInt(u.name.substring(5)) + total, 0), 15 ); + result = await this.http({ + method: "PUT", + url: `/companies/${companies[0].uuid}/users`, + body: { + q: "LIMIT 3" + }, + context + }); + // We have 3 items so 3+2+1=6 + assert.strictEqual( + result.results.reduce((total, u) => parseInt(u.name.substring(5)) + total, 0), + 6 + ); + result = await this.http({ + method: "PUT", + url: `/companies/${companies[0].uuid}/users`, + body: { + q: `LIMIT 2 OFFSET "${result.continuationToken}"` + }, + context + }); + // We should only have 2 items starting from 4 + assert.strictEqual( + result.results.reduce((total, u) => parseInt(u.name.substring(5)) + total, 0), + 9 + ); result = await this.http({ method: "PUT", url: `/companies/${companies[1].uuid}/users`, @@ -244,6 +271,18 @@ class DomainServiceTest extends WebdaTest { }, context }); + await assert.rejects( + () => + this.http({ + method: "PUT", + url: `/companies/${companies[1].uuid}/users`, + body: { + q: "invalid query" + }, + context + }), + WebdaError.BadRequest + ); assert.strictEqual( result.results.reduce((total, u) => parseInt(u.name.substring(5)) + total, 0), 40 diff --git a/packages/postgres/webda.module.json b/packages/postgres/webda.module.json index cd0b08436..bd2cf8627 100644 --- a/packages/postgres/webda.module.json +++ b/packages/postgres/webda.module.json @@ -161,7 +161,7 @@ "properties": { "writable": { "type": "boolean", - "description": "Is `true` if it is safe to call `writable.write()`, which means the stream has not been destroyed, errored or ended." + "description": "Is `true` if it is safe to call `writable.write()`, which means the stream has not been destroyed, errored, or ended." }, "writableEnded": { "type": "boolean", @@ -193,7 +193,7 @@ }, "closed": { "type": "boolean", - "description": "Is true after 'close' has been emitted." + "description": "Is `true` after `'close'` has been emitted." }, "errored": { "anyOf": [ @@ -223,7 +223,7 @@ }, "writableNeedDrain": { "type": "boolean", - "description": "Is `true` if the stream's buffer has been full and stream will emit 'drain'." + "description": "Is `true` if the stream's buffer has been full and stream will emit `'drain'`." }, "readable": { "type": "boolean", @@ -273,7 +273,7 @@ }, "allowHalfOpen": { "type": "boolean", - "description": "If `false` then the stream will automatically end the writable side when the readable side ends. Set initially by the `allowHalfOpen` constructor option, which defaults to `false`.\n\nThis can be changed manually to change the half-open behavior of an existing`Duplex` stream instance, but must be changed before the `'end'` event is emitted." + "description": "If `false` then the stream will automatically end the writable side when the readable side ends. Set initially by the `allowHalfOpen` constructor option, which defaults to `true`.\n\nThis can be changed manually to change the half-open behavior of an existing`Duplex` stream instance, but must be changed before the `'end'` event is emitted." } }, "required": [ @@ -424,9 +424,6 @@ "description": "If true the server will reject any connection which is not authorized with the list of supplied CAs. This option only has an effect if requestCert is true.", "default": true }, - "ALPNCallback": { - "description": "If set, this will be called when a client opens a connection using the ALPN extension. One argument will be passed to the callback: an object containing `servername` and `protocols` fields, respectively containing the server name from the SNI extension (if any) and an array of ALPN protocol name strings. The callback must return either one of the strings listed in `protocols`, which will be returned to the client as the selected ALPN protocol, or `undefined`, to reject the connection with a fatal alert. If a string is returned that does not match one of the client's ALPN protocols, an error will be thrown. This option cannot be used with the `ALPNProtocols` option, and setting both options will throw an error." - }, "ca": { "anyOf": [ { @@ -1014,7 +1011,7 @@ "properties": { "writable": { "type": "boolean", - "description": "Is `true` if it is safe to call `writable.write()`, which means the stream has not been destroyed, errored or ended." + "description": "Is `true` if it is safe to call `writable.write()`, which means the stream has not been destroyed, errored, or ended." }, "writableEnded": { "type": "boolean", @@ -1046,7 +1043,7 @@ }, "closed": { "type": "boolean", - "description": "Is true after 'close' has been emitted." + "description": "Is `true` after `'close'` has been emitted." }, "errored": { "anyOf": [ @@ -1076,7 +1073,7 @@ }, "writableNeedDrain": { "type": "boolean", - "description": "Is `true` if the stream's buffer has been full and stream will emit 'drain'." + "description": "Is `true` if the stream's buffer has been full and stream will emit `'drain'`." }, "readable": { "type": "boolean", @@ -1126,7 +1123,7 @@ }, "allowHalfOpen": { "type": "boolean", - "description": "If `false` then the stream will automatically end the writable side when the readable side ends. Set initially by the `allowHalfOpen` constructor option, which defaults to `false`.\n\nThis can be changed manually to change the half-open behavior of an existing`Duplex` stream instance, but must be changed before the `'end'` event is emitted." + "description": "If `false` then the stream will automatically end the writable side when the readable side ends. Set initially by the `allowHalfOpen` constructor option, which defaults to `true`.\n\nThis can be changed manually to change the half-open behavior of an existing`Duplex` stream instance, but must be changed before the `'end'` event is emitted." } }, "required": [ @@ -1260,7 +1257,7 @@ "properties": { "writable": { "type": "boolean", - "description": "Is `true` if it is safe to call `writable.write()`, which means the stream has not been destroyed, errored or ended." + "description": "Is `true` if it is safe to call `writable.write()`, which means the stream has not been destroyed, errored, or ended." }, "writableEnded": { "type": "boolean", @@ -1292,7 +1289,7 @@ }, "closed": { "type": "boolean", - "description": "Is true after 'close' has been emitted." + "description": "Is `true` after `'close'` has been emitted." }, "errored": { "anyOf": [ @@ -1322,7 +1319,7 @@ }, "writableNeedDrain": { "type": "boolean", - "description": "Is `true` if the stream's buffer has been full and stream will emit 'drain'." + "description": "Is `true` if the stream's buffer has been full and stream will emit `'drain'`." }, "readable": { "type": "boolean", @@ -1372,7 +1369,7 @@ }, "allowHalfOpen": { "type": "boolean", - "description": "If `false` then the stream will automatically end the writable side when the readable side ends. Set initially by the `allowHalfOpen` constructor option, which defaults to `false`.\n\nThis can be changed manually to change the half-open behavior of an existing`Duplex` stream instance, but must be changed before the `'end'` event is emitted." + "description": "If `false` then the stream will automatically end the writable side when the readable side ends. Set initially by the `allowHalfOpen` constructor option, which defaults to `true`.\n\nThis can be changed manually to change the half-open behavior of an existing`Duplex` stream instance, but must be changed before the `'end'` event is emitted." } }, "required": [ @@ -1523,9 +1520,6 @@ "description": "If true the server will reject any connection which is not authorized with the list of supplied CAs. This option only has an effect if requestCert is true.", "default": true }, - "ALPNCallback": { - "description": "If set, this will be called when a client opens a connection using the ALPN extension. One argument will be passed to the callback: an object containing `servername` and `protocols` fields, respectively containing the server name from the SNI extension (if any) and an array of ALPN protocol name strings. The callback must return either one of the strings listed in `protocols`, which will be returned to the client as the selected ALPN protocol, or `undefined`, to reject the connection with a fatal alert. If a string is returned that does not match one of the client's ALPN protocols, an error will be thrown. This option cannot be used with the `ALPNProtocols` option, and setting both options will throw an error." - }, "ca": { "anyOf": [ { @@ -2113,7 +2107,7 @@ "properties": { "writable": { "type": "boolean", - "description": "Is `true` if it is safe to call `writable.write()`, which means the stream has not been destroyed, errored or ended." + "description": "Is `true` if it is safe to call `writable.write()`, which means the stream has not been destroyed, errored, or ended." }, "writableEnded": { "type": "boolean", @@ -2145,7 +2139,7 @@ }, "closed": { "type": "boolean", - "description": "Is true after 'close' has been emitted." + "description": "Is `true` after `'close'` has been emitted." }, "errored": { "anyOf": [ @@ -2175,7 +2169,7 @@ }, "writableNeedDrain": { "type": "boolean", - "description": "Is `true` if the stream's buffer has been full and stream will emit 'drain'." + "description": "Is `true` if the stream's buffer has been full and stream will emit `'drain'`." }, "readable": { "type": "boolean", @@ -2225,7 +2219,7 @@ }, "allowHalfOpen": { "type": "boolean", - "description": "If `false` then the stream will automatically end the writable side when the readable side ends. Set initially by the `allowHalfOpen` constructor option, which defaults to `false`.\n\nThis can be changed manually to change the half-open behavior of an existing`Duplex` stream instance, but must be changed before the `'end'` event is emitted." + "description": "If `false` then the stream will automatically end the writable side when the readable side ends. Set initially by the `allowHalfOpen` constructor option, which defaults to `true`.\n\nThis can be changed manually to change the half-open behavior of an existing`Duplex` stream instance, but must be changed before the `'end'` event is emitted." } }, "required": [ diff --git a/packages/shell/package.json b/packages/shell/package.json index d827d4b8c..fea451532 100644 --- a/packages/shell/package.json +++ b/packages/shell/package.json @@ -59,7 +59,7 @@ "unzipper": "^0.12.1", "yaml": "^2.0.0", "yargs": "^17.0.1", - "yeoman-environment": "^3.5.1" + "yeoman-environment": "^4.4.3" }, "c8": { "report-dir": "./reports", diff --git a/sample-app/webda.module.json b/sample-app/webda.module.json index ff83f2631..b019ff52c 100644 --- a/sample-app/webda.module.json +++ b/sample-app/webda.module.json @@ -704,7 +704,7 @@ "type": "string" }, "interfaceParam": { - "$ref": "#/definitions/Partial%3Cinterface-829200435-246-294-829200435-0-1238%3E", + "$ref": "#/definitions/Partial%3Cinterface-1076421816-246-294-1076421816-0-1238%3E", "description": "The Partial will generate a new $ref" }, "openapi": { @@ -719,7 +719,7 @@ ], "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { - "Partial": { + "Partial": { "type": "object", "properties": { "test": { @@ -1325,7 +1325,7 @@ "description": "URL on which to serve the content" }, "introspection": { - "$ref": "#/definitions/Partial%3Cclass-1131383939-2740-3224-1131383939-0-9466976353190%3E" + "$ref": "#/definitions/Partial%3Cclass-178760925-2740-3224-178760925-0-9466976353190%3E" }, "openapi": { "type": "object", @@ -1338,7 +1338,7 @@ ], "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { - "Partial": { + "Partial": { "type": "object", "properties": { "type": { @@ -1369,7 +1369,7 @@ "type": "string" }, "interfaceParam": { - "$ref": "#/definitions/Partial%3Cinterface-829200435-246-294-829200435-0-1238%3E", + "$ref": "#/definitions/Partial%3Cinterface-1076421816-246-294-1076421816-0-1238%3E", "description": "The Partial will generate a new $ref" }, "fourthParameter": { @@ -1390,7 +1390,7 @@ ], "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { - "Partial": { + "Partial": { "type": "object", "properties": { "test": { @@ -1601,7 +1601,7 @@ "type": "string" }, "interfaceParam": { - "$ref": "#/definitions/Partial%3Cinterface-829200435-246-294-829200435-0-1238%3E", + "$ref": "#/definitions/Partial%3Cinterface-1076421816-246-294-1076421816-0-1238%3E", "description": "The Partial will generate a new $ref" }, "openapi": { @@ -1616,7 +1616,7 @@ ], "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { - "Partial": { + "Partial": { "type": "object", "properties": { "test": {