@flusys/nestjs-form-builder 6.4.1 → 7.0.0-beta.1
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.
- package/cjs/config/event-actions.js +29 -0
- package/cjs/config/index.js +1 -0
- package/cjs/modules/form-builder.module.js +1 -0
- package/cjs/services/form-result.service.js +17 -0
- package/cjs/services/form-result.service.spec.js +75 -0
- package/config/event-actions.d.ts +8 -0
- package/config/index.d.ts +1 -0
- package/fesm/config/event-actions.js +11 -0
- package/fesm/config/index.js +1 -0
- package/fesm/modules/form-builder.module.js +2 -1
- package/fesm/services/form-result.service.js +18 -1
- package/fesm/services/form-result.service.spec.js +75 -0
- package/interfaces/form-builder-module.interface.d.ts +2 -0
- package/package.json +3 -3
- package/services/form-result.service.d.ts +1 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain event actions published by the form builder module, alongside the
|
|
3
|
+
* CRUD lifecycle actions every ApiService emits.
|
|
4
|
+
*/ "use strict";
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
function _export(target, all) {
|
|
9
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
_export(exports, {
|
|
15
|
+
get FORM_BUILDER_EVENT_ACTIONS () {
|
|
16
|
+
return FORM_BUILDER_EVENT_ACTIONS;
|
|
17
|
+
},
|
|
18
|
+
get FORM_BUILDER_EVENT_ENTITIES () {
|
|
19
|
+
return FORM_BUILDER_EVENT_ENTITIES;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
const FORM_BUILDER_EVENT_ACTIONS = {
|
|
23
|
+
SUBMITTED: 'submitted',
|
|
24
|
+
DRAFT_SAVED: 'draft-saved'
|
|
25
|
+
};
|
|
26
|
+
const FORM_BUILDER_EVENT_ENTITIES = {
|
|
27
|
+
FORM: 'form',
|
|
28
|
+
FORM_RESULT: 'form_result'
|
|
29
|
+
};
|
package/cjs/config/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
+
_export_star(require("./event-actions"), exports);
|
|
5
6
|
_export_star(require("./form-builder.constants"), exports);
|
|
6
7
|
_export_star(require("./message-keys"), exports);
|
|
7
8
|
function _export_star(from, to) {
|
|
@@ -80,6 +80,7 @@ let FormBuilderModule = class FormBuilderModule {
|
|
|
80
80
|
useValue: options
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
|
+
providers.push((0, _modules.createModuleEventsProvider)('form-builder', _formbuilderconstants.FORM_BUILDER_MODULE_OPTIONS));
|
|
83
84
|
return providers;
|
|
84
85
|
}
|
|
85
86
|
static buildAsyncProviders(options) {
|
|
@@ -207,6 +207,7 @@ let FormResultService = class FormResultService extends _classes.ApiService {
|
|
|
207
207
|
await this.invalidateResultCache([
|
|
208
208
|
saved
|
|
209
209
|
]);
|
|
210
|
+
await this.publishSubmissionEvent(saved, dto.formId, true, user);
|
|
210
211
|
return this.convertEntityToResponseDto(saved, false);
|
|
211
212
|
}
|
|
212
213
|
await this.repository.softRemove(existingDraft);
|
|
@@ -226,6 +227,7 @@ let FormResultService = class FormResultService extends _classes.ApiService {
|
|
|
226
227
|
...staleResults,
|
|
227
228
|
saved
|
|
228
229
|
]);
|
|
230
|
+
await this.publishSubmissionEvent(saved, dto.formId, isDraft, user);
|
|
229
231
|
return this.convertEntityToResponseDto(saved, false);
|
|
230
232
|
}
|
|
231
233
|
async getMyDraft(formId, user) {
|
|
@@ -266,8 +268,23 @@ let FormResultService = class FormResultService extends _classes.ApiService {
|
|
|
266
268
|
await this.invalidateResultCache([
|
|
267
269
|
saved
|
|
268
270
|
]);
|
|
271
|
+
await this.publishSubmissionEvent(saved, dto.formId, isDraft, user);
|
|
269
272
|
return this.convertEntityToResponseDto(saved, false);
|
|
270
273
|
}
|
|
274
|
+
async publishSubmissionEvent(result, formId, isDraft, user) {
|
|
275
|
+
await this.publishDomainAction(isDraft ? _config.FORM_BUILDER_EVENT_ACTIONS.DRAFT_SAVED : _config.FORM_BUILDER_EVENT_ACTIONS.SUBMITTED, {
|
|
276
|
+
ids: [
|
|
277
|
+
result.id
|
|
278
|
+
],
|
|
279
|
+
data: result,
|
|
280
|
+
metadata: {
|
|
281
|
+
formId,
|
|
282
|
+
isDraft,
|
|
283
|
+
schemaVersion: result.schemaVersion
|
|
284
|
+
},
|
|
285
|
+
user
|
|
286
|
+
});
|
|
287
|
+
}
|
|
271
288
|
async invalidateResultCache(results) {
|
|
272
289
|
await Promise.all([
|
|
273
290
|
this.clearCacheForAll(),
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
5
|
const _testing = require("@nestjs/testing");
|
|
6
|
+
const _classes = require("@flusys/nestjs-shared/classes");
|
|
6
7
|
const _modules = require("@flusys/nestjs-shared/modules");
|
|
7
8
|
const _repositorymock = require("@test-utils/mocks/repository.mock");
|
|
8
9
|
const _loggedusermock = require("@test-utils/mocks/logged-user.mock");
|
|
@@ -745,4 +746,78 @@ describe('FormResultService', ()=>{
|
|
|
745
746
|
expect(result).toBe(false);
|
|
746
747
|
});
|
|
747
748
|
});
|
|
749
|
+
describe('domain events', ()=>{
|
|
750
|
+
let publish;
|
|
751
|
+
beforeEach(()=>{
|
|
752
|
+
publish = jest.fn().mockResolvedValue(undefined);
|
|
753
|
+
_classes.EventBusRegistry.reset();
|
|
754
|
+
_classes.EventBusRegistry.setBus({
|
|
755
|
+
publish,
|
|
756
|
+
subscribe: jest.fn(),
|
|
757
|
+
close: jest.fn()
|
|
758
|
+
});
|
|
759
|
+
_classes.EventBusRegistry.configureModule('form-builder', {
|
|
760
|
+
enabled: true
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
afterEach(()=>{
|
|
764
|
+
_classes.EventBusRegistry.reset();
|
|
765
|
+
});
|
|
766
|
+
it('publishes form-builder.form_result.submitted for a final submission', async ()=>{
|
|
767
|
+
await buildService(false);
|
|
768
|
+
mockFormRepo.findOne.mockResolvedValue(buildForm({
|
|
769
|
+
accessType: _formaccesstypeenum.FormAccessType.PUBLIC
|
|
770
|
+
}));
|
|
771
|
+
mockResultRepo.save.mockResolvedValue(buildFormResult({
|
|
772
|
+
isDraft: false
|
|
773
|
+
}));
|
|
774
|
+
await service.submitForm({
|
|
775
|
+
formId: 'form-uuid-1',
|
|
776
|
+
data: {}
|
|
777
|
+
}, null, true);
|
|
778
|
+
expect(publish).toHaveBeenCalledWith(expect.objectContaining({
|
|
779
|
+
name: 'form-builder.form_result.submitted',
|
|
780
|
+
action: 'submitted',
|
|
781
|
+
payload: expect.objectContaining({
|
|
782
|
+
ids: [
|
|
783
|
+
'result-uuid-1'
|
|
784
|
+
],
|
|
785
|
+
metadata: expect.objectContaining({
|
|
786
|
+
formId: 'form-uuid-1',
|
|
787
|
+
isDraft: false
|
|
788
|
+
})
|
|
789
|
+
})
|
|
790
|
+
}));
|
|
791
|
+
});
|
|
792
|
+
it('publishes form-builder.form_result.draft-saved when the submission is a draft', async ()=>{
|
|
793
|
+
await buildService(false);
|
|
794
|
+
mockFormRepo.findOne.mockResolvedValue(buildForm({
|
|
795
|
+
accessType: _formaccesstypeenum.FormAccessType.PUBLIC
|
|
796
|
+
}));
|
|
797
|
+
mockResultRepo.save.mockResolvedValue(buildFormResult({
|
|
798
|
+
isDraft: true
|
|
799
|
+
}));
|
|
800
|
+
await service.submitForm({
|
|
801
|
+
formId: 'form-uuid-1',
|
|
802
|
+
data: {},
|
|
803
|
+
isDraft: true
|
|
804
|
+
}, null, true);
|
|
805
|
+
expect(publish.mock.calls[0][0].name).toBe('form-builder.form_result.draft-saved');
|
|
806
|
+
});
|
|
807
|
+
it('does not publish when the module has events disabled', async ()=>{
|
|
808
|
+
_classes.EventBusRegistry.configureModule('form-builder', {
|
|
809
|
+
enabled: false
|
|
810
|
+
});
|
|
811
|
+
await buildService(false);
|
|
812
|
+
mockFormRepo.findOne.mockResolvedValue(buildForm({
|
|
813
|
+
accessType: _formaccesstypeenum.FormAccessType.PUBLIC
|
|
814
|
+
}));
|
|
815
|
+
mockResultRepo.save.mockResolvedValue(buildFormResult());
|
|
816
|
+
await service.submitForm({
|
|
817
|
+
formId: 'form-uuid-1',
|
|
818
|
+
data: {}
|
|
819
|
+
}, null, true);
|
|
820
|
+
expect(publish).not.toHaveBeenCalled();
|
|
821
|
+
});
|
|
822
|
+
});
|
|
748
823
|
});
|
package/config/index.d.ts
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain event actions published by the form builder module, alongside the
|
|
3
|
+
* CRUD lifecycle actions every ApiService emits.
|
|
4
|
+
*/ export const FORM_BUILDER_EVENT_ACTIONS = {
|
|
5
|
+
SUBMITTED: 'submitted',
|
|
6
|
+
DRAFT_SAVED: 'draft-saved'
|
|
7
|
+
};
|
|
8
|
+
/** Every entity the form builder module publishes under */ export const FORM_BUILDER_EVENT_ENTITIES = {
|
|
9
|
+
FORM: 'form',
|
|
10
|
+
FORM_RESULT: 'form_result'
|
|
11
|
+
};
|
package/fesm/config/index.js
CHANGED
|
@@ -17,7 +17,7 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
17
17
|
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
18
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
19
|
}
|
|
20
|
-
import { CacheModule, UtilsModule } from '@flusys/nestjs-shared/modules';
|
|
20
|
+
import { CacheModule, UtilsModule, createModuleEventsProvider } from '@flusys/nestjs-shared/modules';
|
|
21
21
|
import { Module } from '@nestjs/common';
|
|
22
22
|
import { FORM_BUILDER_MODULE_OPTIONS } from '../config/form-builder.constants';
|
|
23
23
|
import { FormController, FormResultController } from '../controllers';
|
|
@@ -70,6 +70,7 @@ export class FormBuilderModule {
|
|
|
70
70
|
useValue: options
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
|
+
providers.push(createModuleEventsProvider('form-builder', FORM_BUILDER_MODULE_OPTIONS));
|
|
73
74
|
return providers;
|
|
74
75
|
}
|
|
75
76
|
static buildAsyncProviders(options) {
|
|
@@ -31,7 +31,7 @@ import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
|
31
31
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
32
32
|
import { BadRequestException, ForbiddenException, Inject, Injectable, NotFoundException, Scope, UnauthorizedException } from '@nestjs/common';
|
|
33
33
|
import { IsNull } from 'typeorm';
|
|
34
|
-
import { FORM_MESSAGES, FORM_RESULT_MESSAGES } from '../config';
|
|
34
|
+
import { FORM_BUILDER_EVENT_ACTIONS, FORM_MESSAGES, FORM_RESULT_MESSAGES } from '../config';
|
|
35
35
|
import { SubmitFormDto } from '../dtos/form-result.dto';
|
|
36
36
|
import { Form, FormResult, FormWithCompany } from '../entities';
|
|
37
37
|
import { FormAccessType } from '../enums/form-access-type.enum';
|
|
@@ -197,6 +197,7 @@ export class FormResultService extends ApiService {
|
|
|
197
197
|
await this.invalidateResultCache([
|
|
198
198
|
saved
|
|
199
199
|
]);
|
|
200
|
+
await this.publishSubmissionEvent(saved, dto.formId, true, user);
|
|
200
201
|
return this.convertEntityToResponseDto(saved, false);
|
|
201
202
|
}
|
|
202
203
|
await this.repository.softRemove(existingDraft);
|
|
@@ -216,6 +217,7 @@ export class FormResultService extends ApiService {
|
|
|
216
217
|
...staleResults,
|
|
217
218
|
saved
|
|
218
219
|
]);
|
|
220
|
+
await this.publishSubmissionEvent(saved, dto.formId, isDraft, user);
|
|
219
221
|
return this.convertEntityToResponseDto(saved, false);
|
|
220
222
|
}
|
|
221
223
|
async getMyDraft(formId, user) {
|
|
@@ -256,8 +258,23 @@ export class FormResultService extends ApiService {
|
|
|
256
258
|
await this.invalidateResultCache([
|
|
257
259
|
saved
|
|
258
260
|
]);
|
|
261
|
+
await this.publishSubmissionEvent(saved, dto.formId, isDraft, user);
|
|
259
262
|
return this.convertEntityToResponseDto(saved, false);
|
|
260
263
|
}
|
|
264
|
+
async publishSubmissionEvent(result, formId, isDraft, user) {
|
|
265
|
+
await this.publishDomainAction(isDraft ? FORM_BUILDER_EVENT_ACTIONS.DRAFT_SAVED : FORM_BUILDER_EVENT_ACTIONS.SUBMITTED, {
|
|
266
|
+
ids: [
|
|
267
|
+
result.id
|
|
268
|
+
],
|
|
269
|
+
data: result,
|
|
270
|
+
metadata: {
|
|
271
|
+
formId,
|
|
272
|
+
isDraft,
|
|
273
|
+
schemaVersion: result.schemaVersion
|
|
274
|
+
},
|
|
275
|
+
user
|
|
276
|
+
});
|
|
277
|
+
}
|
|
261
278
|
async invalidateResultCache(results) {
|
|
262
279
|
await Promise.all([
|
|
263
280
|
this.clearCacheForAll(),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Test } from '@nestjs/testing';
|
|
2
|
+
import { EventBusRegistry } from '@flusys/nestjs-shared/classes';
|
|
2
3
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
3
4
|
import { createMockQueryBuilder, createMockRepository } from '@test-utils/mocks/repository.mock';
|
|
4
5
|
import { buildMockUser } from '@test-utils/mocks/logged-user.mock';
|
|
@@ -700,4 +701,78 @@ describe('FormResultService', ()=>{
|
|
|
700
701
|
expect(result).toBe(false);
|
|
701
702
|
});
|
|
702
703
|
});
|
|
704
|
+
describe('domain events', ()=>{
|
|
705
|
+
let publish;
|
|
706
|
+
beforeEach(()=>{
|
|
707
|
+
publish = jest.fn().mockResolvedValue(undefined);
|
|
708
|
+
EventBusRegistry.reset();
|
|
709
|
+
EventBusRegistry.setBus({
|
|
710
|
+
publish,
|
|
711
|
+
subscribe: jest.fn(),
|
|
712
|
+
close: jest.fn()
|
|
713
|
+
});
|
|
714
|
+
EventBusRegistry.configureModule('form-builder', {
|
|
715
|
+
enabled: true
|
|
716
|
+
});
|
|
717
|
+
});
|
|
718
|
+
afterEach(()=>{
|
|
719
|
+
EventBusRegistry.reset();
|
|
720
|
+
});
|
|
721
|
+
it('publishes form-builder.form_result.submitted for a final submission', async ()=>{
|
|
722
|
+
await buildService(false);
|
|
723
|
+
mockFormRepo.findOne.mockResolvedValue(buildForm({
|
|
724
|
+
accessType: FormAccessType.PUBLIC
|
|
725
|
+
}));
|
|
726
|
+
mockResultRepo.save.mockResolvedValue(buildFormResult({
|
|
727
|
+
isDraft: false
|
|
728
|
+
}));
|
|
729
|
+
await service.submitForm({
|
|
730
|
+
formId: 'form-uuid-1',
|
|
731
|
+
data: {}
|
|
732
|
+
}, null, true);
|
|
733
|
+
expect(publish).toHaveBeenCalledWith(expect.objectContaining({
|
|
734
|
+
name: 'form-builder.form_result.submitted',
|
|
735
|
+
action: 'submitted',
|
|
736
|
+
payload: expect.objectContaining({
|
|
737
|
+
ids: [
|
|
738
|
+
'result-uuid-1'
|
|
739
|
+
],
|
|
740
|
+
metadata: expect.objectContaining({
|
|
741
|
+
formId: 'form-uuid-1',
|
|
742
|
+
isDraft: false
|
|
743
|
+
})
|
|
744
|
+
})
|
|
745
|
+
}));
|
|
746
|
+
});
|
|
747
|
+
it('publishes form-builder.form_result.draft-saved when the submission is a draft', async ()=>{
|
|
748
|
+
await buildService(false);
|
|
749
|
+
mockFormRepo.findOne.mockResolvedValue(buildForm({
|
|
750
|
+
accessType: FormAccessType.PUBLIC
|
|
751
|
+
}));
|
|
752
|
+
mockResultRepo.save.mockResolvedValue(buildFormResult({
|
|
753
|
+
isDraft: true
|
|
754
|
+
}));
|
|
755
|
+
await service.submitForm({
|
|
756
|
+
formId: 'form-uuid-1',
|
|
757
|
+
data: {},
|
|
758
|
+
isDraft: true
|
|
759
|
+
}, null, true);
|
|
760
|
+
expect(publish.mock.calls[0][0].name).toBe('form-builder.form_result.draft-saved');
|
|
761
|
+
});
|
|
762
|
+
it('does not publish when the module has events disabled', async ()=>{
|
|
763
|
+
EventBusRegistry.configureModule('form-builder', {
|
|
764
|
+
enabled: false
|
|
765
|
+
});
|
|
766
|
+
await buildService(false);
|
|
767
|
+
mockFormRepo.findOne.mockResolvedValue(buildForm({
|
|
768
|
+
accessType: FormAccessType.PUBLIC
|
|
769
|
+
}));
|
|
770
|
+
mockResultRepo.save.mockResolvedValue(buildFormResult());
|
|
771
|
+
await service.submitForm({
|
|
772
|
+
formId: 'form-uuid-1',
|
|
773
|
+
data: {}
|
|
774
|
+
}, null, true);
|
|
775
|
+
expect(publish).not.toHaveBeenCalled();
|
|
776
|
+
});
|
|
777
|
+
});
|
|
703
778
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ModuleMetadata, Type } from '@nestjs/common';
|
|
2
2
|
import { IBootstrapAppConfig, IDataSourceServiceOptions, IDynamicModuleConfig, IModuleOptionsFactory } from '@flusys/nestjs-core';
|
|
3
|
+
import { IModuleEventsConfig } from '@flusys/nestjs-shared/interfaces';
|
|
3
4
|
export interface IFormBuilderConfig extends IDataSourceServiceOptions {
|
|
5
|
+
events?: IModuleEventsConfig;
|
|
4
6
|
}
|
|
5
7
|
export interface FormBuilderModuleOptions extends IDynamicModuleConfig {
|
|
6
8
|
bootstrapAppConfig?: IBootstrapAppConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flusys/nestjs-form-builder",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0-beta.1",
|
|
4
4
|
"description": "Dynamic form builder module with schema versioning and access control",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "fesm/index.js",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"express": "^5.0.0"
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"@flusys/nestjs-core": "
|
|
88
|
-
"@flusys/nestjs-shared": "
|
|
87
|
+
"@flusys/nestjs-core": "7.0.0-beta.1",
|
|
88
|
+
"@flusys/nestjs-shared": "7.0.0-beta.1"
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -33,6 +33,7 @@ export declare class FormResultService extends ApiService<CreateFormResultDto, U
|
|
|
33
33
|
submitForm(dto: SubmitFormDto, user: ILoggedUserInfo | null, isPublic?: boolean): Promise<IFormResult>;
|
|
34
34
|
getMyDraft(formId: string, user: ILoggedUserInfo): Promise<IFormResult | null>;
|
|
35
35
|
updateDraft(draftId: string, dto: SubmitFormDto, user: ILoggedUserInfo): Promise<IFormResult>;
|
|
36
|
+
private publishSubmissionEvent;
|
|
36
37
|
private invalidateResultCache;
|
|
37
38
|
getByFormId(formId: string, user: ILoggedUserInfo | null, pagination?: {
|
|
38
39
|
page?: number;
|