@tsed/formio 8.0.1 → 8.0.2

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.
Files changed (65) hide show
  1. package/package.json +11 -10
  2. package/src/FormioModule.spec.ts +113 -0
  3. package/src/FormioModule.ts +130 -0
  4. package/src/builder/FormioMapper.spec.ts +93 -0
  5. package/src/builder/FormioMapper.ts +71 -0
  6. package/src/components/AlterActions.spec.ts +376 -0
  7. package/src/components/AlterActions.ts +137 -0
  8. package/src/components/AlterAudit.spec.ts +19 -0
  9. package/src/components/AlterAudit.ts +12 -0
  10. package/src/components/AlterHost.spec.ts +20 -0
  11. package/src/components/AlterHost.ts +11 -0
  12. package/src/components/AlterLog.spec.ts +19 -0
  13. package/src/components/AlterLog.ts +12 -0
  14. package/src/components/AlterSkip.spec.ts +44 -0
  15. package/src/components/AlterSkip.ts +28 -0
  16. package/src/components/AlterTemplateExportSteps.spec.ts +99 -0
  17. package/src/components/AlterTemplateExportSteps.ts +58 -0
  18. package/src/components/AlterTemplateImportSteps.spec.ts +70 -0
  19. package/src/components/AlterTemplateImportSteps.ts +50 -0
  20. package/src/decorators/action.ts +20 -0
  21. package/src/decorators/actionCtx.spec.ts +25 -0
  22. package/src/decorators/actionCtx.ts +29 -0
  23. package/src/decorators/alter.spec.ts +16 -0
  24. package/src/decorators/alter.ts +19 -0
  25. package/src/decorators/on.spec.ts +16 -0
  26. package/src/decorators/on.ts +19 -0
  27. package/src/decorators/useFormioAuth.spec.ts +15 -0
  28. package/src/decorators/useFormioAuth.ts +12 -0
  29. package/src/domain/AlterHook.ts +3 -0
  30. package/src/domain/Formio.ts +122 -0
  31. package/src/domain/FormioAction.ts +30 -0
  32. package/src/domain/FormioActionsIndex.ts +19 -0
  33. package/src/domain/FormioAuth.ts +83 -0
  34. package/src/domain/FormioBaseModel.ts +14 -0
  35. package/src/domain/FormioConfig.ts +63 -0
  36. package/src/domain/FormioCtxMapper.ts +8 -0
  37. package/src/domain/FormioDecodedToken.ts +13 -0
  38. package/src/domain/FormioErrors.ts +53 -0
  39. package/src/domain/FormioHooks.ts +207 -0
  40. package/src/domain/FormioJs.ts +18 -0
  41. package/src/domain/FormioModels.ts +48 -0
  42. package/src/domain/FormioRouter.ts +10 -0
  43. package/src/domain/FormioSettings.ts +61 -0
  44. package/src/domain/FormioTemplate.ts +9 -0
  45. package/src/domain/FormioTemplateUtil.ts +15 -0
  46. package/src/domain/FormioUpdate.ts +23 -0
  47. package/src/domain/FormioUtils.ts +331 -0
  48. package/src/domain/OnHook.ts +3 -0
  49. package/src/domain/Resource.ts +21 -0
  50. package/src/index.ts +46 -0
  51. package/src/middlewares/FormioAuthMiddleware.spec.ts +61 -0
  52. package/src/middlewares/FormioAuthMiddleware.ts +33 -0
  53. package/src/services/FormioAuthService.spec.ts +396 -0
  54. package/src/services/FormioAuthService.ts +227 -0
  55. package/src/services/FormioDatabase.spec.ts +326 -0
  56. package/src/services/FormioDatabase.ts +165 -0
  57. package/src/services/FormioHooksService.spec.ts +156 -0
  58. package/src/services/FormioHooksService.ts +91 -0
  59. package/src/services/FormioInstaller.spec.ts +146 -0
  60. package/src/services/FormioInstaller.ts +46 -0
  61. package/src/services/FormioRepository.spec.ts +114 -0
  62. package/src/services/FormioRepository.ts +49 -0
  63. package/src/services/FormioService.spec.ts +368 -0
  64. package/src/services/FormioService.ts +133 -0
  65. package/src/utils/isMongoId.ts +3 -0
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tsed/formio",
3
3
  "description": "Formio package for Ts.ED framework",
