Skip to content

Commit

Permalink
fix(formio): fix import/export formio
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Mar 5, 2022
1 parent f01f845 commit 97efbfa
Show file tree
Hide file tree
Showing 10 changed files with 445 additions and 27 deletions.
2 changes: 1 addition & 1 deletion packages/third-parties/formio/.nycrc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
],
"check-coverage": true,
"statements": 100,
"branches": 81.16,
"branches": 80.43,
"functions": 100,
"lines": 100
}
3 changes: 2 additions & 1 deletion packages/third-parties/formio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@
"@tsed/core": "^6.103.2",
"@tsed/di": "^6.103.2",
"@tsed/mongoose": "^6.103.2",
"lodash": "^4.17.21",
"express": "^4.17.1",
"formio": "^2.0.0",
"mongodb": "*",
"mongoose": "^5.12.2"
}
}
}
82 changes: 82 additions & 0 deletions packages/third-parties/formio/src/components/AlterActions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,4 +290,86 @@ describe("AlterActions", () => {
});
expect(result.message).to.eq("bad request");
});
it("should create the new actions and call next (with status and headers)", async () => {
@Action({
name: "custom",
title: "My custom Action",
description: "Custom action description",
priority: 0,
defaults: {
handler: [],
method: []
}
})
class CustomAction implements ActionMethods {
async resolve(@ActionCtx() actionCtx: ActionCtx, @Context() ctx: PlatformContext) {
return {
statusText: "Created",
status: 201,
headers: {
"x-header": "test"
},
data: {
hello: "world"
}
};
}

async settingsForm() {
return [{} as any];
}
}

const formio = {
Action: class Action {}
};

// PlatformTest.injector.forkProvider(CustomAction);
PlatformTest.injector.invoke(CustomAction);

const {ctx, alterActions} = await getActionsFixture(formio);

let actions: FormioActions = {} as any;

actions = alterActions.transform(actions);

const info: FormioActionInfo = await new Promise((resolve) => {
actions.custom.info(ctx.getRequest(), ctx.getResponse(), (err, info) => resolve(info));
});

const settings: FormioComponent[] = await new Promise((resolve) => {
actions.custom.settingsForm(ctx.getRequest(), ctx.getResponse(), (err, components) => resolve(components));
});

const instance = new actions.custom(info as any, ctx.getRequest(), ctx.getResponse());

new Promise((resolve) => {
instance.resolve(
"handler",
"method",
ctx.getRequest(),
ctx.getResponse(),
(err: any, result: any) => resolve(result),
"setActionItemMessage" as any
);
});

await new Promise((r) => setTimeout(r, 200));

expect(instance).to.be.instanceOf(formio.Action);
expect(info).to.deep.eq({
defaults: {
handler: [],
method: []
},
description: "Custom action description",
name: "custom",
priority: 0,
title: "My custom Action"
});
expect(settings).to.deep.eq([{}]);
expect(ctx.response.raw.data).to.deep.eq({
hello: "world"
});
});
});
76 changes: 64 additions & 12 deletions packages/third-parties/formio/src/components/AlterActions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import {EndpointMetadata, PlatformContext, PlatformHandler} from "@tsed/common";
import {Inject, InjectorService, Provider} from "@tsed/di";
import {EndpointMetadata} from "@tsed/schema";
import {FormioActionInfo} from "@tsed/formio-types";
import {PlatformParams} from "@tsed/platform-params";
import {PlatformResponseFilter} from "@tsed/platform-response-filter";
import {AnyToPromise, AnyToPromiseStatus} from "@tsed/core";
import {PlatformContext, setResponseHeaders} from "@tsed/common";
import {Alter} from "../decorators/alter";
import {AlterHook} from "../domain/AlterHook";
import {SetActionItemMessage} from "../domain/FormioAction";
Expand All @@ -15,18 +19,25 @@ export class AlterActions implements AlterHook {
@Inject()
protected formio: FormioService;

@Inject()
protected params: PlatformParams;

@Inject()
protected responseFilter: PlatformResponseFilter;

transform(actions: FormioActions): FormioActions {
const {Action} = this.formio;

return this.getActions().reduce((actions, provider) => {
const instance = this.injector.invoke<any>(provider.token);
const options = provider.store.get<FormioActionInfo>("formio:action");
const resolveHandler = this.createResolveHandler(provider);
const resolveHandler = this.createHandler(provider, "resolve");

return {
...actions,
[options.name]: class extends Action {
static access = options.access;

static async info(req: any, res: any, next: Function) {
let opts = {...options};

Expand All @@ -53,25 +64,66 @@ export class AlterActions implements AlterHook {
return this.injector.getProviders("formio:action");
}

protected createResolveHandler(provider: Provider<any>) {
const platformHandler = this.injector.get<PlatformHandler>(PlatformHandler)!;
const middleware = platformHandler.createCustomHandler(provider, "resolve");
protected createHandler(provider: Provider, propertyKey: string | symbol) {
const promisedHandler = this.params.compileHandler({
token: provider.token,
propertyKey
});

return (
return async (
action: any,
handler: string,
method: string,
req: {$ctx: PlatformContext},
res: Response,
res: any,
next: any,
setActionItemMessage: SetActionItemMessage
) => {
req.$ctx.set("ACTION_CTX", {handler, method, setActionItemMessage, action});
req.$ctx.endpoint = EndpointMetadata.get(provider.useClass, "resolve");
const $ctx = req.$ctx;
$ctx.set("ACTION_CTX", {handler, method, setActionItemMessage, action});
$ctx.endpoint = EndpointMetadata.get(provider.useClass, "resolve");

await this.injector.runInContext($ctx, async () => {
try {
const resolver = new AnyToPromise();
const handler = await promisedHandler;
const {state, data, status, headers} = await resolver.call(() => handler({$ctx}));
if (state === AnyToPromiseStatus.RESOLVED) {
if (status) {
$ctx.response.status(status);
}

if (headers) {
$ctx.response.setHeaders(headers);
}

if (data !== undefined) {
$ctx.data = data;

middleware(req.$ctx)
.then(() => (req.$ctx.data ? platformHandler.flush(req.$ctx.data, req.$ctx) : next()))
.catch(next);
return await this.flush($ctx.data, $ctx);
}

next();
}
} catch (er) {
next(er);
}
});
};
}

private async flush(data: any, $ctx: PlatformContext) {
const {response} = $ctx;

if (!response.isDone()) {
setResponseHeaders($ctx);

data = await this.responseFilter.serialize(data, $ctx);
data = await this.responseFilter.transform(data, $ctx);

response.body(data);
}

return response;
}
}
4 changes: 2 additions & 2 deletions packages/third-parties/formio/src/components/AlterAudit.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {PlatformContext} from "@tsed/common";
import {DIContext} from "@tsed/di";
import {Alter} from "../decorators/alter";
import {AlterHook} from "../domain/AlterHook";

@Alter("audit")
export class AlterAudit implements AlterHook {
transform(info: any[], event: string, ctx: PlatformContext): boolean {
transform(info: any[], event: string, ctx: DIContext): boolean {
ctx.logger.info({event, info});
return false;
}
Expand Down
1 change: 1 addition & 0 deletions packages/third-parties/formio/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ export * from "./services/FormioAuthService";
export * from "./services/FormioDatabase";
export * from "./services/FormioHooksService";
export * from "./services/FormioInstaller";
export * from "./services/FormioRepository";
export * from "./services/FormioService";
export * from "./utils/isMongoId";
82 changes: 77 additions & 5 deletions packages/third-parties/formio/src/services/FormioDatabase.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ async function createServiceFixture() {
static countDocuments = sandbox.stub();
static find = sandbox.stub().resolves([{_id: "form_id", machineName: "form_machine"}]);
static findOne = sandbox.stub().returnsThis();
static findOneAndUpdate = sandbox.stub();
static updateOne = sandbox.stub().returnsThis();
static lean = sandbox.stub().returnsThis();
static exec = sandbox.stub();
Expand All @@ -33,7 +34,8 @@ async function createServiceFixture() {
find: sandbox.stub().resolves([{_id: "action_id", machineName: "action_machine"}])
},
submission: {
find: sandbox.stub()
find: sandbox.stub().resolves([]),
findOneAndUpdate: sandbox.stub().callsFake((o, o1) => ({...o, ...o1, _id: o._id || "newID"}))
},
token: {},
actionItem: {}
Expand Down Expand Up @@ -157,7 +159,7 @@ describe("FormioDatabase", () => {
expect(service.actionItemModel).to.deep.eq(formioService.mongoose.models.actionItem);
});
});
describe("createFormIfNotExists()", () => {
describe("importFormIfNotExists()", () => {
it("should return create the form if not exists", async () => {
const {service, formioService} = await createServiceFixture();
const onCreate = sandbox.stub();
Expand All @@ -171,7 +173,7 @@ describe("FormioDatabase", () => {
name: "name"
});

const result = await service.createFormIfNotExists(form, onCreate);
const result = await service.importFormIfNotExists(form, onCreate);

expect(result).to.deep.eq({
_id: "id",
Expand All @@ -192,15 +194,86 @@ describe("FormioDatabase", () => {
name: "name"
});

const result = await service.createFormIfNotExists(form, onCreate);
const result = await service.importFormIfNotExists(form, onCreate);

expect(result).to.deep.equal({
_id: "id",
name: "name"
});
});
});
describe("getSubmissions()", () => {
it("should return submissions", async () => {
const {service} = await createServiceFixture();

const result = await service.getSubmissions();

expect(result).to.deep.eq([]);
expect(service.submissionModel.find).to.have.been.calledWithExactly({deleted: {$eq: null}});
});
it("should return submissions with query", async () => {
const {service} = await createServiceFixture();

const result = await service.getSubmissions({
form: "id"
});

expect(result).to.deep.eq([]);
expect(service.submissionModel.find).to.have.been.calledWithExactly({deleted: {$eq: null}, form: "id"});
});
});
describe("saveSubmissions()", () => {
it("should create submission", async () => {
const {service} = await createServiceFixture();

const result = await service.saveSubmission({
data: {}
});

expect(result).to.deep.eq({
_id: "newID",
data: {}
});
expect(service.submissionModel.findOneAndUpdate).to.have.been.calledWithExactly(
{_id: undefined},
{data: {}},
{new: true, upsert: true}
);
});
it("should update submission", async () => {
const {service} = await createServiceFixture();

const result = await service.saveSubmission({
_id: "id",
data: {}
});

expect(result).to.deep.eq({
_id: "id",
data: {}
});
expect(service.submissionModel.findOneAndUpdate).to.have.been.calledWithExactly(
{_id: "id"},
{_id: "id", data: {}},
{new: true, upsert: true}
);
});
});
describe("importSubmission()", () => {
it("should import submission", async () => {
const {service} = await createServiceFixture();

Sinon.stub(service, "saveSubmission").resolves({} as any);

const result = await service.importSubmission({
_id: "id",
data: {}
});

expect(result).to.deep.eq({});
expect(service.saveSubmission).to.have.been.calledWithExactly({_id: "id", data: {}});
});
});
describe("getForm", () => {
it("should return form from id", async () => {
const {service, formioService} = await createServiceFixture();
Expand Down Expand Up @@ -228,7 +301,6 @@ describe("FormioDatabase", () => {
expect(formioService.mongoose.models.form.exec).to.have.been.calledWithExactly();
});
});

describe("idToBson", () => {
beforeEach(() => {});
it("should convert id", async () => {
Expand Down
Loading

0 comments on commit 97efbfa

Please sign in to comment.