@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.
@@ -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
+ };
@@ -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
  });
@@ -0,0 +1,8 @@
1
+ export declare const FORM_BUILDER_EVENT_ACTIONS: {
2
+ readonly SUBMITTED: "submitted";
3
+ readonly DRAFT_SAVED: "draft-saved";
4
+ };
5
+ export declare const FORM_BUILDER_EVENT_ENTITIES: {
6
+ readonly FORM: "form";
7
+ readonly FORM_RESULT: "form_result";
8
+ };
package/config/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
+ export * from './event-actions';
1
2
  export * from './form-builder.constants';
2
3
  export * from './message-keys';
@@ -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
+ };
@@ -1,2 +1,3 @@
1
+ export * from './event-actions';
1
2
  export * from './form-builder.constants';
2
3
  export * from './message-keys';
@@ -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": "6.4.1",
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": "6.4.1",
88
- "@flusys/nestjs-shared": "6.4.1"
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;