@flusys/nestjs-form-builder 7.1.0 → 8.0.0
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/config/index.d.ts +3 -3
- package/controllers/form-result.controller.d.ts +3 -3
- package/controllers/form.controller.d.ts +3 -3
- package/controllers/index.d.ts +2 -2
- package/docs/form-builder-swagger.config.d.ts +2 -2
- package/docs/index.d.ts +1 -1
- package/dtos/form.dto.d.ts +1 -1
- package/dtos/index.d.ts +2 -2
- package/entities/form-with-company.entity.d.ts +1 -1
- package/entities/form.entity.d.ts +1 -1
- package/entities/index.d.ts +7 -7
- package/enums/index.d.ts +1 -1
- package/fesm/262.js +1841 -0
- package/fesm/98.js +807 -0
- package/fesm/config/index.js +98 -3
- package/fesm/controllers/index.js +14 -2
- package/fesm/docs/index.js +171 -1
- package/fesm/dtos/index.js +571 -2
- package/fesm/entities/index.js +342 -10
- package/fesm/enums/index.js +38 -1
- package/fesm/index.js +482 -10
- package/fesm/interfaces/index.js +17 -3
- package/fesm/modules/index.js +178 -1
- package/fesm/runtime.js +85 -0
- package/fesm/services/index.js +35 -4
- package/index.d.ts +10 -10
- package/interfaces/form-builder-module.interface.d.ts +3 -3
- package/interfaces/form-result.interface.d.ts +1 -1
- package/interfaces/form.interface.d.ts +2 -2
- package/interfaces/index.d.ts +3 -3
- package/modules/form-builder.module.d.ts +2 -2
- package/modules/index.d.ts +1 -1
- package/package.json +26 -36
- package/services/form-builder-config.service.d.ts +4 -4
- package/services/form-builder-datasource.provider.d.ts +3 -3
- package/services/form-result.service.d.ts +6 -6
- package/services/form.service.d.ts +7 -7
- package/services/index.d.ts +4 -4
- package/utils/index.d.ts +2 -2
- package/utils/permission.utils.d.ts +1 -1
- package/cjs/config/event-actions.js +0 -33
- package/cjs/config/form-builder.constants.js +0 -13
- package/cjs/config/index.js +0 -20
- package/cjs/config/message-keys.js +0 -38
- package/cjs/controllers/form-result.controller.js +0 -281
- package/cjs/controllers/form-result.controller.spec.js +0 -257
- package/cjs/controllers/form.controller.js +0 -231
- package/cjs/controllers/form.controller.spec.js +0 -212
- package/cjs/controllers/index.js +0 -19
- package/cjs/docs/form-builder-swagger.config.js +0 -150
- package/cjs/docs/form-builder-swagger.config.spec.js +0 -75
- package/cjs/docs/index.js +0 -11
- package/cjs/dtos/form-result.dto.js +0 -248
- package/cjs/dtos/form.dto.js +0 -270
- package/cjs/dtos/index.js +0 -19
- package/cjs/entities/form-result.entity.js +0 -100
- package/cjs/entities/form-with-company.entity.js +0 -65
- package/cjs/entities/form.entity.js +0 -127
- package/cjs/entities/index.js +0 -52
- package/cjs/entities/index.spec.js +0 -18
- package/cjs/enums/form-access-type.enum.js +0 -19
- package/cjs/enums/index.js +0 -18
- package/cjs/index.js +0 -28
- package/cjs/interfaces/form-builder-module.interface.js +0 -4
- package/cjs/interfaces/form-result.interface.js +0 -4
- package/cjs/interfaces/form.interface.js +0 -4
- package/cjs/interfaces/index.js +0 -20
- package/cjs/modules/form-builder.module.js +0 -136
- package/cjs/modules/form-builder.module.spec.js +0 -133
- package/cjs/modules/index.js +0 -18
- package/cjs/services/form-builder-config.service.js +0 -69
- package/cjs/services/form-builder-config.service.spec.js +0 -121
- package/cjs/services/form-builder-datasource.provider.js +0 -125
- package/cjs/services/form-builder-datasource.provider.spec.js +0 -147
- package/cjs/services/form-result.service.js +0 -366
- package/cjs/services/form-result.service.spec.js +0 -823
- package/cjs/services/form.service.js +0 -341
- package/cjs/services/form.service.spec.js +0 -746
- package/cjs/services/index.js +0 -21
- package/cjs/utils/computed-field.utils.js +0 -199
- package/cjs/utils/computed-field.utils.spec.js +0 -1205
- package/cjs/utils/index.js +0 -19
- package/cjs/utils/permission.utils.js +0 -44
- package/cjs/utils/permission.utils.spec.js +0 -118
- package/fesm/config/event-actions.js +0 -12
- package/fesm/config/form-builder.constants.js +0 -3
- package/fesm/config/message-keys.js +0 -20
- package/fesm/controllers/form-result.controller.js +0 -271
- package/fesm/controllers/form-result.controller.spec.js +0 -253
- package/fesm/controllers/form.controller.js +0 -221
- package/fesm/controllers/form.controller.spec.js +0 -208
- package/fesm/docs/form-builder-swagger.config.js +0 -146
- package/fesm/docs/form-builder-swagger.config.spec.js +0 -71
- package/fesm/dtos/form-result.dto.js +0 -215
- package/fesm/dtos/form.dto.js +0 -243
- package/fesm/entities/form-result.entity.js +0 -90
- package/fesm/entities/form-with-company.entity.js +0 -55
- package/fesm/entities/form.entity.js +0 -117
- package/fesm/entities/index.spec.js +0 -14
- package/fesm/enums/form-access-type.enum.js +0 -9
- package/fesm/interfaces/form-builder-module.interface.js +0 -3
- package/fesm/interfaces/form-result.interface.js +0 -6
- package/fesm/interfaces/form.interface.js +0 -3
- package/fesm/modules/form-builder.module.js +0 -126
- package/fesm/modules/form-builder.module.spec.js +0 -129
- package/fesm/services/form-builder-config.service.js +0 -59
- package/fesm/services/form-builder-config.service.spec.js +0 -117
- package/fesm/services/form-builder-datasource.provider.js +0 -74
- package/fesm/services/form-builder-datasource.provider.spec.js +0 -143
- package/fesm/services/form-result.service.js +0 -356
- package/fesm/services/form-result.service.spec.js +0 -778
- package/fesm/services/form.service.js +0 -331
- package/fesm/services/form.service.spec.js +0 -701
- package/fesm/utils/computed-field.utils.js +0 -195
- package/fesm/utils/computed-field.utils.spec.js +0 -1201
- package/fesm/utils/index.js +0 -2
- package/fesm/utils/permission.utils.js +0 -42
- package/fesm/utils/permission.utils.spec.js +0 -114
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
import { Test } from '@nestjs/testing';
|
|
2
|
-
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
3
|
-
import { buildMockUser } from '@test-utils/mocks/logged-user.mock';
|
|
4
|
-
import { FormResultController } from './form-result.controller';
|
|
5
|
-
import { FormResultService } from '../services/form-result.service';
|
|
6
|
-
function buildResultDto(overrides = {}) {
|
|
7
|
-
return {
|
|
8
|
-
id: 'result-1',
|
|
9
|
-
formId: 'form-1',
|
|
10
|
-
schemaVersionSnapshot: {
|
|
11
|
-
sections: []
|
|
12
|
-
},
|
|
13
|
-
schemaVersion: 1,
|
|
14
|
-
data: {
|
|
15
|
-
field1: 'value1'
|
|
16
|
-
},
|
|
17
|
-
submittedById: 'user-uuid-1',
|
|
18
|
-
submittedAt: new Date(),
|
|
19
|
-
isDraft: false,
|
|
20
|
-
createdAt: new Date(),
|
|
21
|
-
updatedAt: new Date(),
|
|
22
|
-
deletedAt: null,
|
|
23
|
-
createdById: null,
|
|
24
|
-
updatedById: null,
|
|
25
|
-
deletedById: null,
|
|
26
|
-
...overrides
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
describe('FormResultController', ()=>{
|
|
30
|
-
let controller;
|
|
31
|
-
let mockService;
|
|
32
|
-
beforeEach(async ()=>{
|
|
33
|
-
mockService = {
|
|
34
|
-
insert: jest.fn(),
|
|
35
|
-
insertMany: jest.fn(),
|
|
36
|
-
findById: jest.fn(),
|
|
37
|
-
findByIds: jest.fn(),
|
|
38
|
-
getAll: jest.fn(),
|
|
39
|
-
update: jest.fn(),
|
|
40
|
-
updateMany: jest.fn(),
|
|
41
|
-
delete: jest.fn(),
|
|
42
|
-
submitForm: jest.fn(),
|
|
43
|
-
getMyDraft: jest.fn(),
|
|
44
|
-
updateDraft: jest.fn(),
|
|
45
|
-
getByFormId: jest.fn(),
|
|
46
|
-
hasUserSubmitted: jest.fn()
|
|
47
|
-
};
|
|
48
|
-
const module = await Test.createTestingModule({
|
|
49
|
-
controllers: [
|
|
50
|
-
FormResultController
|
|
51
|
-
],
|
|
52
|
-
// UtilsService is a real (dependency-free) provider — required to satisfy the
|
|
53
|
-
// Slug interceptor applied by the inherited insert/update endpoints.
|
|
54
|
-
providers: [
|
|
55
|
-
{
|
|
56
|
-
provide: FormResultService,
|
|
57
|
-
useValue: mockService
|
|
58
|
-
},
|
|
59
|
-
UtilsService
|
|
60
|
-
]
|
|
61
|
-
}).compile();
|
|
62
|
-
controller = module.get(FormResultController);
|
|
63
|
-
});
|
|
64
|
-
afterEach(()=>{
|
|
65
|
-
jest.clearAllMocks();
|
|
66
|
-
});
|
|
67
|
-
describe('submitForm', ()=>{
|
|
68
|
-
it('submits on behalf of the authenticated user (isPublic=false)', async ()=>{
|
|
69
|
-
const user = buildMockUser();
|
|
70
|
-
mockService.submitForm.mockResolvedValue(buildResultDto());
|
|
71
|
-
const result = await controller.submitForm({
|
|
72
|
-
formId: 'form-1',
|
|
73
|
-
data: {
|
|
74
|
-
field1: 'value1'
|
|
75
|
-
}
|
|
76
|
-
}, user);
|
|
77
|
-
expect(mockService.submitForm).toHaveBeenCalledWith({
|
|
78
|
-
formId: 'form-1',
|
|
79
|
-
data: {
|
|
80
|
-
field1: 'value1'
|
|
81
|
-
}
|
|
82
|
-
}, user, false);
|
|
83
|
-
expect(result).toEqual({
|
|
84
|
-
success: true,
|
|
85
|
-
message: 'Form submitted successfully',
|
|
86
|
-
messageKey: 'form.result.submit.success',
|
|
87
|
-
data: expect.objectContaining({
|
|
88
|
-
id: 'result-1'
|
|
89
|
-
})
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
it('propagates errors from the service (e.g. permission denied)', async ()=>{
|
|
93
|
-
mockService.submitForm.mockRejectedValue(Object.assign(new Error('denied'), {
|
|
94
|
-
messageKey: 'form.access.denied'
|
|
95
|
-
}));
|
|
96
|
-
await expect(controller.submitForm({
|
|
97
|
-
formId: 'form-1',
|
|
98
|
-
data: {}
|
|
99
|
-
}, buildMockUser())).rejects.toThrow();
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
describe('submitPublicForm', ()=>{
|
|
103
|
-
it('submits without a user and marks the submission as public', async ()=>{
|
|
104
|
-
mockService.submitForm.mockResolvedValue(buildResultDto({
|
|
105
|
-
submittedById: null
|
|
106
|
-
}));
|
|
107
|
-
const result = await controller.submitPublicForm({
|
|
108
|
-
formId: 'form-1',
|
|
109
|
-
data: {}
|
|
110
|
-
});
|
|
111
|
-
expect(mockService.submitForm).toHaveBeenCalledWith({
|
|
112
|
-
formId: 'form-1',
|
|
113
|
-
data: {}
|
|
114
|
-
}, null, true);
|
|
115
|
-
expect(result.data.submittedById).toBeNull();
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
describe('getMyDraft', ()=>{
|
|
119
|
-
it('returns the current draft for the user', async ()=>{
|
|
120
|
-
const user = buildMockUser();
|
|
121
|
-
mockService.getMyDraft.mockResolvedValue(buildResultDto({
|
|
122
|
-
isDraft: true
|
|
123
|
-
}));
|
|
124
|
-
const result = await controller.getMyDraft({
|
|
125
|
-
formId: 'form-1'
|
|
126
|
-
}, user);
|
|
127
|
-
expect(mockService.getMyDraft).toHaveBeenCalledWith('form-1', user);
|
|
128
|
-
expect(result.data?.isDraft).toBe(true);
|
|
129
|
-
});
|
|
130
|
-
it('returns null data when no draft exists', async ()=>{
|
|
131
|
-
mockService.getMyDraft.mockResolvedValue(null);
|
|
132
|
-
const result = await controller.getMyDraft({
|
|
133
|
-
formId: 'form-1'
|
|
134
|
-
}, buildMockUser());
|
|
135
|
-
expect(result.data).toBeNull();
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
describe('updateDraft', ()=>{
|
|
139
|
-
it('updates the identified draft', async ()=>{
|
|
140
|
-
const user = buildMockUser();
|
|
141
|
-
mockService.updateDraft.mockResolvedValue(buildResultDto({
|
|
142
|
-
isDraft: true
|
|
143
|
-
}));
|
|
144
|
-
const result = await controller.updateDraft({
|
|
145
|
-
draftId: 'draft-1',
|
|
146
|
-
formId: 'form-1',
|
|
147
|
-
data: {
|
|
148
|
-
field1: 'new'
|
|
149
|
-
}
|
|
150
|
-
}, user);
|
|
151
|
-
expect(mockService.updateDraft).toHaveBeenCalledWith('draft-1', {
|
|
152
|
-
draftId: 'draft-1',
|
|
153
|
-
formId: 'form-1',
|
|
154
|
-
data: {
|
|
155
|
-
field1: 'new'
|
|
156
|
-
}
|
|
157
|
-
}, user);
|
|
158
|
-
expect(result.success).toBe(true);
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
describe('getByFormId', ()=>{
|
|
162
|
-
it('returns paginated results with the requested page/pageSize', async ()=>{
|
|
163
|
-
mockService.getByFormId.mockResolvedValue({
|
|
164
|
-
data: [
|
|
165
|
-
buildResultDto()
|
|
166
|
-
],
|
|
167
|
-
total: 1
|
|
168
|
-
});
|
|
169
|
-
const result = await controller.getByFormId({
|
|
170
|
-
formId: 'form-1',
|
|
171
|
-
page: 1,
|
|
172
|
-
pageSize: 5
|
|
173
|
-
}, buildMockUser());
|
|
174
|
-
expect(mockService.getByFormId).toHaveBeenCalledWith('form-1', expect.any(Object), {
|
|
175
|
-
page: 1,
|
|
176
|
-
pageSize: 5
|
|
177
|
-
});
|
|
178
|
-
expect(result.meta).toEqual({
|
|
179
|
-
total: 1,
|
|
180
|
-
page: 1,
|
|
181
|
-
pageSize: 5,
|
|
182
|
-
count: 1
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
it('defaults to page 0 and pageSize 10 when not provided', async ()=>{
|
|
186
|
-
mockService.getByFormId.mockResolvedValue({
|
|
187
|
-
data: [],
|
|
188
|
-
total: 0
|
|
189
|
-
});
|
|
190
|
-
const result = await controller.getByFormId({
|
|
191
|
-
formId: 'form-1'
|
|
192
|
-
}, buildMockUser());
|
|
193
|
-
expect(mockService.getByFormId).toHaveBeenCalledWith('form-1', expect.any(Object), {
|
|
194
|
-
page: 0,
|
|
195
|
-
pageSize: 10
|
|
196
|
-
});
|
|
197
|
-
expect(result.meta).toEqual({
|
|
198
|
-
total: 0,
|
|
199
|
-
page: 0,
|
|
200
|
-
pageSize: 10,
|
|
201
|
-
count: 0
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
describe('hasUserSubmitted', ()=>{
|
|
206
|
-
it('returns a truthy submitted message when the user has submitted', async ()=>{
|
|
207
|
-
mockService.hasUserSubmitted.mockResolvedValue(true);
|
|
208
|
-
const result = await controller.hasUserSubmitted({
|
|
209
|
-
formId: 'form-1'
|
|
210
|
-
}, buildMockUser());
|
|
211
|
-
expect(result).toEqual({
|
|
212
|
-
success: true,
|
|
213
|
-
message: 'User has submitted this form',
|
|
214
|
-
messageKey: 'form.result.has.submitted.success',
|
|
215
|
-
data: true
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
it('returns a not-submitted message when the user has not submitted', async ()=>{
|
|
219
|
-
mockService.hasUserSubmitted.mockResolvedValue(false);
|
|
220
|
-
const result = await controller.hasUserSubmitted({
|
|
221
|
-
formId: 'form-1'
|
|
222
|
-
}, buildMockUser());
|
|
223
|
-
expect(result).toEqual({
|
|
224
|
-
success: true,
|
|
225
|
-
message: 'User has not submitted this form',
|
|
226
|
-
messageKey: 'form.result.has.not.submitted.success',
|
|
227
|
-
data: false
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
// ==========================================================================
|
|
232
|
-
// Inherited base CRUD wiring (createApiController)
|
|
233
|
-
// ==========================================================================
|
|
234
|
-
describe('inherited CRUD endpoints', ()=>{
|
|
235
|
-
it('delete delegates to FormResultService.delete', async ()=>{
|
|
236
|
-
mockService.delete.mockResolvedValue(null);
|
|
237
|
-
const user = buildMockUser();
|
|
238
|
-
const result = await controller.delete({
|
|
239
|
-
id: [
|
|
240
|
-
'result-1'
|
|
241
|
-
],
|
|
242
|
-
type: 'delete'
|
|
243
|
-
}, user);
|
|
244
|
-
expect(mockService.delete).toHaveBeenCalledWith({
|
|
245
|
-
id: [
|
|
246
|
-
'result-1'
|
|
247
|
-
],
|
|
248
|
-
type: 'delete'
|
|
249
|
-
}, user);
|
|
250
|
-
expect(result.success).toBe(true);
|
|
251
|
-
});
|
|
252
|
-
});
|
|
253
|
-
});
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
function _define_property(obj, key, value) {
|
|
2
|
-
if (key in obj) {
|
|
3
|
-
Object.defineProperty(obj, key, {
|
|
4
|
-
value: value,
|
|
5
|
-
enumerable: true,
|
|
6
|
-
configurable: true,
|
|
7
|
-
writable: true
|
|
8
|
-
});
|
|
9
|
-
} else {
|
|
10
|
-
obj[key] = value;
|
|
11
|
-
}
|
|
12
|
-
return obj;
|
|
13
|
-
}
|
|
14
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
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
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
-
}
|
|
20
|
-
function _ts_metadata(k, v) {
|
|
21
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
-
}
|
|
23
|
-
function _ts_param(paramIndex, decorator) {
|
|
24
|
-
return function(target, key) {
|
|
25
|
-
decorator(target, key, paramIndex);
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
import { createApiController, FORM_PERMISSIONS } from '@flusys/nestjs-shared/classes';
|
|
29
|
-
import { CurrentUser, Public, RequirePermission } from '@flusys/nestjs-shared/decorators';
|
|
30
|
-
import { JwtAuthGuard, PermissionGuard } from '@flusys/nestjs-shared/guards';
|
|
31
|
-
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
32
|
-
import { Controller, HttpCode, HttpStatus, Inject, Param, Post, UseGuards } from '@nestjs/common';
|
|
33
|
-
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
|
34
|
-
import { FORM_MESSAGES } from '../config';
|
|
35
|
-
import { CreateFormDto, FormResponseDto, UpdateFormDto } from '../dtos';
|
|
36
|
-
import { FormService } from '../services/form.service';
|
|
37
|
-
export class FormController extends createApiController(CreateFormDto, UpdateFormDto, FormResponseDto, {
|
|
38
|
-
entityName: 'form',
|
|
39
|
-
security: {
|
|
40
|
-
insert: {
|
|
41
|
-
level: 'permission',
|
|
42
|
-
permissions: [
|
|
43
|
-
FORM_PERMISSIONS.CREATE
|
|
44
|
-
]
|
|
45
|
-
},
|
|
46
|
-
insertMany: {
|
|
47
|
-
level: 'permission',
|
|
48
|
-
permissions: [
|
|
49
|
-
FORM_PERMISSIONS.CREATE
|
|
50
|
-
]
|
|
51
|
-
},
|
|
52
|
-
getById: {
|
|
53
|
-
level: 'permission',
|
|
54
|
-
permissions: [
|
|
55
|
-
FORM_PERMISSIONS.READ
|
|
56
|
-
]
|
|
57
|
-
},
|
|
58
|
-
getAll: {
|
|
59
|
-
level: 'permission',
|
|
60
|
-
permissions: [
|
|
61
|
-
FORM_PERMISSIONS.READ
|
|
62
|
-
]
|
|
63
|
-
},
|
|
64
|
-
update: {
|
|
65
|
-
level: 'permission',
|
|
66
|
-
permissions: [
|
|
67
|
-
FORM_PERMISSIONS.UPDATE
|
|
68
|
-
]
|
|
69
|
-
},
|
|
70
|
-
updateMany: {
|
|
71
|
-
level: 'permission',
|
|
72
|
-
permissions: [
|
|
73
|
-
FORM_PERMISSIONS.UPDATE
|
|
74
|
-
]
|
|
75
|
-
},
|
|
76
|
-
delete: {
|
|
77
|
-
level: 'permission',
|
|
78
|
-
permissions: [
|
|
79
|
-
FORM_PERMISSIONS.DELETE
|
|
80
|
-
]
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}) {
|
|
84
|
-
// Public Endpoints
|
|
85
|
-
/**
|
|
86
|
-
* Get form access info (no authentication required)
|
|
87
|
-
*/ async getFormAccessInfo(id) {
|
|
88
|
-
const data = await this.formService.getFormAccessInfo(id);
|
|
89
|
-
return {
|
|
90
|
-
success: true,
|
|
91
|
-
message: 'Form access info retrieved',
|
|
92
|
-
messageKey: FORM_MESSAGES.GET_ACCESS_INFO_SUCCESS,
|
|
93
|
-
data
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
async getPublicForm(id) {
|
|
97
|
-
const data = await this.formService.getPublicForm(id);
|
|
98
|
-
return {
|
|
99
|
-
success: true,
|
|
100
|
-
message: 'Form retrieved',
|
|
101
|
-
messageKey: FORM_MESSAGES.GET_SUCCESS,
|
|
102
|
-
data
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
async getAuthenticatedForm(id, user) {
|
|
106
|
-
const data = await this.formService.getAuthenticatedForm(id, user);
|
|
107
|
-
return {
|
|
108
|
-
success: true,
|
|
109
|
-
message: 'Form retrieved',
|
|
110
|
-
messageKey: FORM_MESSAGES.GET_SUCCESS,
|
|
111
|
-
data
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
async getBySlug(slug, _user) {
|
|
115
|
-
const data = await this.formService.getBySlug(slug);
|
|
116
|
-
return {
|
|
117
|
-
success: true,
|
|
118
|
-
message: 'Form retrieved',
|
|
119
|
-
messageKey: FORM_MESSAGES.GET_SUCCESS,
|
|
120
|
-
data
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
async getPublicFormBySlug(slug) {
|
|
124
|
-
const data = await this.formService.getPublicFormBySlug(slug);
|
|
125
|
-
return {
|
|
126
|
-
success: true,
|
|
127
|
-
message: 'Form retrieved',
|
|
128
|
-
messageKey: FORM_MESSAGES.GET_SUCCESS,
|
|
129
|
-
data
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
constructor(formService){
|
|
133
|
-
super(formService), _define_property(this, "formService", void 0), this.formService = formService;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
_ts_decorate([
|
|
137
|
-
Post('access-info/:id'),
|
|
138
|
-
Public(),
|
|
139
|
-
HttpCode(HttpStatus.OK),
|
|
140
|
-
ApiOperation({
|
|
141
|
-
summary: 'Get form access info (no auth required)'
|
|
142
|
-
}),
|
|
143
|
-
_ts_param(0, Param('id')),
|
|
144
|
-
_ts_metadata("design:type", Function),
|
|
145
|
-
_ts_metadata("design:paramtypes", [
|
|
146
|
-
String
|
|
147
|
-
]),
|
|
148
|
-
_ts_metadata("design:returntype", Promise)
|
|
149
|
-
], FormController.prototype, "getFormAccessInfo", null);
|
|
150
|
-
_ts_decorate([
|
|
151
|
-
Post('public/:id'),
|
|
152
|
-
Public(),
|
|
153
|
-
HttpCode(HttpStatus.OK),
|
|
154
|
-
ApiOperation({
|
|
155
|
-
summary: 'Get public form for submission (no auth required)'
|
|
156
|
-
}),
|
|
157
|
-
_ts_param(0, Param('id')),
|
|
158
|
-
_ts_metadata("design:type", Function),
|
|
159
|
-
_ts_metadata("design:paramtypes", [
|
|
160
|
-
String
|
|
161
|
-
]),
|
|
162
|
-
_ts_metadata("design:returntype", Promise)
|
|
163
|
-
], FormController.prototype, "getPublicForm", null);
|
|
164
|
-
_ts_decorate([
|
|
165
|
-
Post('authenticated/:id'),
|
|
166
|
-
UseGuards(JwtAuthGuard),
|
|
167
|
-
ApiBearerAuth(),
|
|
168
|
-
HttpCode(HttpStatus.OK),
|
|
169
|
-
ApiOperation({
|
|
170
|
-
summary: 'Get form for authenticated submission'
|
|
171
|
-
}),
|
|
172
|
-
_ts_param(0, Param('id')),
|
|
173
|
-
_ts_param(1, CurrentUser()),
|
|
174
|
-
_ts_metadata("design:type", Function),
|
|
175
|
-
_ts_metadata("design:paramtypes", [
|
|
176
|
-
String,
|
|
177
|
-
typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
|
|
178
|
-
]),
|
|
179
|
-
_ts_metadata("design:returntype", Promise)
|
|
180
|
-
], FormController.prototype, "getAuthenticatedForm", null);
|
|
181
|
-
_ts_decorate([
|
|
182
|
-
Post('by-slug/:slug'),
|
|
183
|
-
UseGuards(JwtAuthGuard, PermissionGuard),
|
|
184
|
-
ApiBearerAuth(),
|
|
185
|
-
RequirePermission(FORM_PERMISSIONS.READ),
|
|
186
|
-
HttpCode(HttpStatus.OK),
|
|
187
|
-
ApiOperation({
|
|
188
|
-
summary: 'Get form by slug'
|
|
189
|
-
}),
|
|
190
|
-
_ts_param(0, Param('slug')),
|
|
191
|
-
_ts_param(1, CurrentUser()),
|
|
192
|
-
_ts_metadata("design:type", Function),
|
|
193
|
-
_ts_metadata("design:paramtypes", [
|
|
194
|
-
String,
|
|
195
|
-
typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
|
|
196
|
-
]),
|
|
197
|
-
_ts_metadata("design:returntype", Promise)
|
|
198
|
-
], FormController.prototype, "getBySlug", null);
|
|
199
|
-
_ts_decorate([
|
|
200
|
-
Post('public/by-slug/:slug'),
|
|
201
|
-
Public(),
|
|
202
|
-
HttpCode(HttpStatus.OK),
|
|
203
|
-
ApiOperation({
|
|
204
|
-
summary: 'Get public form by slug (no auth required)'
|
|
205
|
-
}),
|
|
206
|
-
_ts_param(0, Param('slug')),
|
|
207
|
-
_ts_metadata("design:type", Function),
|
|
208
|
-
_ts_metadata("design:paramtypes", [
|
|
209
|
-
String
|
|
210
|
-
]),
|
|
211
|
-
_ts_metadata("design:returntype", Promise)
|
|
212
|
-
], FormController.prototype, "getPublicFormBySlug", null);
|
|
213
|
-
FormController = _ts_decorate([
|
|
214
|
-
ApiTags('Form Builder - Forms'),
|
|
215
|
-
Controller('form-builder/form'),
|
|
216
|
-
_ts_param(0, Inject(FormService)),
|
|
217
|
-
_ts_metadata("design:type", Function),
|
|
218
|
-
_ts_metadata("design:paramtypes", [
|
|
219
|
-
typeof FormService === "undefined" ? Object : FormService
|
|
220
|
-
])
|
|
221
|
-
], FormController);
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
import { Test } from '@nestjs/testing';
|
|
2
|
-
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
3
|
-
import { buildMockUser } from '@test-utils/mocks/logged-user.mock';
|
|
4
|
-
import { FormAccessType } from '../enums/form-access-type.enum';
|
|
5
|
-
import { FormController } from './form.controller';
|
|
6
|
-
import { FormService } from '../services/form.service';
|
|
7
|
-
describe('FormController', ()=>{
|
|
8
|
-
let controller;
|
|
9
|
-
let mockService;
|
|
10
|
-
beforeEach(async ()=>{
|
|
11
|
-
mockService = {
|
|
12
|
-
insert: jest.fn(),
|
|
13
|
-
insertMany: jest.fn(),
|
|
14
|
-
findById: jest.fn(),
|
|
15
|
-
findByIds: jest.fn(),
|
|
16
|
-
getAll: jest.fn(),
|
|
17
|
-
update: jest.fn(),
|
|
18
|
-
updateMany: jest.fn(),
|
|
19
|
-
delete: jest.fn(),
|
|
20
|
-
getFormAccessInfo: jest.fn(),
|
|
21
|
-
getPublicForm: jest.fn(),
|
|
22
|
-
getAuthenticatedForm: jest.fn(),
|
|
23
|
-
getBySlug: jest.fn(),
|
|
24
|
-
getPublicFormBySlug: jest.fn()
|
|
25
|
-
};
|
|
26
|
-
const module = await Test.createTestingModule({
|
|
27
|
-
controllers: [
|
|
28
|
-
FormController
|
|
29
|
-
],
|
|
30
|
-
// UtilsService is a real (dependency-free) provider — required to satisfy the
|
|
31
|
-
// Slug interceptor applied by the inherited insert/update endpoints.
|
|
32
|
-
providers: [
|
|
33
|
-
{
|
|
34
|
-
provide: FormService,
|
|
35
|
-
useValue: mockService
|
|
36
|
-
},
|
|
37
|
-
UtilsService
|
|
38
|
-
]
|
|
39
|
-
}).compile();
|
|
40
|
-
controller = module.get(FormController);
|
|
41
|
-
});
|
|
42
|
-
afterEach(()=>{
|
|
43
|
-
jest.clearAllMocks();
|
|
44
|
-
});
|
|
45
|
-
describe('getFormAccessInfo', ()=>{
|
|
46
|
-
it('returns access info without requiring authentication', async ()=>{
|
|
47
|
-
mockService.getFormAccessInfo.mockResolvedValue({
|
|
48
|
-
id: 'form-1',
|
|
49
|
-
name: 'Survey',
|
|
50
|
-
description: null,
|
|
51
|
-
accessType: FormAccessType.PUBLIC,
|
|
52
|
-
actionGroups: null,
|
|
53
|
-
isActive: true
|
|
54
|
-
});
|
|
55
|
-
const result = await controller.getFormAccessInfo('form-1');
|
|
56
|
-
expect(result).toEqual({
|
|
57
|
-
success: true,
|
|
58
|
-
message: 'Form access info retrieved',
|
|
59
|
-
messageKey: 'form.get.access.info.success',
|
|
60
|
-
data: expect.objectContaining({
|
|
61
|
-
id: 'form-1',
|
|
62
|
-
accessType: FormAccessType.PUBLIC
|
|
63
|
-
})
|
|
64
|
-
});
|
|
65
|
-
expect(mockService.getFormAccessInfo).toHaveBeenCalledWith('form-1');
|
|
66
|
-
});
|
|
67
|
-
it('propagates NotFoundException raised by the service', async ()=>{
|
|
68
|
-
mockService.getFormAccessInfo.mockRejectedValue(Object.assign(new Error('not found'), {
|
|
69
|
-
messageKey: 'form.not.found'
|
|
70
|
-
}));
|
|
71
|
-
await expect(controller.getFormAccessInfo('missing')).rejects.toThrow();
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
describe('getPublicForm', ()=>{
|
|
75
|
-
it('returns the public form projection', async ()=>{
|
|
76
|
-
mockService.getPublicForm.mockResolvedValue({
|
|
77
|
-
id: 'form-1',
|
|
78
|
-
name: 'Survey',
|
|
79
|
-
description: null,
|
|
80
|
-
schema: {
|
|
81
|
-
sections: []
|
|
82
|
-
},
|
|
83
|
-
schemaVersion: 1
|
|
84
|
-
});
|
|
85
|
-
const result = await controller.getPublicForm('form-1');
|
|
86
|
-
expect(result.success).toBe(true);
|
|
87
|
-
expect(result.messageKey).toBe('form.get.success');
|
|
88
|
-
expect(result.data.id).toBe('form-1');
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
describe('getAuthenticatedForm', ()=>{
|
|
92
|
-
it('forwards the current user to the service and returns the form', async ()=>{
|
|
93
|
-
const user = buildMockUser();
|
|
94
|
-
mockService.getAuthenticatedForm.mockResolvedValue({
|
|
95
|
-
id: 'form-1',
|
|
96
|
-
name: 'Survey',
|
|
97
|
-
description: null,
|
|
98
|
-
schema: {},
|
|
99
|
-
schemaVersion: 1
|
|
100
|
-
});
|
|
101
|
-
const result = await controller.getAuthenticatedForm('form-1', user);
|
|
102
|
-
expect(mockService.getAuthenticatedForm).toHaveBeenCalledWith('form-1', user);
|
|
103
|
-
expect(result.data.id).toBe('form-1');
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
describe('getBySlug', ()=>{
|
|
107
|
-
it('returns the form when found by slug', async ()=>{
|
|
108
|
-
mockService.getBySlug.mockResolvedValue({
|
|
109
|
-
id: 'form-1',
|
|
110
|
-
name: 'Survey',
|
|
111
|
-
description: null,
|
|
112
|
-
slug: 'survey',
|
|
113
|
-
schema: {},
|
|
114
|
-
schemaVersion: 1,
|
|
115
|
-
accessType: FormAccessType.AUTHENTICATED,
|
|
116
|
-
actionGroups: null,
|
|
117
|
-
tags: null,
|
|
118
|
-
companyId: null,
|
|
119
|
-
isActive: true,
|
|
120
|
-
createdAt: new Date(),
|
|
121
|
-
updatedAt: new Date(),
|
|
122
|
-
deletedAt: null,
|
|
123
|
-
createdById: null,
|
|
124
|
-
updatedById: null,
|
|
125
|
-
deletedById: null
|
|
126
|
-
});
|
|
127
|
-
const result = await controller.getBySlug('survey', buildMockUser());
|
|
128
|
-
expect(result.data?.slug).toBe('survey');
|
|
129
|
-
});
|
|
130
|
-
it('returns null data when no form matches the slug', async ()=>{
|
|
131
|
-
mockService.getBySlug.mockResolvedValue(null);
|
|
132
|
-
const result = await controller.getBySlug('missing', buildMockUser());
|
|
133
|
-
expect(result.data).toBeNull();
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
describe('getPublicFormBySlug', ()=>{
|
|
137
|
-
it('returns the public form projection when found', async ()=>{
|
|
138
|
-
mockService.getPublicFormBySlug.mockResolvedValue({
|
|
139
|
-
id: 'form-1',
|
|
140
|
-
name: 'Survey',
|
|
141
|
-
description: null,
|
|
142
|
-
schema: {},
|
|
143
|
-
schemaVersion: 1
|
|
144
|
-
});
|
|
145
|
-
const result = await controller.getPublicFormBySlug('survey');
|
|
146
|
-
expect(result.data?.id).toBe('form-1');
|
|
147
|
-
});
|
|
148
|
-
it('returns null data when no public form matches the slug', async ()=>{
|
|
149
|
-
mockService.getPublicFormBySlug.mockResolvedValue(null);
|
|
150
|
-
const result = await controller.getPublicFormBySlug('missing');
|
|
151
|
-
expect(result.data).toBeNull();
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
// ==========================================================================
|
|
155
|
-
// Inherited base CRUD wiring (createApiController)
|
|
156
|
-
// ==========================================================================
|
|
157
|
-
describe('inherited CRUD endpoints', ()=>{
|
|
158
|
-
it('insert delegates to FormService.insert and wraps the result', async ()=>{
|
|
159
|
-
mockService.insert.mockResolvedValue({
|
|
160
|
-
id: 'form-1',
|
|
161
|
-
name: 'Survey',
|
|
162
|
-
description: null,
|
|
163
|
-
slug: null,
|
|
164
|
-
schema: {},
|
|
165
|
-
schemaVersion: 1,
|
|
166
|
-
accessType: FormAccessType.AUTHENTICATED,
|
|
167
|
-
actionGroups: null,
|
|
168
|
-
tags: null,
|
|
169
|
-
companyId: null,
|
|
170
|
-
isActive: true,
|
|
171
|
-
createdAt: new Date(),
|
|
172
|
-
updatedAt: new Date(),
|
|
173
|
-
deletedAt: null,
|
|
174
|
-
createdById: null,
|
|
175
|
-
updatedById: null,
|
|
176
|
-
deletedById: null
|
|
177
|
-
});
|
|
178
|
-
const user = buildMockUser();
|
|
179
|
-
const result = await controller.insert({
|
|
180
|
-
name: 'Survey',
|
|
181
|
-
schema: {}
|
|
182
|
-
}, user);
|
|
183
|
-
expect(mockService.insert).toHaveBeenCalledWith({
|
|
184
|
-
name: 'Survey',
|
|
185
|
-
schema: {}
|
|
186
|
-
}, user);
|
|
187
|
-
expect(result.success).toBe(true);
|
|
188
|
-
expect(result.data.name).toBe('Survey');
|
|
189
|
-
});
|
|
190
|
-
it('delete delegates to FormService.delete', async ()=>{
|
|
191
|
-
mockService.delete.mockResolvedValue(null);
|
|
192
|
-
const user = buildMockUser();
|
|
193
|
-
const result = await controller.delete({
|
|
194
|
-
id: [
|
|
195
|
-
'form-1'
|
|
196
|
-
],
|
|
197
|
-
type: 'delete'
|
|
198
|
-
}, user);
|
|
199
|
-
expect(mockService.delete).toHaveBeenCalledWith({
|
|
200
|
-
id: [
|
|
201
|
-
'form-1'
|
|
202
|
-
],
|
|
203
|
-
type: 'delete'
|
|
204
|
-
}, user);
|
|
205
|
-
expect(result.success).toBe(true);
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
});
|