Skip to content

Commit

Permalink
feat: allow context propagation in linked model
Browse files Browse the repository at this point in the history
  • Loading branch information
loopingz committed Oct 7, 2023
1 parent 2d1ef2e commit e84df9a
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 58 deletions.
63 changes: 39 additions & 24 deletions docs/pages/DebugInVSCode.md
@@ -1,33 +1,48 @@
# Debug in VSCode

Seems we need to add the outFiles as absolute path
Launch in your "Javascript Debugger Console".

For unit tests, you have to specify something:

```
```


```
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
version: "0.2.0",
configurations: [
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
type: "pwa-node",
request: "launch",
name: `Debug ${"projectName"} Webda API`, // Add project-name
skipFiles: ["<node_internals>/**"],
runtimeArgs: ["--nolazy"],
program: "node_modules/.bin/webda",
args: ["--notty", "--noCompile", "--logLevel", "TRACE", "serve", "--devMode"],
sourceMaps: true,
smartStep: true,
console: "integratedTerminal",
autoAttachChildProcesses: true,
outFiles: [
"${absPath}/lib/**/*.js", // Replace by abspath
"${absPath}/lib/*.js",
"/datas/git/webda.io/packages/**/lib/**/*.js",
"/datas/git/webda.io/packages/**/lib/*.js"
]
"type": "node",
"request": "launch",
"name": "Launch Webda Test",
"skipFiles": ["<node_internals>/**"],
"runtimeArgs": ["--nolazy"],
"program": "node_modules/.bin/mocha",
"cwd": "${workspaceFolder}/packages/core",
"args": [
"--exit",
"--timeout=30000",
"-r",
"../../node_modules/ts-node/register",
"src/**/*.spec.ts",
"src/*.spec.ts",
"src/**/**/*.spec.ts",
"-g",
"DomainServiceTest"
],
"sourceMaps": true,
"smartStep": true,
"console": "integratedTerminal",
"autoAttachChildProcesses": true,
"outFiles": [
"/datas/git/webda.io/packages/**/lib/**/*.js",
"/datas/git/webda.io/packages/**/lib/*.js"
]
}
]
]
}
```
16 changes: 12 additions & 4 deletions packages/core/src/models/aclmodel.spec.ts
@@ -1,6 +1,6 @@
import { suite, test } from "@testdeck/mocha";
import * as assert from "assert";
import { AclModel, Core, HttpContext, Session, SimpleUser, WebContext, WebdaError } from "../index";
import { AclModel, Core, HttpContext, Session, SimpleUser, User, WebContext, WebdaError } from "../index";
import { TestApplication } from "../test";
import { getCommonJS } from "../utils/esm";