4
4
  "type": "module",
5
- "version": "8.0.1",
5
+ "version": "8.0.2",
6
6
  "keywords": [
7
7
  "Formio",
8
8
  "Ts.ED",
@@ -17,6 +17,7 @@
17
17
  "typings": "./lib/types/index.d.ts",
18
18
  "exports": {
19
19
  ".": {
20
+ "@tsed/source": "./src/index.ts",
20
21
  "types": "./lib/types/index.d.ts",
21
22
  "import": "./lib/esm/index.js",
22
23
  "default": "./lib/esm/index.js"
@@ -31,17 +32,17 @@
31
32
  "test:ci": "vitest run --coverage.thresholds.autoUpdate=true"
32
33
  },
33
34
  "dependencies": {
34
- "@tsed/formio-types": "8.0.1",
35
- "@tsed/normalize-path": "8.0.1",
35
+ "@tsed/formio-types": "8.0.2",
36
+ "@tsed/normalize-path": "8.0.2",
36
37
  "tslib": "2.7.0"
37
38
  },
38
39
  "devDependencies": {
39
- "@tsed/barrels": "8.0.1",
40
- "@tsed/core": "8.0.1",
41
- "@tsed/di": "8.0.1",
42
- "@tsed/mongoose": "8.0.1",
43
- "@tsed/platform-http": "8.0.1",
44
- "@tsed/typescript": "8.0.1",
40
+ "@tsed/barrels": "8.0.2",
41
+ "@tsed/core": "8.0.2",
42
+ "@tsed/di": "8.0.2",
43
+ "@tsed/mongoose": "8.0.2",
44
+ "@tsed/platform-http": "8.0.2",
45
+ "@tsed/typescript": "8.0.2",
45
46
  "@types/async": "3.2.24",
46
47
  "eslint": "9.12.0",
47
48
  "express": "^4.21.0",
@@ -52,7 +53,7 @@
52
53
  "vitest": "2.1.2"
53
54
  },
54
55
  "peerDependencies": {
55
- "@tsed/platform-http": "8.0.1",
56
+ "@tsed/platform-http": "8.0.2",
56
57
  "express": "^4.17.1",
57
58
  "formio": ">=2.0.0",
58
59
  "lodash": "^4.17.21",
@@ -0,0 +1,113 @@
1
+ import {faker} from "@faker-js/faker";
2
+ import {PlatformApplication, PlatformRouteDetails} from "@tsed/platform-http";
3
+ import {PlatformTest} from "@tsed/platform-http/testing";
4
+
5
+ import {FormioModule} from "./FormioModule.js";
6
+ import {FormioInstaller} from "./services/FormioInstaller.js";
7
+ import {FormioService} from "./services/FormioService.js";
8
+
9
+ async function createFormioModuleFixture() {
10
+ const formio = {
11
+ swagger: vi.fn(),
12
+ router: vi.fn(),
13
+ middleware: {
14
+ restrictRequestTypes: vi.fn()
15
+ },
16
+ isInit: vi.fn().mockReturnValue(true),
17
+ init: vi.fn()
18
+ };
19
+
20
+ const installer = {
21
+ hasForms: vi.fn().mockReturnValue(false),
22
+ install: vi.fn()
23
+ };
24
+
25
+ const app = {
26
+ use: vi.fn(),
27
+ getRouter: vi.fn().mockReturnThis()
28
+ };
29
+
30
+ const service = await PlatformTest.invoke<FormioModule>(FormioModule, [
31
+ {
32
+ token: FormioInstaller,
33
+ use: installer
34
+ },
35
+ {
36
+ token: FormioService,
37
+ use: formio
38
+ },
39
+ {
40
+ token: PlatformApplication,
41
+ use: app
42
+ }
43
+ ]);
44
+
45
+ return {service, app, installer, formio};
46
+ }
47
+
48
+ describe("FormioModule", () => {
49
+ beforeEach(() => PlatformTest.create());
50
+ afterEach(PlatformTest.reset);
51
+
52
+ describe("$onRoutesInit()", () => {
53
+ it("should run install template", async () => {
54
+ const {service, app, installer, formio} = await createFormioModuleFixture();
55
+
56
+ const template = {
57
+ forms: {}
58
+ };
59
+ const root = {
60
+ email: faker.internet.email()
61
+ };
62
+
63
+ PlatformTest.injector.settings.set("formio.template", template);
64
+ PlatformTest.injector.settings.set("formio.root", root);
65
+
66
+ installer.install.mockResolvedValue((service as any).template);
67
+
68
+ await service.$onRoutesInit();
69
+
70
+ expect(app.use).toHaveBeenCalledWith("/", formio.middleware.restrictRequestTypes, formio.router);
71
+ expect(installer.install).toHaveBeenCalledWith(template, root);
72
+ });
73
+ it("should skip install", async () => {
74
+ const {service, installer} = await createFormioModuleFixture();
75
+ PlatformTest.injector.settings.set("formio.template", {});
76
+
77
+ await service.$onRoutesInit();
78
+
79
+ expect(installer.install).toHaveBeenCalled();
80
+ });
81
+ });
82
+ describe("$logRoutes()", () => {
83
+ it("should add formio routes", async () => {
84
+ const {service, formio} = await createFormioModuleFixture();
85
+ const routes: PlatformRouteDetails[] = [];
86
+
87
+ PlatformTest.injector.settings.set("formio.baseUrl", "/projects");
88
+ PlatformTest.injector.settings.set("formio.root");
89
+
90
+ formio.swagger.mockReturnValue({
91
+ paths: {
92
+ "/path/to": {
93
+ get: {
94
+ operationId: "operationId"
95
+ }
96
+ }
97
+ }
98
+ });
99
+
100
+ const results = await service.$logRoutes(routes);
101
+
102
+ expect(results).toEqual([
103
+ {
104
+ className: "formio",
105
+ method: "get",
106
+ methodClassName: "operationId",
107
+ name: "operationId",
108
+ url: "/projects/path/to"
109
+ }
110
+ ]);
111
+ });
112
+ });
113
+ });
@@ -0,0 +1,130 @@
1
+ import {deepClone} from "@tsed/core";
2
+ import {Constant, Inject, InjectorService, Module} from "@tsed/di";
3
+ import {normalizePath} from "@tsed/normalize-path";
4
+ import {OnReady, OnRoutesInit, PlatformApplication, PlatformRouteDetails} from "@tsed/platform-http";
5
+
6
+ import {AlterActions} from "./components/AlterActions.js";
7
+ import {AlterAudit} from "./components/AlterAudit.js";
8
+ import {AlterHost} from "./components/AlterHost.js";
9
+ import {AlterLog} from "./components/AlterLog.js";
10
+ import {AlterSkip} from "./components/AlterSkip.js";
11
+ import {AlterTemplateExportSteps} from "./components/AlterTemplateExportSteps.js";
12
+ import {AlterTemplateImportSteps} from "./components/AlterTemplateImportSteps.js";
13
+ import {FormioConfig} from "./domain/FormioConfig.js";
14
+ import {FormioTemplate} from "./domain/FormioTemplate.js";
15
+ import {FormioAuthService} from "./services/FormioAuthService.js";
16
+ import {FormioHooksService} from "./services/FormioHooksService.js";
17
+ import {FormioInstaller} from "./services/FormioInstaller.js";
18
+ import {FormioService} from "./services/FormioService.js";
19
+
20
+ @Module({
21
+ imports: [
22
+ FormioService,
23
+ FormioHooksService,
24
+ FormioAuthService,
25
+ AlterActions,
26
+ AlterHost,
27
+ AlterAudit,
28
+ AlterLog,
29
+ AlterSkip,
30
+ AlterTemplateImportSteps,
31
+ AlterTemplateExportSteps
32
+ ]
33
+ })
34
+ export class FormioModule implements OnRoutesInit, OnReady {
35
+ @Inject()
36
+ protected formio: FormioService;
37
+
38
+ @Inject()
39
+ protected hooks: FormioHooksService;
40
+
41
+ @Inject()
42
+ protected installer: FormioInstaller;
43
+
44
+ @Inject()
45
+ protected app: PlatformApplication;
46
+
47
+ @Inject()
48
+ protected injector: InjectorService;
49
+
50
+ @Constant("formio", {})
51
+ protected settings: FormioConfig;
52
+
53
+ @Constant("formio.baseUrl", "/")
54
+ protected baseUrl: string;
55
+
56
+ @Constant("formio.skipInstall", false)
57
+ protected skipInstall: boolean;
58
+
59
+ @Constant("formio.template")
60
+ protected template?: FormioTemplate;
61
+
62
+ @Constant("formio.root")
63
+ protected root?: any;
64
+
65
+ $onInit() {
66
+ return this.init(deepClone(this.settings));
67
+ }
68
+
69
+ init(options: FormioConfig) {
70
+ return this.formio.init(options, this.hooks.getHooks());
71
+ }
72
+
73
+ async $onRoutesInit() {
74
+ if (this.formio.isInit()) {
75
+ this.app.use(this.baseUrl, this.formio.middleware.restrictRequestTypes, this.formio.router);
76
+
77
+ if (await this.shouldInstall()) {
78
+ await this.installer.install(this.template!, this.root);
79
+ }
80
+ }
81
+ }
82
+
83
+ async $logRoutes(routes: PlatformRouteDetails[]): Promise<PlatformRouteDetails[]> {
84
+ if (this.formio.isInit()) {
85
+ const spec = (await this.formio.swagger(
86
+ {
87
+ $ctx: {
88
+ request: {
89
+ protocol: "http",
90
+ host: "localhost"
91
+ }
92
+ }
93
+ },
94
+ this.formio.router
95
+ )) as any;
96
+ const {baseUrl} = this;
97
+
98
+ Object.entries(spec.paths).forEach(([path, methods]: [string, any]) => {
99
+ Object.entries(methods).forEach(([method, operation]: [string, any]) => {
100
+ routes.push({
101
+ method,
102
+ name: operation.operationId,
103
+ url: normalizePath(baseUrl, path.replace(/\/{(.*)}/gi, "/:$1")),
104
+ className: "formio",
105
+ methodClassName: operation.operationId
106
+ });
107
+ });
108
+ });
109
+ }
110
+
111
+ return routes;
112
+ }
113
+
114
+ // istanbul ignore next
115
+ $onReady() {
116
+ if (this.formio.isInit() && "getBestHost" in this.injector.settings) {
117
+ const {injector} = this;
118
+ // @ts-ignore
119
+ const host = injector.settings.getBestHost();
120
+ const url = host.toString();
121
+
122
+ injector.logger.info(`Form.io API is available on ${url}${this.baseUrl || "/"}`);
123
+ }
124
+ }
125
+
126
+ protected async shouldInstall() {
127
+ const hasForms = await this.installer.hasForms();
128
+ return this.template && !(hasForms || this.skipInstall);
129
+ }
130
+ }
@@ -0,0 +1,93 @@
1
+ import {toMap as tMap} from "@tsed/core";
2
+ import {MongooseDocument} from "@tsed/mongoose";
3
+
4
+ import {FormioMapper} from "./FormioMapper.js";
5
+
6
+ function toMap<T>(list: any[]) {
7
+ return tMap<string, MongooseDocument<T>>(list, (o: any) => [o._id.toString(), `$machineName:${o.machineName}`]);
8
+ }
9
+
10
+ describe("FormioMapper", () => {
11
+ it("should map data to import (obj)", () => {
12
+ const mapper = new FormioMapper({
13
+ forms: toMap([{_id: "form_id", machineName: "form_machine"}]),
14
+ actions: toMap([{_id: "action_id", machineName: "action_machine"}]),
15
+ roles: toMap([{_id: "role_id", machineName: "role_machine"}])
16
+ });
17
+ const data = {
18
+ form: "form_machine",
19
+ value: 0,
20
+ notAnId: "id",
21
+ date: new Date()
22
+ };
23
+
24
+ const result = mapper.mapToImport(data);
25
+
26
+ expect(result).toEqual({
27
+ ...data,
28
+ form: "form_id"
29
+ });
30
+ });
31
+ it("should map data to import (array)", () => {
32
+ const mapper = new FormioMapper({
33
+ forms: toMap([{_id: "form_id", machineName: "form_machine"}]),
34
+ actions: toMap([{_id: "action_id", machineName: "action_machine"}]),
35
+ roles: toMap([{_id: "role_id", machineName: "role_machine"}])
36
+ });
37
+ const data = {
38
+ form: "form_machine",
39
+ value: 0,
40
+ notAnId: "id",
41
+ date: new Date()
42
+ };
43
+ const result = mapper.mapToImport([data]);
44
+
45
+ expect(result).toEqual([
46
+ {
47
+ ...data,
48
+ form: "form_id"
49
+ }
50
+ ]);
51
+ });
52
+ it("should map data to export (obj)", () => {
53
+ const mapper = new FormioMapper({
54
+ forms: toMap([{_id: "form_id", machineName: "form_machine"}]),
55
+ actions: toMap([{_id: "action_id", machineName: "action_machine"}]),
56
+ roles: toMap([{_id: "role_id", machineName: "role_machine"}])
57
+ });
58
+ const data = {
59
+ form: "form_id",
60
+ value: 0,
61
+ notAnId: "id",
62
+ date: new Date()
63
+ };
64
+
65
+ const result = mapper.mapToExport(data);
66
+
67
+ expect(result).toEqual({
68
+ ...data,
69
+ form: "$machineName:form_machine"
70
+ });
71
+ });
72
+ it("should map data to export (array)", () => {
73
+ const mapper = new FormioMapper({
74
+ forms: toMap([{_id: "form_id", machineName: "form_machine"}]),
75
+ actions: toMap([{_id: "action_id", machineName: "action_machine"}]),
76
+ roles: toMap([{_id: "role_id", machineName: "role_machine"}])
77
+ });
78
+ const data = {
79
+ form: "form_id",
80
+ value: 0,
81
+ notAnId: "id",
82
+ date: new Date()
83
+ };
84
+ const result = mapper.mapToExport([data]);
85
+
86
+ expect(result).toEqual([
87
+ {
88
+ ...data,
89
+ form: "$machineName:form_machine"
90
+ }
91
+ ]);
92
+ });
93
+ });
@@ -0,0 +1,71 @@
1
+ import {FormioCtxMapper} from "../domain/FormioCtxMapper.js";
2
+
3
+ export class FormioMapper {
4
+ constructor(readonly ctxData: FormioCtxMapper) {}
5
+
6
+ find(key: string) {
7
+ const store = Object.values(this.ctxData).find((store) => store.get(key));
8
+
9
+ return store?.get(key);
10
+ }
11
+
12
+ findId(data: string) {
13
+ const key = String(data).startsWith("$machineName:") ? String(data) : `$machineName:${String(data)}`;
14
+ const item = this.find(key);
15
+
16
+ return item ? item._id : undefined;
17
+ }
18
+
19
+ findMachineName(data: any) {
20
+ const key = String(data);
21
+ const item: any = this.find(key);
22
+
23
+ return item ? `$machineName:${item.name || item.machineName}` : undefined;
24
+ }
25
+
26
+ mapToExport(data: any) {
27
+ return this.mapData(data, this.findMachineName.bind(this));
28
+ }
29
+
30
+ mapToImport(data: any) {
31
+ return this.mapData(data, this.findId.bind(this));
32
+ }
33
+
34
+ mapData(data: any, resolver: any): any {
35
+ if (!data) {
36
+ return data;
37
+ }
38
+
39
+ if (Array.isArray(data)) {
40
+ return data.map((item) => {
41
+ return this.mapData(item, resolver);
42
+ });
43
+ }
44
+
45
+ const key = resolver(data, this.ctxData);
46
+
47
+ if (key) {
48
+ return key;
49
+ }
50
+
51
+ if (typeof data === "object" && !("_bsontype" in data) && !(data instanceof Date)) {
52
+ const isForm = data.path && data.type;
53
+ const mapped = Object.entries(data).reduce((obj, [key, value]) => {
54
+ return {
55
+ ...obj,
56
+ [key]: this.mapData(value, resolver)
57
+ };
58
+ }, {});
59
+
60
+ return isForm
61
+ ? {
62
+ ...mapped,
63
+ name: data.name,
64
+ machineName: data.machineName || data.name
65
+ }
66
+ : mapped;
67
+ }
68
+
69
+ return data;
70
+ }
71
+ }