Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions packages/async/webda.module.json
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,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",
Expand Down Expand Up @@ -412,7 +412,7 @@
},
"closed": {
"type": "boolean",
"description": "Is true after 'close' has been emitted."
"description": "Is `true` after `'close'` has been emitted."
},
"errored": {
"anyOf": [
Expand Down Expand Up @@ -442,7 +442,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'`."
}
},
"required": [
Expand Down Expand Up @@ -756,7 +756,7 @@
},
"hostname": {
"type": "string",
"description": "Gets and sets the host name portion of the URL. The key difference between`url.host` and `url.hostname` is that `url.hostname` does _not_ include the port.\n\n```js const myURL = new URL('https://example.org:81/foo'); console.log(myURL.hostname); // Prints example.org\n\n// Setting the hostname does not change the port myURL.hostname = 'example.com:82'; console.log(myURL.href); // Prints https://example.com:81/foo\n\n// Use myURL.host to change the hostname and port myURL.host = 'example.org:82'; console.log(myURL.href); // Prints https://example.org:82/foo ```\n\nInvalid host name values assigned to the `hostname` property are ignored."
"description": "Gets and sets the host name portion of the URL. The key difference between`url.host` and `url.hostname` is that `url.hostname` does _not_ include the port.\n\n```js const myURL = new URL('https://example.org:81/foo'); console.log(myURL.hostname); // Prints example.org\n\n// Setting the hostname does not change the port myURL.hostname = 'example.com'; console.log(myURL.href); // Prints https://example.com:81/foo\n\n// Use myURL.host to change the hostname and port myURL.host = 'example.org:82'; console.log(myURL.href); // Prints https://example.org:82/foo ```\n\nInvalid host name values assigned to the `hostname` property are ignored."
},
"href": {
"type": "string",
Expand All @@ -768,7 +768,7 @@
},
"password": {
"type": "string",
"description": "Gets and sets the password portion of the URL.\n\n```js const myURL = new URL('https://abc:xyz@example.com'); console.log(myURL.password); // Prints xyz\n\nmyURL.password = '123'; console.log(myURL.href); // Prints https://abc:123@example.com ```\n\nInvalid URL characters included in the value assigned to the `password` property are `percent-encoded`. The selection of which characters to percent-encode may vary somewhat from what the {@link parse } and {@link format } methods would produce."
"description": "Gets and sets the password portion of the URL.\n\n```js const myURL = new URL('https://abc:xyz@example.com'); console.log(myURL.password); // Prints xyz\n\nmyURL.password = '123'; console.log(myURL.href); // Prints https://abc:123@example.com/ ```\n\nInvalid URL characters included in the value assigned to the `password` property are `percent-encoded`. The selection of which characters to percent-encode may vary somewhat from what the {@link parse } and {@link format } methods would produce."
},
"pathname": {
"type": "string",
Expand Down Expand Up @@ -797,7 +797,7 @@
"required": [
"size"
],
"description": "Gets the `URLSearchParams` object representing the query parameters of the URL. This property is read-only but the `URLSearchParams` object it provides can be used to mutate the URL instance; to replace the entirety of query parameters of the URL, use the {@link search } setter. See `URLSearchParams` documentation for details.\n\nUse care when using `.searchParams` to modify the `URL` because, per the WHATWG specification, the `URLSearchParams` object uses different rules to determine which characters to percent-encode. For instance, the `URL` object will not percent encode the ASCII tilde (`~`) character, while `URLSearchParams` will always encode it:\n\n```js const myUrl = new URL('https://example.org/abc?foo=~bar');\n\nconsole.log(myUrl.search); // prints ?foo=~bar\n\n// Modify the URL via searchParams... myUrl.searchParams.sort();\n\nconsole.log(myUrl.search); // prints ?foo=%7Ebar ```"
"description": "Gets the `URLSearchParams` object representing the query parameters of the URL. This property is read-only but the `URLSearchParams` object it provides can be used to mutate the URL instance; to replace the entirety of query parameters of the URL, use the {@link search } setter. See `URLSearchParams` documentation for details.\n\nUse care when using `.searchParams` to modify the `URL` because, per the WHATWG specification, the `URLSearchParams` object uses different rules to determine which characters to percent-encode. For instance, the `URL` object will not percent encode the ASCII tilde (`~`) character, while `URLSearchParams` will always encode it:\n\n```js const myURL = new URL('https://example.org/abc?foo=~bar');\n\nconsole.log(myURL.search); // prints ?foo=~bar\n\n// Modify the URL via searchParams... myURL.searchParams.sort();\n\nconsole.log(myURL.search); // prints ?foo=%7Ebar ```"
},
"username": {
"type": "string",
Expand Down Expand Up @@ -839,9 +839,7 @@
"type": "string"
},
"stdio": {
"$ref": "#/definitions/StdioOptions",
"description": "Can be set to 'pipe', 'inherit', 'overlapped', or 'ignore', or an array of these strings. If passed as an array, the first element is used for `stdin`, the second for `stdout`, and the third for `stderr`. A fourth element can be used to specify the `stdio` behavior beyond the standard streams. See {@link ChildProcess.stdio } for more information.",
"default": "pipe"
"$ref": "#/definitions/StdioOptions"
},
"shell": {
"type": [
Expand Down
2 changes: 1 addition & 1 deletion packages/aws/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"@webda/workout": "^4.0.0-beta.1",
"bluebird": "^3.7.2",
"commitlint": "^19.2.1",
"cookie": "^0.6.0",
"cookie": "^1.0.1",
"glob": "^10.0.0",
"iam-policy-optimizer": "^1.2.0",
"mime-types": "^2.1.35",
Expand Down
4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@
"ajv-formats": "^3.0.1",
"antlr4ts": "^0.5.0-alpha.4",
"bcryptjs": "^2.4.3",
"cookie": "^0.6.0",
"cookie": "^1.0.1",
"dateformat": "^5.0.3",
"deepmerge-ts": "^5.1.0",
"deepmerge-ts": "^7.1.3",
"email-templates": "^12.0.1",
"glob": "^10.0.0",
"global": "^4.4.0",
Expand Down
23 changes: 14 additions & 9 deletions packages/core/src/rest/restdomainservice.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
39 changes: 39 additions & 0 deletions packages/core/src/services/domainservice.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand All @@ -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
Expand Down
71 changes: 52 additions & 19 deletions packages/core/src/unpackedapplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
UnpackedConfiguration
} from "./application";
import { FileUtils } from "./utils/serializers";
import { join } from "path";

/**
* Empty git information
Expand Down Expand Up @@ -218,6 +219,53 @@ export class UnpackedApplication extends Application {
return cacheModules;
}

/**
* Search the node_modules structure for webda.module.json files
*
* @param path
* @returns
*/
static findModulesFiles(path: string): string[] {
if (!path.endsWith("node_modules") || !fs.existsSync(path)) {
return [];
}
const files = new Set<string>();
const checkFolder = (filepath: string) => {
if (fs.existsSync(join(filepath, "webda.module.json"))) {
files.add(join(filepath, "webda.module.json"));
}
if (fs.existsSync(join(filepath, "node_modules"))) {
this.findModulesFiles(join(filepath, "node_modules")).forEach(f => files.add(f));
}
};
const recursiveSearch = (dirpath: string, depth: number = 0) => {
fs.readdirSync(dirpath, { withFileTypes: true }).forEach(file => {
if (file.name.startsWith(".")) {
return;
}
const filepath = join(dirpath, file.name);
if (file.isDirectory() && file.name.startsWith("@") && depth === 0) {
// One recursion
recursiveSearch(filepath, depth + 1);
} else if (file.isDirectory()) {
checkFolder(filepath);
} else if (file.isSymbolicLink()) {
// We want to follow symbolic links w/o increasing depth
let realPath;
try {
// realpathSync will throw if the symlink is broken
realPath = fs.realpathSync(filepath);
} catch (err) {
return;
}
checkFolder(realPath);
}
});
};
recursiveSearch(path, 0);
return [...files];
}

/**
* Load all imported modules and current module
* It will compile module
Expand All @@ -239,27 +287,12 @@ export class UnpackedApplication extends Application {
files.push(currentModule);
}

const findModuleFiles = (nodeModules: string): void => {
if (!fs.existsSync(nodeModules)) {
return;
}
FileUtils.walkSync(
nodeModules,
filepath => {
// We filter out the cache of nx
// If it is inside a node_modules/. we consider it should not be checked
if (filepath.endsWith("webda.module.json") && !filepath.includes("node_modules/.")) {
files.push(filepath);
}
},
{ followSymlinks: true }
);
};

findModuleFiles(this.getAppPath("node_modules"));
UnpackedApplication.findModulesFiles(this.getAppPath("node_modules")).forEach(f => files.push(f));
// Search workspace for webda.module.json
if (module.project.webda.workspaces && module.project.webda.workspaces.path !== "") {
findModuleFiles(path.join(module.project.webda.workspaces.path, "node_modules"));
UnpackedApplication.findModulesFiles(path.join(module.project.webda.workspaces.path, "node_modules")).forEach(f =>
files.push(f)
);
}

// Ensure we are not adding many times the same modules
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/utils/context.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,14 @@ class ContextTest extends WebdaTest {
cookie: "PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1"
});
assert.strictEqual(ctx.getAbsoluteUrl("/test"), "http://test.webda.io/test");
assert.deepStrictEqual(ctx.getCookies(), {
const cookies = ctx.getCookies();
const valid = {
PHPSESSID: "298zf09hf012fh2",
csrftoken: "u32t4o3tb3gg43",
_gat: "1"
};
Object.keys(cookies).forEach(key => {
assert.deepEqual(cookies[key], valid[key]);
});
ctx = new HttpContext("test.webda.io", "GET", "/uritemplate/plop", "https", 80);
assert.strictEqual(ctx.getAbsoluteUrl(), "https://test.webda.io:80/uritemplate/plop");
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/utils/cookie.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CookieSerializeOptions, serialize as cookieSerialize } from "cookie";
import { type SerializeOptions, serialize as cookieSerialize } from "cookie";
import { JWTOptions } from "../services/cryptoservice";
import { WebContext } from "./context";
import { HttpContext } from "./httpcontext";
Expand All @@ -12,7 +12,7 @@ const SPLIT = 4096;
/**
* Cookie Options
*/
export class CookieOptions implements Omit<CookieSerializeOptions, "domain"> {
export class CookieOptions implements Omit<SerializeOptions, "domain"> {
/**
* @default lax
*/
Expand Down Expand Up @@ -164,7 +164,7 @@ export class SecureCookie {
name ??= "webda";
let cookieName = name;
let limit;
const mapLength = cookieSerialize(name, "", <CookieSerializeOptions>params).length;
const mapLength = cookieSerialize(name, "", <SerializeOptions>params).length;
for (let i = 0; i < value.length; ) {
limit = SPLIT - mapLength;
if (j > 1) {
Expand Down
Loading