Expand Down Expand Up @@ -133,16 +133,24 @@ class AclModelTest {
this._ctx.reinit();
this._ctx.getHttpContext().setBody({ raw: { acl: "mine" } });
await assert.rejects(() => this.model.acl(this._ctx));
this._ctx.reinit();
this._ctx.setHttpContext(new HttpContext("test.webda.io", "GET", "/"));
await this.model.acl(this._ctx);
assert.deepStrictEqual(JSON.parse(<string>this._ctx.getResponseBody()), {
// Action return their result directly
await User.ref("acl").create({
displayName: "Plopi"
});
let res = await this.model.acl(this._ctx);
assert.deepStrictEqual(res, {
raw: {
acl: "mine"
},
resolved: [
{
actor: {
displayName: "Plopi"
avatar: undefined,
displayName: "Plopi",
email: undefined,
uuid: "acl"
},
permission: "mine"
}
Expand Down
24 changes: 14 additions & 10 deletions packages/core/src/models/coremodel.ts
Expand Up @@ -327,17 +327,20 @@ export class ModelRef<T extends CoreModel> {
protected store: Store<T>;
@NotEnumerable
protected model: CoreModelDefinition<T>;
@NotEnumerable
protected parent: CoreModel;

constructor(
protected uuid: string,
model: CoreModelDefinition<T>
model: CoreModelDefinition<T>,
parent?: CoreModel,
) {
this.model = model;
this.uuid = uuid === "" ? undefined : model.completeUid(uuid);
this.store = Core.get().getModelStore(model);
}
async get(context?: OperationContext): Promise<T> {
return (await this.store.get(this.uuid))?.setContext(context);
return (await this.store.get(this.uuid))?.setContext(context || this.parent?.getContext());
}
set(id: string | T) {
this.uuid = id instanceof CoreModel ? id.getUuid() : id;
Expand Down Expand Up @@ -430,9 +433,10 @@ export class ModelRefCustom<T extends CoreModel> extends ModelRef<T> {
constructor(
public uuid: string,
model: CoreModelDefinition<T>,
data: any
data: any,
parent: CoreModel
) {
super(uuid, model);
super(uuid, model, parent);
Object.assign(this, data);
}

Expand Down Expand Up @@ -974,19 +978,19 @@ class CoreModel {
if (link.type === "LINK") {
this[link.attribute] ??= "";
if (typeof this[link.attribute] === "string") {
this[link.attribute] = new ModelRef(this[link.attribute], model);
this[link.attribute] = new ModelRef(this[link.attribute], model, this);
}
} else if (link.type === "LINKS_ARRAY") {
this[link.attribute] = new ModelLinksArray(model, this[link.attribute]);
this[link.attribute] = new ModelLinksArray(model, this[link.attribute], this);
} else if (link.type === "LINKS_SIMPLE_ARRAY") {
this[link.attribute] = new ModelLinksSimpleArray(model, this[link.attribute]);
this[link.attribute] = new ModelLinksSimpleArray(model, this[link.attribute], this);
} else if (link.type === "LINKS_MAP") {
this[link.attribute] = createModelLinksMap(model, this[link.attribute]);
this[link.attribute] = createModelLinksMap(model, this[link.attribute], this);
}
}
for (let link of rel.maps || []) {
this[link.attribute] = (this[link.attribute] || []).map(
el => new ModelMapLoaderImplementation(Core.get().getModel(link.model), el)
el => new ModelMapLoaderImplementation(Core.get().getModel(link.model), el, this)
);
}
for (let query of rel.queries || []) {
Expand All @@ -995,7 +999,7 @@ class CoreModel {
if (rel.parent) {
this[rel.parent.attribute] ??= "";
if (typeof this[rel.parent.attribute] === "string") {
this[rel.parent.attribute] = new ModelRef(this[rel.parent.attribute], Core.get().getModel(rel.parent.model));
this[rel.parent.attribute] = new ModelRef(this[rel.parent.attribute], Core.get().getModel(rel.parent.model), this);
}
}
for (let binary of rel.binaries || []) {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/models/ownermodel.ts
Expand Up @@ -27,7 +27,7 @@ export class OwnerModel extends UuidModel {
* @param uuid
*/
setOwner(uuid: string): void {
this._user ??= new ModelLink<User>(uuid, <any>User);
this._user ??= new ModelLink<User>(uuid, <any>User, this);
this._user.set(uuid);
}

Expand Down
47 changes: 34 additions & 13 deletions packages/core/src/models/relations.ts
@@ -1,3 +1,4 @@
import { Core } from "../core";
import {
CoreModel,
CoreModelDefinition,
Expand Down Expand Up @@ -77,12 +78,19 @@ export type ModelLink<T extends CoreModel, _FK extends keyof T = any> = ModelLoa
};
*/
export class ModelLink<T extends CoreModel> {
@NotEnumerable
protected parent: CoreModel;

constructor(
protected uuid: string,
protected model: CoreModelDefinition<T>
) {}
protected model: CoreModelDefinition<T>,
parent?: CoreModel
) {
this.parent = parent;
}

async get(): Promise<T> {
return this.model.ref(this.uuid).get();
return (await this.model.ref(this.uuid).get())?.setContext(this.parent?.getContext());
}
set(id: string | T) {
this.uuid = typeof id === "string" ? id : id.getUuid();
Expand Down Expand Up @@ -117,16 +125,21 @@ type ModelCollectionManager<T> = {
};

export class ModelLinksSimpleArray<T extends CoreModel> extends Array<ModelRef<T>> {
@NotEnumerable
private parent: CoreModel;

constructor(
protected model: CoreModelDefinition<T>,
content: any[] = []
content: any[] = [],
parent?: CoreModel
) {
super();
content.forEach(c => this.add(c));
this.parent = parent;
}

add(model: string | ModelRef<T> | T) {
this.push(typeof model === "string" ? new ModelRef(model, this.model) : new ModelRef(model.getUuid(), this.model));
this.push(typeof model === "string" ? new ModelRef(model, this.model, this.parent) : new ModelRef(model.getUuid(), this.model, this.parent));
}

remove(model: ModelRef<T> | string | T) {
Expand All @@ -140,13 +153,17 @@ export class ModelLinksSimpleArray<T extends CoreModel> extends Array<ModelRef<T
export class ModelLinksArray<T extends CoreModel, K> extends Array<
ModelRefCustomProperties<T, (K & { uuid: string }) | { getUuid: () => string }>
> {
@NotEnumerable
parent: CoreModel;
constructor(
protected model: CoreModelDefinition<T>,
content: any[] = []
content: any[] = [],
parent?: CoreModel,
) {
super();
this.parent = parent;
this.push(
...content.filter(c => c && c.uuid).map(c => <ModelRefCustomProperties<T, K>>new ModelRefCustom(c.uuid, model, c))
...content.filter(c => c && c.uuid).map(c => <ModelRefCustomProperties<T, K>>new ModelRefCustom(c.uuid, model, c, this.parent))
);
}

Expand All @@ -162,7 +179,8 @@ export class ModelLinksArray<T extends CoreModel, K> extends Array<
: new ModelRefCustom(
(<{ uuid: string }>model).uuid || (<{ getUuid: () => string }>model).getUuid(),
this.model,
model
model,
this.parent
))
)
);
Expand All @@ -187,7 +205,7 @@ export type ModelLinksMap<T extends CoreModel, K> = Readonly<{
}> &
ModelCollectionManager<K & ({ uuid: string } | { getUuid: () => string })>;

export function createModelLinksMap<T extends CoreModel>(model: CoreModelDefinition<any>, data: any = {}) {
export function createModelLinksMap<T extends CoreModel>(model: CoreModelDefinition<any>, data: any = {}, parent?: CoreModel) {
let result = {
add: (model: ModelRefCustomProperties<T, any>) => {
result[model.uuid || model.getUuid()] = model;
Expand All @@ -201,7 +219,7 @@ export function createModelLinksMap<T extends CoreModel>(model: CoreModelDefinit
Object.keys(data)
.filter(k => k !== "__proto__")
.forEach(key => {
result[key] = new ModelRefCustom(data[key].uuid, model, data[key]);
result[key] = new ModelRefCustom(data[key].uuid, model, data[key], parent);
});
return result;
}
Expand All @@ -226,23 +244,26 @@ export type ModelActions = {
*/
export class ModelMapLoaderImplementation<T extends CoreModel, K = any> {
@NotEnumerable
protected _model;
protected _model: CoreModelDefinition<T>;
@NotEnumerable
protected _parent: CoreModel;
/**
* The uuid of the object
*/
public uuid: string;

constructor(model: CoreModelDefinition<T>, data: { uuid: string } & K) {
constructor(model: CoreModelDefinition<T>, data: { uuid: string } & K, parent: CoreModel) {
Object.assign(this, data);
this._model = model;
this._parent = parent;
}

/**
*
* @returns the model
*/
async get(): Promise<T> {
return this._model.ref(this.uuid).get();
return this._model.ref(this.uuid).get(this._parent.getContext());
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/services/invitationservice.spec.ts
Expand Up @@ -522,7 +522,7 @@ class InvitationTest extends WebdaTest {
await ctx2.getCurrentUser(true);
ctx2.getCurrentUser = async () => {
let user = await ident4.getUser().get();
user.getIdents = () => [<ModelMapLoader<Ident, "_type">>new ModelMapLoaderImplementation(ident4.__class, ident4)];
user.getIdents = () => [<ModelMapLoader<Ident, "_type">>new ModelMapLoaderImplementation(ident4.__class, ident4, user)];
return <any>user;
};
await this.execute(ctx2, "test.webda.io", "PUT", `/companies/${company.getUuid()}/invitations`, {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/stores/file.spec.ts
Expand Up @@ -80,7 +80,7 @@ class FileStoreTest extends StoreTest {
);
await user.refresh();
assert.strictEqual(user.plop, undefined);
user.idents[0] = new ModelMapLoaderImplementation(identStore._model, user.idents[0]);
user.idents[0] = new ModelMapLoaderImplementation(identStore._model, user.idents[0], user);
userStore["initModel"](user);
await identStore.delete(user.getUuid());
} finally {
Expand Down
4 changes: 2 additions & 2 deletions packages/hawk/webda.module.json
Expand Up @@ -10,7 +10,7 @@
"links": [
{
"attribute": "_user",
"model": "Webda/User",
"model": "unknown",
"type": "LINK"
}
]
Expand All @@ -36,7 +36,7 @@
"___checker": "(address: string) => boolean",
"origins": "string[]",
"whitelist": "string[]",
"_user": "ModelLink<User>",
"_user": "ModelLink<T>",
"public": "boolean",
"uuid": "string",
"___class": "CoreModelDefinition",
Expand Down
19 changes: 17 additions & 2 deletions sample-app/src/models/relations.ts
Expand Up @@ -79,7 +79,15 @@ class Classroom extends DefaultTestModel {
hardwares: ModelRelated<Hardware, "classroom">;

@Action()
test(context: OperationContext<{ test: string; id: string }>) {}
async test(context: OperationContext<{ test: string; id: string }>) {
// Testing action is waited for
await new Promise<void>((resolve) => {
setTimeout(() => {
context.write({});
resolve();
}, 50);
});
}
}

@Expose({ root: true })
Expand All @@ -89,7 +97,14 @@ class Hardware extends DefaultTestModel {
brands: ModelRelated<Brand, "name">;

@Action()
static globalAction(context: OperationContext) {
static async globalAction(context: OperationContext) {
// Testing action is waited for
await new Promise<void>((resolve) => {
setTimeout(() => {
context.write({});
resolve();
}, 50);
});
return;
}
}
Expand Down

0 comments on commit e84df9a

Please sign in to comment.