@getjones/test-infra 5.2.12 → 5.2.13
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/package.json +2 -2
- package/src/common/commonFlows/records/records.js +43 -0
- package/src/common/consts/projectSettings.js +7 -0
- package/src/common/errorCodes.js +2 -1
- package/src/index.js +4 -0
- package/src/saas/internal-api/accounts/api/core/accounts.core.api.js +8 -2
- package/src/saas/internal-api/accounts/api/core/accounts.core.service.js +10 -0
- package/src/saas/internal-api/auth/api/auth.service.js +13 -2
- package/src/saas/internal-api/certificial/api/certificial.api.js +15 -0
- package/src/saas/internal-api/certificial/api/certificial.service.js +24 -0
- package/src/saas/internal-api/certificial/consts.js +8 -0
- package/src/saas/internal-api/certificial/index.js +4 -0
- package/src/saas/internal-api/compliance/api/overrideGap/override.gap.api.js +25 -2
- package/src/saas/internal-api/compliance/api/overrideGap/override.gap.service.js +93 -2
- package/src/saas/internal-api/compliance/consts/overrideGapRequest/override.gap.request.js +8 -1
- package/src/saas/internal-api/compliance/models/overrideGapRequest/override.gap.request.model.js +16 -1
- package/src/saas/internal-api/dbEmails/api/db.emails.service.js +31 -0
- package/src/saas/internal-api/dbEmails/consts/consts.js +3 -0
- package/src/saas/internal-api/notificationCenter/consts.js +4 -1
- package/src/saas/internal-api/outlook/api/outlook.api.js +2 -2
- package/src/saas/internal-api/outlook/api/outlook.service.js +2 -2
- package/src/saas/internal-api/projects/models/settings/update.project.settings.model.js +17 -1
- package/src/saas/internal-api/schedulerJobRun/api/scheduler.job.run.api.js +31 -0
- package/src/saas/internal-api/schedulerJobRun/api/scheduler.job.run.service.js +56 -0
- package/src/saas/internal-api/schedulerJobRun/index.js +3 -0
- package/src/saas/internal-api/thread-scheduler/runner.js +17 -0
- package/src/saas/internal-api/thread-scheduler/schedulerJobs.js +2 -0
- package/src/saas/internal-api/uploadFiles/api/upload.file.service.js +19 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getjones/test-infra",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.13",
|
|
4
4
|
"description": "Providing internal and openapi interface",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"license": "ISC",
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"axios": "1.8.1",
|
|
22
|
-
"chai": "
|
|
22
|
+
"chai": "6.3.0",
|
|
23
23
|
"dotenv": "16.5.0",
|
|
24
24
|
"enum": "3.0.4",
|
|
25
25
|
"form-data": "4.0.6",
|
|
@@ -23,6 +23,7 @@ const { TabExtractionRequestModel } = require('../../../saas/internal-api/requir
|
|
|
23
23
|
const { DEFAULT_LEASE_PATH } = require('../../../saas/internal-api/requirements/consts/consts');
|
|
24
24
|
const { RELATED_DOC_TYPES } = require('../../../saas/internal-api/uploadFiles/consts');
|
|
25
25
|
const { WAIVER_REASONS, EXPIRY_TYPES } = compliance.models.waive;
|
|
26
|
+
const { DEFAULT_PM_OVERRIDE_REASON } = require('../../../saas/internal-api/compliance/consts/overrideGapRequest/override.gap.request');
|
|
26
27
|
|
|
27
28
|
const makeRecordNotResponsive = async function(recordId, context) {
|
|
28
29
|
const { data: record } = await contractors.services.getContractorById(recordId, context);
|
|
@@ -328,6 +329,45 @@ const rejectOverrideGapRequest = async (overrideGapRequestId, rejectReason, reco
|
|
|
328
329
|
return compliance.services.overrideGap.rejectOverrideGapRequest(overrideGapRequestId, rejectReason, record.dateModified, context);
|
|
329
330
|
};
|
|
330
331
|
|
|
332
|
+
const createPmOverrideRequest = async (recordId, policyName, attributeName, assigneeUserIds, context, {
|
|
333
|
+
requestReason = DEFAULT_PM_OVERRIDE_REASON,
|
|
334
|
+
expiryType = EXPIRY_TYPES.NEVER,
|
|
335
|
+
expiryDate = null,
|
|
336
|
+
expectError
|
|
337
|
+
} = {}) => {
|
|
338
|
+
const { data: record } = await contractors.services.getContractorById(recordId, context);
|
|
339
|
+
const auditorContractorId = compliance.services.overrideGap.getAuditorContractorIdFromGap(record, policyName, attributeName);
|
|
340
|
+
return compliance.services.overrideGap.createPmOverrideRequest(auditorContractorId, context, {
|
|
341
|
+
requestReason,
|
|
342
|
+
expiryType,
|
|
343
|
+
expiryDate,
|
|
344
|
+
assigneeUserIds,
|
|
345
|
+
recordDateModified: record.dateModified,
|
|
346
|
+
expectError
|
|
347
|
+
});
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const editPmOverrideRequest = async (overrideGapRequestId, recordId, context, {
|
|
351
|
+
requestReason,
|
|
352
|
+
expiryType,
|
|
353
|
+
expiryDate = null,
|
|
354
|
+
assigneeUserIds,
|
|
355
|
+
expectError
|
|
356
|
+
} = {}) => {
|
|
357
|
+
const { data: record } = await contractors.services.getContractorById(recordId, context);
|
|
358
|
+
return compliance.services.overrideGap.editPmOverrideRequest(overrideGapRequestId, context, {
|
|
359
|
+
requestReason,
|
|
360
|
+
expiryType,
|
|
361
|
+
expiryDate,
|
|
362
|
+
assigneeUserIds,
|
|
363
|
+
recordDateModified: record.dateModified,
|
|
364
|
+
expectError
|
|
365
|
+
});
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const deletePmOverrideRequest = (overrideGapRequestId, context, { expectError } = {}) =>
|
|
369
|
+
compliance.services.overrideGap.deletePmOverrideRequest(overrideGapRequestId, context, { expectError });
|
|
370
|
+
|
|
331
371
|
const uploadMultipleCoiFiles = async (recordId, coiFilePaths, uploadedBy, uploaderEmail, context, { uploaderContext, duplicateHandling } = {}) => {
|
|
332
372
|
const recordResponse = await contractors.services.getContractorById(recordId, context);
|
|
333
373
|
const recordDateModified = recordResponse.data.dateModified;
|
|
@@ -447,6 +487,9 @@ module.exports = {
|
|
|
447
487
|
getActiveOverrides,
|
|
448
488
|
createOverrideGapRequest,
|
|
449
489
|
rejectOverrideGapRequest,
|
|
490
|
+
createPmOverrideRequest,
|
|
491
|
+
editPmOverrideRequest,
|
|
492
|
+
deletePmOverrideRequest,
|
|
450
493
|
uploadMultipleCoiFiles,
|
|
451
494
|
createTenantUser,
|
|
452
495
|
deleteRecordByName,
|
|
@@ -21,6 +21,13 @@ module.exports = {
|
|
|
21
21
|
onForTenants: { overrideGapRequestSettings: { isOverrideGapRequestEnabledForVendors: false, isOverrideGapRequestEnabledForTenants: true, isOverrideGapRequestEnabledForGuests: false } },
|
|
22
22
|
onForGuests: { overrideGapRequestSettings: { isOverrideGapRequestEnabledForVendors: false, isOverrideGapRequestEnabledForTenants: false, isOverrideGapRequestEnabledForGuests: true } }
|
|
23
23
|
},
|
|
24
|
+
userOverrideRequest: {
|
|
25
|
+
onForAll: { userOverrideRequestSettings: { isUserOverrideRequestEnabledForVendors: true, isUserOverrideRequestEnabledForTenants: true, isUserOverrideRequestEnabledForGuests: true } },
|
|
26
|
+
offForAll: { userOverrideRequestSettings: { isUserOverrideRequestEnabledForVendors: false, isUserOverrideRequestEnabledForTenants: false, isUserOverrideRequestEnabledForGuests: false } },
|
|
27
|
+
onForVendors: { userOverrideRequestSettings: { isUserOverrideRequestEnabledForVendors: true, isUserOverrideRequestEnabledForTenants: false, isUserOverrideRequestEnabledForGuests: false } },
|
|
28
|
+
onForTenants: { userOverrideRequestSettings: { isUserOverrideRequestEnabledForVendors: false, isUserOverrideRequestEnabledForTenants: true, isUserOverrideRequestEnabledForGuests: false } },
|
|
29
|
+
onForGuests: { userOverrideRequestSettings: { isUserOverrideRequestEnabledForVendors: false, isUserOverrideRequestEnabledForTenants: false, isUserOverrideRequestEnabledForGuests: true } }
|
|
30
|
+
},
|
|
24
31
|
onboardingPhase: {
|
|
25
32
|
on: { onboardingPhase: true },
|
|
26
33
|
off: { onboardingPhase: false }
|
package/src/common/errorCodes.js
CHANGED
|
@@ -8,5 +8,6 @@ module.exports = {
|
|
|
8
8
|
NOT_FOUND: 'NOT_FOUND',
|
|
9
9
|
NOT_ELIGIBLE: 'NOT_ELIGIBLE',
|
|
10
10
|
RECORD_NOT_IN_ACCOUNT: 'RECORD_NOT_IN_ACCOUNT',
|
|
11
|
-
GLOBAL_RECORD_NOT_IN_ACCOUNT: 'GLOBAL_RECORD_NOT_IN_ACCOUNT'
|
|
11
|
+
GLOBAL_RECORD_NOT_IN_ACCOUNT: 'GLOBAL_RECORD_NOT_IN_ACCOUNT',
|
|
12
|
+
IDENTITY_CHANGED: 'IDENTITY_CHANGED'
|
|
12
13
|
};
|
package/src/index.js
CHANGED
|
@@ -47,6 +47,8 @@ const getInternalAPI = () => {
|
|
|
47
47
|
const sampleCoi = require('./saas/internal-api/sampleCoi');
|
|
48
48
|
|
|
49
49
|
const threadScheduler = require('./saas/internal-api/thread-scheduler/runner');
|
|
50
|
+
const schedulerJobRun = require('./saas/internal-api/schedulerJobRun');
|
|
51
|
+
const certificial = require('./saas/internal-api/certificial');
|
|
50
52
|
const dbEmails = require('./saas/internal-api/dbEmails/index');
|
|
51
53
|
const agreement = require('./saas/internal-api/agreement/index');
|
|
52
54
|
const uploadFile = require('./saas/internal-api/uploadFiles/index');
|
|
@@ -86,6 +88,8 @@ const getInternalAPI = () => {
|
|
|
86
88
|
requestsTable,
|
|
87
89
|
sampleCoi,
|
|
88
90
|
threadScheduler,
|
|
91
|
+
schedulerJobRun,
|
|
92
|
+
certificial,
|
|
89
93
|
dbEmails,
|
|
90
94
|
emailCustomization,
|
|
91
95
|
factory,
|
|
@@ -5,6 +5,7 @@ const { backendUrl } = require('../../../../../env').getEnv();
|
|
|
5
5
|
|
|
6
6
|
const Endpoints = {
|
|
7
7
|
getAccount: `${backendUrl}/saas/accounts`,
|
|
8
|
+
getAccountList: `${backendUrl}/saas/accountList`,
|
|
8
9
|
create: `${backendUrl}/saas/createaccount`,
|
|
9
10
|
delete: `${backendUrl}/saas/account`,
|
|
10
11
|
edit: accountId => `${backendUrl}/saas/account/${accountId}/settings`,
|
|
@@ -24,9 +25,13 @@ const updateAccountSettings = (accountId, settings, context) => {
|
|
|
24
25
|
return requests.putRequestHandler(Endpoints.edit(accountId), { accountId, ... settings }, context);
|
|
25
26
|
};
|
|
26
27
|
|
|
27
|
-
const getByName = (accountName, context) => {
|
|
28
|
+
const getByName = (accountName, context, headers = {}) => {
|
|
28
29
|
const params = { name: accountName };
|
|
29
|
-
return requests.getRequestHandler(Endpoints.getAccount, params, context);
|
|
30
|
+
return requests.getRequestHandler(Endpoints.getAccount, params, context, headers);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getAccountList = (context, headers = {}) => {
|
|
34
|
+
return requests.getRequestHandler(Endpoints.getAccountList, {}, context, headers);
|
|
30
35
|
};
|
|
31
36
|
|
|
32
37
|
const getFromCertificateHolder = certificateHolder => {
|
|
@@ -41,6 +46,7 @@ const getSupportAutomationAllowed = accountIds => {
|
|
|
41
46
|
module.exports = {
|
|
42
47
|
create,
|
|
43
48
|
getByName,
|
|
49
|
+
getAccountList,
|
|
44
50
|
deleteAccount,
|
|
45
51
|
updateAccountSettings,
|
|
46
52
|
getFromCertificateHolder,
|
|
@@ -18,6 +18,10 @@ const createAccount = async (createAccountModel, context) => {
|
|
|
18
18
|
return accountsAPI.create(createAccountModel, context);
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
const getAccountsResponse = (accountName, context, headers = {}) => {
|
|
22
|
+
return accountsAPI.getByName(accountName, context, headers);
|
|
23
|
+
};
|
|
24
|
+
|
|
21
25
|
const getAccount = async (accountName, context) => {
|
|
22
26
|
const response = await accountsAPI.getByName(accountName, context);
|
|
23
27
|
expect(response.status, 'Failed to get accounts by name').to.equal(httpResponseStatusCodes.OK);
|
|
@@ -35,6 +39,10 @@ const getAccount = async (accountName, context) => {
|
|
|
35
39
|
return existingAccounts[0];
|
|
36
40
|
};
|
|
37
41
|
|
|
42
|
+
const getAccountList = context => {
|
|
43
|
+
return accountsAPI.getAccountList(context);
|
|
44
|
+
};
|
|
45
|
+
|
|
38
46
|
const getOrCreateAccount = async (createAccountModel, context) => {
|
|
39
47
|
validateType(createAccountModel, CreateAccountModel);
|
|
40
48
|
const existingAccount = await getAccount(createAccountModel.companyName, context);
|
|
@@ -94,6 +102,8 @@ const getSupportAutomationAllowed = async (accountIds, { expectError } = {}) =>
|
|
|
94
102
|
|
|
95
103
|
module.exports = {
|
|
96
104
|
getAccount,
|
|
105
|
+
getAccountsResponse,
|
|
106
|
+
getAccountList,
|
|
97
107
|
getOrCreateAccount,
|
|
98
108
|
getAccountId,
|
|
99
109
|
createAccount,
|
|
@@ -12,7 +12,8 @@ const RETRYABLE_LOGIN_STATUSES = [
|
|
|
12
12
|
httpResponseStatusCodes.GATEWAY_TIMEOUT
|
|
13
13
|
];
|
|
14
14
|
const MAX_LOGIN_ATTEMPTS = 3;
|
|
15
|
-
const
|
|
15
|
+
const LOGIN_RETRY_STEP_TIMEOUT = 2000;
|
|
16
|
+
const LOGIN_RETRY_RANGE_TIMEOUT = 5000;
|
|
16
17
|
|
|
17
18
|
// Parallel callers for the same user share one login request
|
|
18
19
|
const pendingLogins = new Map();
|
|
@@ -41,7 +42,8 @@ const loginWithRetry = async (email, password) => {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
console.warn(`Login for ${email} failed with status ${response.status}, retrying`);
|
|
44
|
-
|
|
45
|
+
const retryRandomTimeout = attempt * LOGIN_RETRY_STEP_TIMEOUT + Math.random() * LOGIN_RETRY_RANGE_TIMEOUT;
|
|
46
|
+
await timeUtils.waitFor(retryRandomTimeout).mSeconds;
|
|
45
47
|
}
|
|
46
48
|
};
|
|
47
49
|
|
|
@@ -131,6 +133,14 @@ const getAuthenticatedUser = context => {
|
|
|
131
133
|
return authApi.getAuthenticatedUser(context);
|
|
132
134
|
};
|
|
133
135
|
|
|
136
|
+
// The id the backend stamps into the session on login — the same value
|
|
137
|
+
// getLoggedInUserId() resolves from the JWT. Available only after the email
|
|
138
|
+
// has logged in (getAuthCookie caches it); returns null otherwise.
|
|
139
|
+
const getUserId = email => {
|
|
140
|
+
const identity = getSystemUserIdentity(email);
|
|
141
|
+
return identity ? identity.id : null;
|
|
142
|
+
};
|
|
143
|
+
|
|
134
144
|
const isHubUser = async (email, { expectError } = {}) => {
|
|
135
145
|
const response = await authApi.isHubUser(email);
|
|
136
146
|
|
|
@@ -198,6 +208,7 @@ module.exports = {
|
|
|
198
208
|
twoFAResendCode,
|
|
199
209
|
twoFAVerify,
|
|
200
210
|
getAuthenticatedUser,
|
|
211
|
+
getUserId,
|
|
201
212
|
isHubUser,
|
|
202
213
|
resetPassword,
|
|
203
214
|
getLoginAttempts,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const requests = require('../../../../common/requests/cookieAuth/requests');
|
|
2
|
+
const { backendUrl, threadSchedulerSecret } = require('../../../../env').getEnv();
|
|
3
|
+
|
|
4
|
+
const Endpoints = {
|
|
5
|
+
enrollments: `${backendUrl}/schedulerJob/certificial/enrollments`
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// Authenticated by x-scheduler-secret, not a cookie, so no ics context is passed.
|
|
9
|
+
const getEnrollments = recordIds => {
|
|
10
|
+
const headers = { 'x-scheduler-secret': threadSchedulerSecret };
|
|
11
|
+
|
|
12
|
+
return requests.getRequestHandler(Endpoints.enrollments, { recordIds: recordIds.toString() }, null, headers);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
module.exports = { getEnrollments };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const certificialApi = require('./certificial.api');
|
|
2
|
+
const httpResponseStatusCodes = require('../../../../common/httpResponseStatusCodes.json');
|
|
3
|
+
|
|
4
|
+
// One row per requested record id. A record with no enrollment comes back with null fields, not a 404.
|
|
5
|
+
const getEnrollmentStates = async recordIds => {
|
|
6
|
+
const response = await certificialApi.getEnrollments(recordIds);
|
|
7
|
+
|
|
8
|
+
if (response.status !== httpResponseStatusCodes.OK) {
|
|
9
|
+
throw new Error(`Failed to read Certificial enrollment state for records ${recordIds}, response status - ${response.status}`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return response.data;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const getEnrollmentState = async recordId => {
|
|
16
|
+
const [enrollmentState] = await getEnrollmentStates([recordId]);
|
|
17
|
+
|
|
18
|
+
return enrollmentState;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
getEnrollmentStates,
|
|
23
|
+
getEnrollmentState
|
|
24
|
+
};
|
|
@@ -13,7 +13,10 @@ const Endpoints = {
|
|
|
13
13
|
updateOverrideExpiryDate: auditorContractorId => `${backendUrl}/saas/integration-tests/override/${auditorContractorId}/expiry-date`,
|
|
14
14
|
getOverrideGapRequestById: overrideGapRequestId => `${backendUrl}/saas/gaps/override-gap-request/${overrideGapRequestId}`,
|
|
15
15
|
createOverrideGapRequest: `${backendUrl}/secured/vendor-app/record/override-gap-request`,
|
|
16
|
-
rejectOverrideGapRequest: `${backendUrl}/saas/gaps/override-gap-request/reject
|
|
16
|
+
rejectOverrideGapRequest: `${backendUrl}/saas/gaps/override-gap-request/reject`,
|
|
17
|
+
createPmOverrideRequest: `${backendUrl}/saas/gaps/override-gap-request`,
|
|
18
|
+
pmOverrideRequestById: overrideGapRequestId => `${backendUrl}/saas/gaps/override-gap-request/${overrideGapRequestId}`,
|
|
19
|
+
eligibleOverrideAssignees: requestId => `${backendUrl}/saas/gaps/override-gap-request/assignees/${requestId}`
|
|
17
20
|
};
|
|
18
21
|
|
|
19
22
|
const waive = async (contractorId, waiveRequestModel, context) => {
|
|
@@ -70,6 +73,22 @@ const rejectOverrideGapRequest = (rejectModel, context) => {
|
|
|
70
73
|
return requests.patchRequestHandler(Endpoints.rejectOverrideGapRequest, context, rejectModel);
|
|
71
74
|
};
|
|
72
75
|
|
|
76
|
+
const createPmOverrideRequest = (pmOverrideRequestModel, context) => {
|
|
77
|
+
return requests.postRequestHandler(Endpoints.createPmOverrideRequest, pmOverrideRequestModel, context);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const editPmOverrideRequest = (overrideGapRequestId, pmOverrideRequestModel, context) => {
|
|
81
|
+
return requests.patchRequestHandler(Endpoints.pmOverrideRequestById(overrideGapRequestId), context, pmOverrideRequestModel);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const deletePmOverrideRequest = (overrideGapRequestId, context) => {
|
|
85
|
+
return requests.deleteRequestHandler(Endpoints.pmOverrideRequestById(overrideGapRequestId), context);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const getEligibleOverrideAssignees = (requestId, context) => {
|
|
89
|
+
return requests.getRequestHandler(Endpoints.eligibleOverrideAssignees(requestId), {}, context);
|
|
90
|
+
};
|
|
91
|
+
|
|
73
92
|
module.exports = {
|
|
74
93
|
waive,
|
|
75
94
|
retract,
|
|
@@ -82,5 +101,9 @@ module.exports = {
|
|
|
82
101
|
updateOverrideExpiryDate,
|
|
83
102
|
getOverrideGapRequestById,
|
|
84
103
|
createOverrideGapRequest,
|
|
85
|
-
rejectOverrideGapRequest
|
|
104
|
+
rejectOverrideGapRequest,
|
|
105
|
+
createPmOverrideRequest,
|
|
106
|
+
editPmOverrideRequest,
|
|
107
|
+
deletePmOverrideRequest,
|
|
108
|
+
getEligibleOverrideAssignees
|
|
86
109
|
};
|
|
@@ -10,7 +10,7 @@ const {
|
|
|
10
10
|
WAIVER_REASONS,
|
|
11
11
|
EXPIRY_TYPES
|
|
12
12
|
} = require('../../models/waive/waive.model');
|
|
13
|
-
const { OverrideGapRequestCreateModel, OverrideGapRequestRejectModel } = require('../../models/overrideGapRequest/override.gap.request.model');
|
|
13
|
+
const { OverrideGapRequestCreateModel, OverrideGapRequestRejectModel, PmOverrideRequestModel } = require('../../models/overrideGapRequest/override.gap.request.model');
|
|
14
14
|
const { validateType } = require('../../../../../common/validations/typeValidations');
|
|
15
15
|
const auditUtil = require('../../utils/auditUtil');
|
|
16
16
|
const httpResponseStatusCodes = require('../../../../../common/httpResponseStatusCodes.json');
|
|
@@ -277,6 +277,93 @@ const updateOverrideExpiryDate = async (auditorContractorId, expiryDate, context
|
|
|
277
277
|
return response;
|
|
278
278
|
};
|
|
279
279
|
|
|
280
|
+
const createPmOverrideRequest = async (auditorContractorId, context, {
|
|
281
|
+
requestReason,
|
|
282
|
+
expiryType,
|
|
283
|
+
expiryDate = null,
|
|
284
|
+
assigneeUserIds,
|
|
285
|
+
recordDateModified,
|
|
286
|
+
expectError
|
|
287
|
+
} = {}) => {
|
|
288
|
+
const model = new PmOverrideRequestModel({
|
|
289
|
+
auditorContractorId,
|
|
290
|
+
requestReason,
|
|
291
|
+
expiryType,
|
|
292
|
+
expiryDate,
|
|
293
|
+
assigneeUserIds,
|
|
294
|
+
recordDateModified
|
|
295
|
+
});
|
|
296
|
+
validateType(model, PmOverrideRequestModel);
|
|
297
|
+
const response = await overrideGapApi.createPmOverrideRequest(model, context);
|
|
298
|
+
|
|
299
|
+
if (expectError) {
|
|
300
|
+
return response;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (response.status !== httpResponseStatusCodes.CREATED) {
|
|
304
|
+
throw Error(`Failed to create PM override request. Status code: ${response.status}. Body: ${JSON.stringify(response.data)}`);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return response.data;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const editPmOverrideRequest = async (overrideGapRequestId, context, {
|
|
311
|
+
requestReason,
|
|
312
|
+
expiryType,
|
|
313
|
+
expiryDate = null,
|
|
314
|
+
assigneeUserIds,
|
|
315
|
+
recordDateModified,
|
|
316
|
+
expectError
|
|
317
|
+
} = {}) => {
|
|
318
|
+
const model = new PmOverrideRequestModel({
|
|
319
|
+
requestReason,
|
|
320
|
+
expiryType,
|
|
321
|
+
expiryDate,
|
|
322
|
+
assigneeUserIds,
|
|
323
|
+
recordDateModified
|
|
324
|
+
});
|
|
325
|
+
validateType(model, PmOverrideRequestModel);
|
|
326
|
+
const response = await overrideGapApi.editPmOverrideRequest(overrideGapRequestId, model, context);
|
|
327
|
+
|
|
328
|
+
if (expectError) {
|
|
329
|
+
return response;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (response.status !== httpResponseStatusCodes.NO_CONTENT) {
|
|
333
|
+
throw Error(`Failed to edit PM override request. Status code: ${response.status}`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return response;
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const deletePmOverrideRequest = async (overrideGapRequestId, context, { expectError } = {}) => {
|
|
340
|
+
const response = await overrideGapApi.deletePmOverrideRequest(overrideGapRequestId, context);
|
|
341
|
+
|
|
342
|
+
if (expectError) {
|
|
343
|
+
return response;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (response.status !== httpResponseStatusCodes.NO_CONTENT) {
|
|
347
|
+
throw Error(`Failed to delete PM override request. Status code: ${response.status}`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return response;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const getEligibleOverrideAssignees = async (requestId, context, { expectError } = {}) => {
|
|
354
|
+
const response = await overrideGapApi.getEligibleOverrideAssignees(requestId, context);
|
|
355
|
+
|
|
356
|
+
if (expectError) {
|
|
357
|
+
return response;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (response.status !== httpResponseStatusCodes.OK) {
|
|
361
|
+
throw Error(`Failed to get eligible override assignees. Status code: ${response.status}`);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return response.data;
|
|
365
|
+
};
|
|
366
|
+
|
|
280
367
|
module.exports = {
|
|
281
368
|
waive,
|
|
282
369
|
waiveAll,
|
|
@@ -296,5 +383,9 @@ module.exports = {
|
|
|
296
383
|
getOverrideGapRequestById,
|
|
297
384
|
rejectOverrideGapRequest,
|
|
298
385
|
getAuditorContractorIdFromGap,
|
|
299
|
-
updateOverrideExpiryDate
|
|
386
|
+
updateOverrideExpiryDate,
|
|
387
|
+
createPmOverrideRequest,
|
|
388
|
+
editPmOverrideRequest,
|
|
389
|
+
deletePmOverrideRequest,
|
|
390
|
+
getEligibleOverrideAssignees
|
|
300
391
|
};
|
|
@@ -4,4 +4,11 @@ const OVERRIDE_GAP_REQUEST_STATUS = {
|
|
|
4
4
|
REJECTED: 'REJECTED'
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const OVERRIDE_REQUESTER_TYPE = {
|
|
8
|
+
HUB_USER: 'HUB_USER',
|
|
9
|
+
INVITER: 'INVITER'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const DEFAULT_PM_OVERRIDE_REASON = 'PM override request reason for integration tests exceeding minimum length of 35 characters.';
|
|
13
|
+
|
|
14
|
+
module.exports = { OVERRIDE_GAP_REQUEST_STATUS, OVERRIDE_REQUESTER_TYPE, DEFAULT_PM_OVERRIDE_REASON };
|
package/src/saas/internal-api/compliance/models/overrideGapRequest/override.gap.request.model.js
CHANGED
|
@@ -13,4 +13,19 @@ class OverrideGapRequestRejectModel {
|
|
|
13
13
|
this.recordDateModified = recordDateModified;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
class PmOverrideRequestModel {
|
|
18
|
+
constructor({ auditorContractorId, requestReason, expiryType, expiryDate = null, assigneeUserIds, recordDateModified }) {
|
|
19
|
+
if (auditorContractorId !== undefined) {
|
|
20
|
+
this.auditorContractorId = auditorContractorId;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
this.requestReason = requestReason;
|
|
24
|
+
this.expiryType = expiryType;
|
|
25
|
+
this.expiryDate = expiryDate;
|
|
26
|
+
this.assigneeUserIds = assigneeUserIds;
|
|
27
|
+
this.recordDateModified = recordDateModified;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { OverrideGapRequestCreateModel, OverrideGapRequestRejectModel, PmOverrideRequestModel };
|
|
@@ -167,9 +167,40 @@ const waitForMail = async (emailModel, { maxSecondToWait = 30 } = {}) => {
|
|
|
167
167
|
return foundMails;
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
+
const waitForMails = async (emailModels, { maxSecondToWait = 60 } = {}) => {
|
|
171
|
+
const oneSecondInMilliseconds = 1000;
|
|
172
|
+
const started = new Date();
|
|
173
|
+
let timeFromTheStart = 0;
|
|
174
|
+
const foundMailsByModel = emailModels.map(() => []);
|
|
175
|
+
|
|
176
|
+
while (foundMailsByModel.some(found => found.length === 0) && timeFromTheStart < maxSecondToWait) {
|
|
177
|
+
for (let index = 0; index < emailModels.length; index++) {
|
|
178
|
+
if (foundMailsByModel[index].length > 0) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const { data } = await getAllMessages(emailModels[index]);
|
|
183
|
+
|
|
184
|
+
if (data.length > 0) {
|
|
185
|
+
foundMailsByModel[index] = data;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (foundMailsByModel.every(found => found.length > 0)) {
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
timeFromTheStart = (new Date() - started) / oneSecondInMilliseconds;
|
|
194
|
+
await timeUtils.waitFor(3).seconds;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return foundMailsByModel;
|
|
198
|
+
};
|
|
199
|
+
|
|
170
200
|
module.exports = {
|
|
171
201
|
util,
|
|
172
202
|
waitForMail,
|
|
203
|
+
waitForMails,
|
|
173
204
|
getAllMessages,
|
|
174
205
|
getLastMessage,
|
|
175
206
|
deleteMultipleMessages,
|
|
@@ -54,6 +54,9 @@ const EMAIL_SUBJECT = {
|
|
|
54
54
|
overrideGapRequestPending: ({ vendorCompanyName }) => `${vendorCompanyName} asked for an exception`,
|
|
55
55
|
overrideGapRequestRejected: ({ accountName }) => `${accountName} denied your exception request`,
|
|
56
56
|
overrideGapRequestApproved: ({ accountName }) => `${accountName} approved your exception request`,
|
|
57
|
+
pmOverrideRequestAssigned: ({ requesterFullName, companyName }) => `${requesterFullName} asked you to override a gap for ${companyName}`,
|
|
58
|
+
pmOverrideRequestDenied: ({ rejecterFullName, companyName }) => `${rejecterFullName} Rejected your override request for ${companyName}`,
|
|
59
|
+
pmOverrideRequestApproved: ({ approverFullName, companyName }) => `${approverFullName} Approved your override request for ${companyName}`,
|
|
57
60
|
timedOverrideExpired: ({ recordCompanyName, requesterName }) => `Override ended - ${recordCompanyName} in ${requesterName}`,
|
|
58
61
|
allRecordsExport: 'Jones - All Records Export'
|
|
59
62
|
};
|
|
@@ -3,7 +3,10 @@ const NOTIFICATION_TYPES = {
|
|
|
3
3
|
OVERRIDE_GAP_REQUEST_PENDING: 'NewOverrideGapRequest',
|
|
4
4
|
RELATED_DOC_UPDATED: 'RelatedDocUpdated',
|
|
5
5
|
GLOBAL_RECORD_RELATED_DOC_UPDATED: 'GlobalRecordRelatedDocUpdated',
|
|
6
|
-
TIMED_OVERRIDE_EXPIRED: 'TimedOverrideExpired'
|
|
6
|
+
TIMED_OVERRIDE_EXPIRED: 'TimedOverrideExpired',
|
|
7
|
+
PM_OVERRIDE_REQUEST: 'NewPmOverrideRequest',
|
|
8
|
+
PM_OVERRIDE_REQUEST_APPROVED: 'PmOverrideRequestApproved',
|
|
9
|
+
PM_OVERRIDE_REQUEST_REJECTED: 'PmOverrideRequestRejected'
|
|
7
10
|
};
|
|
8
11
|
|
|
9
12
|
module.exports = { NOTIFICATION_TYPES };
|
|
@@ -5,8 +5,8 @@ const Endpoints = {
|
|
|
5
5
|
getRequestIdByRecordIdBase64: base64RecordId => `${backendUrl}/secured/outlook/record/${base64RecordId}/compliance-report`
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
const getRequestIdByRecordIdBase64 = async (base64RecordId, context) => {
|
|
9
|
-
return requests.getRequestHandler(Endpoints.getRequestIdByRecordIdBase64(base64RecordId), {}, context);
|
|
8
|
+
const getRequestIdByRecordIdBase64 = async (base64RecordId, context, headers = {}) => {
|
|
9
|
+
return requests.getRequestHandler(Endpoints.getRequestIdByRecordIdBase64(base64RecordId), {}, context, headers);
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
module.exports = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const vendorProfileApi = require('./outlook.api');
|
|
2
2
|
|
|
3
|
-
const getRequestIdByRecordIdBase64 = async (base64RecordId, context) => {
|
|
4
|
-
return vendorProfileApi.getRequestIdByRecordIdBase64(base64RecordId, context);
|
|
3
|
+
const getRequestIdByRecordIdBase64 = async (base64RecordId, context, headers = {}) => {
|
|
4
|
+
return vendorProfileApi.getRequestIdByRecordIdBase64(base64RecordId, context, headers);
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
const generateRecordIdBase64 = recordId => {
|
|
@@ -8,7 +8,9 @@ class UpdateProjectSettingsModel {
|
|
|
8
8
|
projectId = null,
|
|
9
9
|
tenantSolution = false,
|
|
10
10
|
autocompleteSettings = {},
|
|
11
|
-
overrideGapRequestSettings = {}
|
|
11
|
+
overrideGapRequestSettings = {},
|
|
12
|
+
userOverrideRequestSettings = {},
|
|
13
|
+
relevantComplianceDocumentsSettings = {}
|
|
12
14
|
} = {}
|
|
13
15
|
) {
|
|
14
16
|
this.autoGaps = { ... DEFAULT_AUTOGAPS, ... autoGaps };
|
|
@@ -19,6 +21,8 @@ class UpdateProjectSettingsModel {
|
|
|
19
21
|
this.tenantSolution = tenantSolution;
|
|
20
22
|
this.autocompleteSettings = { ... DEFAULT_AUTOCOMPLETE_ADD_VENDORS, ... autocompleteSettings };
|
|
21
23
|
this.overrideGapRequestSettings = { ... DEFAULT_OVERRIDE_GAP_REQUEST, ... overrideGapRequestSettings };
|
|
24
|
+
this.userOverrideRequestSettings = { ... DEFAULT_USER_OVERRIDE_REQUEST, ... userOverrideRequestSettings };
|
|
25
|
+
this.relevantComplianceDocumentsSettings = { ... DEFAULT_RELEVANT_COMPLIANCE_DOCUMENTS, ... relevantComplianceDocumentsSettings };
|
|
22
26
|
}
|
|
23
27
|
}
|
|
24
28
|
|
|
@@ -46,3 +50,15 @@ const DEFAULT_OVERRIDE_GAP_REQUEST = {
|
|
|
46
50
|
isOverrideGapRequestEnabledForTenants: false,
|
|
47
51
|
isOverrideGapRequestEnabledForGuests: false
|
|
48
52
|
};
|
|
53
|
+
|
|
54
|
+
const DEFAULT_USER_OVERRIDE_REQUEST = {
|
|
55
|
+
isUserOverrideRequestEnabledForVendors: false,
|
|
56
|
+
isUserOverrideRequestEnabledForTenants: false,
|
|
57
|
+
isUserOverrideRequestEnabledForGuests: false
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const DEFAULT_RELEVANT_COMPLIANCE_DOCUMENTS = {
|
|
61
|
+
isRelevantComplianceDocumentsEnabledForVendors: false,
|
|
62
|
+
isRelevantComplianceDocumentsEnabledForTenants: false,
|
|
63
|
+
isRelevantComplianceDocumentsEnabledForGuests: false
|
|
64
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const requests = require('../../../../common/requests/cookieAuth/requests');
|
|
2
|
+
const { backendUrl } = require('../../../../env').getEnv();
|
|
3
|
+
|
|
4
|
+
const Endpoints = {
|
|
5
|
+
base: `${backendUrl}/saas/integration-tests/scheduler-job-run`,
|
|
6
|
+
byId: id => `${backendUrl}/saas/integration-tests/scheduler-job-run/${id}`,
|
|
7
|
+
reclaim: `${backendUrl}/saas/integration-tests/scheduler-job-run/reclaim`
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const seedJobRun = (body, context) => {
|
|
11
|
+
return requests.postRequestHandler(Endpoints.base, body, context);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const reclaimStaleJobRuns = (jobType, context) => {
|
|
15
|
+
return requests.postRequestHandler(Endpoints.reclaim, null, context, {}, { jobType });
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const getJobRun = (id, context) => {
|
|
19
|
+
return requests.getRequestHandler(Endpoints.byId(id), {}, context);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const deleteJobRunsByType = (jobType, context) => {
|
|
23
|
+
return requests.deleteRequestHandler(Endpoints.base, context, {}, { jobType });
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
seedJobRun,
|
|
28
|
+
reclaimStaleJobRuns,
|
|
29
|
+
getJobRun,
|
|
30
|
+
deleteJobRunsByType
|
|
31
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const httpResponseStatusCodes = require('../../../../common/httpResponseStatusCodes.json');
|
|
2
|
+
|
|
3
|
+
const authService = require('../../auth/api/auth.service');
|
|
4
|
+
const schedulerJobRunApi = require('./scheduler.job.run.api');
|
|
5
|
+
|
|
6
|
+
const SUCCESS_STATUSES = [httpResponseStatusCodes.OK, httpResponseStatusCodes.NO_CONTENT];
|
|
7
|
+
|
|
8
|
+
// Seeds a job_run with backdated timestamps (applied with the DB clock) to simulate an orphaned run.
|
|
9
|
+
// heartbeatMinutesAgo: null seeds a NULL heartbeat. Returns the id.
|
|
10
|
+
const seedJobRun = async ({ jobType, status, startedMinutesAgo, heartbeatMinutesAgo = null }) => {
|
|
11
|
+
const response = await schedulerJobRunApi.seedJobRun(
|
|
12
|
+
{ jobType, status, startedMinutesAgo, heartbeatMinutesAgo }, await authService.getIcsCookie());
|
|
13
|
+
|
|
14
|
+
if (response.status !== httpResponseStatusCodes.OK) {
|
|
15
|
+
throw new Error(`seedJobRun failed for ${jobType}: ${JSON.stringify(response.data)}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return response.data;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Runs reclaim in isolation (no overlap check, no job submitted) so a test can assert it without starting a real job.
|
|
22
|
+
const reclaimStaleJobRuns = async jobType => {
|
|
23
|
+
const response = await schedulerJobRunApi.reclaimStaleJobRuns(jobType, await authService.getIcsCookie());
|
|
24
|
+
|
|
25
|
+
if (response.status !== httpResponseStatusCodes.OK) {
|
|
26
|
+
throw new Error(`reclaimStaleJobRuns failed for ${jobType}: ${JSON.stringify(response.data)}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return response.data;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const getJobRun = async id => {
|
|
33
|
+
const response = await schedulerJobRunApi.getJobRun(id, await authService.getIcsCookie());
|
|
34
|
+
|
|
35
|
+
if (response.status !== httpResponseStatusCodes.OK) {
|
|
36
|
+
throw new Error(`getJobRun failed for id ${id}: ${JSON.stringify(response.data)}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return response.data;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Clears all job_run rows of a type between runs. Safe outside production, where only seeded rows exist.
|
|
43
|
+
const deleteJobRunsByType = async jobType => {
|
|
44
|
+
const response = await schedulerJobRunApi.deleteJobRunsByType(jobType, await authService.getIcsCookie());
|
|
45
|
+
|
|
46
|
+
if (!SUCCESS_STATUSES.includes(response.status)) {
|
|
47
|
+
throw new Error(`deleteJobRunsByType failed for ${jobType}: ${JSON.stringify(response.data)}`);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
seedJobRun,
|
|
53
|
+
reclaimStaleJobRuns,
|
|
54
|
+
getJobRun,
|
|
55
|
+
deleteJobRunsByType
|
|
56
|
+
};
|
|
@@ -58,11 +58,28 @@ module.exports = {
|
|
|
58
58
|
runJobForExpireTimedOverrides: function(entityIds) {
|
|
59
59
|
return runSchedulerJob({ jobType: schedulerJobs.EXPIRE_TIMED_OVERRIDES, entityIds });
|
|
60
60
|
},
|
|
61
|
+
runJobForCertificialEnrollment: function(entityIds) {
|
|
62
|
+
requireScope(entityIds, schedulerJobs.CERTIFICIAL_ENROLLMENT);
|
|
63
|
+
return runSchedulerJob({ jobType: schedulerJobs.CERTIFICIAL_ENROLLMENT, entityIds });
|
|
64
|
+
},
|
|
65
|
+
runJobForCertificialMonitoring: function(entityIds) {
|
|
66
|
+
requireScope(entityIds, schedulerJobs.CERTIFICIAL_MONITORING);
|
|
67
|
+
return runSchedulerJob({ jobType: schedulerJobs.CERTIFICIAL_MONITORING, entityIds });
|
|
68
|
+
},
|
|
61
69
|
runAllJobsNew: function(entityIds) {
|
|
62
70
|
return runSchedulerJob({ jobType: schedulerJobs.ALL, entityIds });
|
|
63
71
|
}
|
|
64
72
|
};
|
|
65
73
|
|
|
74
|
+
/* An unscoped enrollment run emails a real broker for every eligible record in the environment and
|
|
75
|
+
cannot be undone; an unscoped monitoring run polls and notifies on every active enrollment. A
|
|
76
|
+
since: token still counts as a scope. */
|
|
77
|
+
function requireScope(entityIds, jobType) {
|
|
78
|
+
if (!entityIds || !entityIds.length) {
|
|
79
|
+
throw new Error(`${jobType} must be scoped with entityIds`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
66
83
|
function isOverloadError(errorCode) {
|
|
67
84
|
const overloadResponseErrors = [httpResponseStatusCodes.TOO_MANY_REQUESTS, httpResponseStatusCodes.CONFLICT];
|
|
68
85
|
return overloadResponseErrors.includes(errorCode);
|
|
@@ -128,14 +128,28 @@ const createPolicyFiles = async (requestId, policyFileList, context, { shouldExp
|
|
|
128
128
|
return createPolicyFilesResponse;
|
|
129
129
|
};
|
|
130
130
|
|
|
131
|
-
const
|
|
131
|
+
const uploadPolicyModels = (policyTypeList, policyFilePath, requestId, context) => {
|
|
132
|
+
return Promise.all(policyTypeList.map(policyType => uploadPolicyFileAndGetPolicyModel(requestId, policyFilePath, policyType, context)));
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const uploadAndCreatePolicyFile = async (policyTypeList, policyFilePath, requestId, context, { markAsScanned, maxRetries = 3 } = {}) => {
|
|
132
136
|
validateArray(policyTypeList);
|
|
133
137
|
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
const policyModels = await uploadPolicyModels(policyTypeList, policyFilePath, requestId, context);
|
|
139
|
+
const policyFilesResponse = await createPolicyFiles(requestId, policyModels, context);
|
|
140
|
+
|
|
141
|
+
for (let retry = 1; retry <= maxRetries; retry++) {
|
|
142
|
+
const missingPolicyTypes = policyTypeList.filter(policyType => !policyFilesResponse.data[policyType]);
|
|
137
143
|
|
|
138
|
-
|
|
144
|
+
if (!missingPolicyTypes.length) {
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.warn(`Policy types ${missingPolicyTypes} were not created for request ${requestId}, retry`);
|
|
149
|
+
const missingPolicyModels = await uploadPolicyModels(missingPolicyTypes, policyFilePath, requestId, context);
|
|
150
|
+
const retryResponse = await createPolicyFiles(requestId, missingPolicyModels, context);
|
|
151
|
+
Object.assign(policyFilesResponse.data, retryResponse.data);
|
|
152
|
+
}
|
|
139
153
|
|
|
140
154
|
if (markAsScanned) {
|
|
141
155
|
const { data: request } = await requestsApi.getRequestById(requestId, context);
|