sr-npm 1.0.16 → 1.0.18

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.
@@ -2,8 +2,8 @@ const COLLECTIONS = {
2
2
  AMOUNT_OF_JOBS_PER_DEPARTMENT: 'AmountOfJobsPerDepartment',
3
3
  CITIES: 'cities',
4
4
  JOBS: 'Jobs',
5
- COMPANY_ID: 'CompanyId',
6
5
  TEMPLATE_TYPE: 'templateType',
6
+ SECRET_MANAGER_MIRROR: 'SecretManagerMirror',
7
7
  }
8
8
  const JOBS_COLLECTION_FIELDS = {
9
9
  LOCATION: 'location',
@@ -62,14 +62,25 @@ const COLLECTIONS_FIELDS = {
62
62
  {key:'city', type: 'REFERENCE', typeMetadata: { reference: { referencedCollectionId: COLLECTIONS.CITIES } } },
63
63
  { key: 'image', type: 'IMAGE' },
64
64
  ],
65
- COMPANY_ID: [
66
- {key:'companyId', type: 'TEXT'},
67
- ],
68
65
  TEMPLATE_TYPE: [
69
66
  {key:'templateType', type: 'TEXT'},
70
67
  ],
68
+ SECRET_MANAGER_MIRROR: [
69
+ {key:'tokenName', type: 'TEXT'},
70
+ {key:'tokenValue', type: 'TEXT'},
71
+ ],
71
72
  };
72
73
 
74
+ const TEMPLATE_TYPE = {
75
+ INTERNAL: 'INTERNAL',
76
+ EXTERNAL: 'PUBLIC',
77
+ }
78
+
79
+ const TOKEN_NAME = {
80
+ COMPANY_ID: 'companyId',
81
+ SMART_TOKEN: 'x-smarttoken',
82
+ }
83
+
73
84
 
74
85
  module.exports = {
75
86
  COLLECTIONS,
@@ -77,4 +88,6 @@ const COLLECTIONS_FIELDS = {
77
88
  JOBS_COLLECTION_FIELDS,
78
89
  AMOUNT_OF_JOBS_PER_DEPARTMENT_COLLECTION_FIELDS,
79
90
  CITIES_COLLECTION_FIELDS,
91
+ TEMPLATE_TYPE,
92
+ TOKEN_NAME,
80
93
  };
package/backend/consts.js CHANGED
@@ -10,7 +10,7 @@ const TASKS_NAMES = {
10
10
  AGGREGATE_JOBS_BY_FIELD_TO_CMS: 'aggregateJobsByFieldToCMS',
11
11
  REFERENCE_JOBS: 'referenceJobs',
12
12
  CREATE_COLLECTIONS: 'createCollections',
13
- CREATE_COMPANY_ID_COLLECTION_AND_FILL_IT: 'createCompanyIdCollectionAndFillIt',
13
+ CREATE_SECRET_MANAGER_MIRROR_AND_FILL_IT: 'createSecretManagerMirrorAndFillIt',
14
14
  SYNC_JOBS_FAST: 'syncJobsFast',
15
15
  }
16
16
 
@@ -65,7 +65,7 @@ const TASKS = {
65
65
  },
66
66
  [TASKS_NAMES.SYNC_JOBS_FAST]: {
67
67
  name: TASKS_NAMES.SYNC_JOBS_FAST,
68
- getIdentifier:()=> "SHOULD_NEVER_SKIP",
68
+ getIdentifier:()=>"SHOULD_NEVER_SKIP",
69
69
  process:syncJobsFast,
70
70
  shouldSkipCheck:()=>false,
71
71
  estimatedDurationSec:60
@@ -74,6 +74,7 @@ const TASKS = {
74
74
 
75
75
 
76
76
 
77
+
77
78
  const TASK_TYPE = {
78
79
  SCHEDULED: 'scheduled',
79
80
  EVENT: 'event',
@@ -83,5 +84,5 @@ const TASK_TYPE = {
83
84
  TASKS_NAMES,
84
85
  TASK_TYPE,
85
86
  TASKS,
86
- QUERY_MAX_LIMIT
87
+ QUERY_MAX_LIMIT,
87
88
  };
package/backend/data.js CHANGED
@@ -1,10 +1,10 @@
1
1
  const { items: wixData } = require('@wix/data');
2
2
  const { fetchPositionsFromSRAPI, fetchJobDescription } = require('./fetchPositionsFromSRAPI');
3
3
  const { createCollectionIfMissing } = require('@hisense-staging/velo-npm/backend');
4
- const { COLLECTIONS, COLLECTIONS_FIELDS,JOBS_COLLECTION_FIELDS } = require('./collectionConsts');
4
+ const { COLLECTIONS, COLLECTIONS_FIELDS,JOBS_COLLECTION_FIELDS,TEMPLATE_TYPE,TOKEN_NAME } = require('./collectionConsts');
5
5
  const { chunkedBulkOperation, countJobsPerGivenField, fillCityLocationAndLocationAddress ,prepareToSaveArray,normalizeCityName} = require('./utils');
6
6
  const { getAllPositions } = require('./queries');
7
- const { getCompanyId } = require('./secretsData');
7
+ const { getCompanyId, getSmartToken } = require('./secretsData');
8
8
 
9
9
  function validatePosition(position) {
10
10
  if (!position.id) {
@@ -288,37 +288,13 @@ function fetchJobLocation(jobDetails) {
288
288
 
289
289
 
290
290
 
291
-
292
- async function createCompanyIdCollectionAndFillIt() {
293
- console.log("Creating CompanyId collection and filling it with the company ID");
294
- await createCollectionIfMissing(COLLECTIONS.COMPANY_ID, COLLECTIONS_FIELDS.COMPANY_ID,null,'singleItem');
295
- console.log("Getting the company ID ");
296
- const companyId = await getCompanyId();
297
- console.log("companyId is : ", companyId);
298
- console.log("Inserting the company ID into the CompanyId collection");
299
- try {
300
- await wixData.insert(COLLECTIONS.COMPANY_ID, {
301
- companyId: companyId.value
302
- });
303
- console.log("company ID inserted into the CompanyId collection");
304
- } catch (error) {
305
- if (error.message.includes("WDE0074: An item with _id [SINGLE_ITEM_ID] already exists")) {
306
- console.log("company ID already exists in the CompanyId collection");
307
- }
308
- else {
309
- throw error;
310
- }
311
- }
312
-
313
-
314
- }
315
-
316
291
  async function createCollections() {
317
292
  console.log("Creating collections");
318
293
  await Promise.all(
319
294
  [createCollectionIfMissing(COLLECTIONS.JOBS, JOBS_COLLECTION_FIELDS.JOBS,{ insert: 'ADMIN', update: 'ADMIN', remove: 'ADMIN', read: 'ANYONE' }),
320
295
  createCollectionIfMissing(COLLECTIONS.CITIES, COLLECTIONS_FIELDS.CITIES),
321
- createCollectionIfMissing(COLLECTIONS.AMOUNT_OF_JOBS_PER_DEPARTMENT, COLLECTIONS_FIELDS.AMOUNT_OF_JOBS_PER_DEPARTMENT)
296
+ createCollectionIfMissing(COLLECTIONS.AMOUNT_OF_JOBS_PER_DEPARTMENT, COLLECTIONS_FIELDS.AMOUNT_OF_JOBS_PER_DEPARTMENT),
297
+ createCollectionIfMissing(COLLECTIONS.SECRET_MANAGER_MIRROR, COLLECTIONS_FIELDS.SECRET_MANAGER_MIRROR)
322
298
  ]);
323
299
  console.log("finished creating Collections");
324
300
  }
@@ -341,9 +317,9 @@ async function referenceJobs() {
341
317
 
342
318
  async function syncJobsFast() {
343
319
  console.log("Syncing jobs fast");
344
- await createCompanyIdCollectionAndFillIt();
345
320
  await createCollections();
346
321
  await clearCollections();
322
+ await fillSecretManagerMirror();
347
323
  console.log("saving jobs data to CMS");
348
324
  await saveJobsDataToCMS();
349
325
  console.log("saved jobs data to CMS successfully");
@@ -360,19 +336,60 @@ async function clearCollections() {
360
336
  await Promise.all([
361
337
  wixData.truncate(COLLECTIONS.CITIES),
362
338
  wixData.truncate(COLLECTIONS.AMOUNT_OF_JOBS_PER_DEPARTMENT),
363
- wixData.truncate(COLLECTIONS.JOBS)
339
+ wixData.truncate(COLLECTIONS.JOBS),
340
+ wixData.truncate(COLLECTIONS.SECRET_MANAGER_MIRROR)
364
341
  ]);
365
342
  console.log("cleared collections successfully");
366
343
  }
367
344
 
345
+ async function markTemplateAsExternal() {
346
+ await createCollectionIfMissing(COLLECTIONS.TEMPLATE_TYPE, COLLECTIONS_FIELDS.TEMPLATE_TYPE,null,'singleItem');
347
+ const tempalte = await wixData.save(COLLECTIONS.TEMPLATE_TYPE, {
348
+ templateType: TEMPLATE_TYPE.EXTERNAL
349
+ });
350
+ return tempalte;
351
+ }
352
+
353
+ async function markTemplateAsInternal() {
354
+ await createCollectionIfMissing(COLLECTIONS.TEMPLATE_TYPE, COLLECTIONS_FIELDS.TEMPLATE_TYPE,null,'singleItem');
355
+ const tempalte = await wixData.save(COLLECTIONS.TEMPLATE_TYPE, {
356
+ templateType: TEMPLATE_TYPE.INTERNAL
357
+ });
358
+ return tempalte;
359
+ }
360
+
361
+ async function fillSecretManagerMirror() {
362
+ console.log("Getting the company ID ");
363
+ const companyId = await getCompanyId();
364
+ console.log("companyId is : ", companyId);
365
+ await wixData.insert(COLLECTIONS.SECRET_MANAGER_MIRROR, {
366
+ tokenName: TOKEN_NAME.COMPANY_ID,
367
+ tokenValue: companyId.value
368
+ });
369
+ console.log("companyId inserted into the SecretManagerMirror collection");
370
+ try{
371
+ const token = await getSmartToken();
372
+ await wixData.insert(COLLECTIONS.SECRET_MANAGER_MIRROR, {
373
+ tokenName: TOKEN_NAME.SMART_TOKEN,
374
+ tokenValue: token.value
375
+ });
376
+ console.log("x-smarttoken inserted into the SecretManagerMirror collection");
377
+ } catch (error) {
378
+ console.log("Error creating SecretManagerMirror collection:", error);
379
+ }
380
+ }
381
+
382
+
368
383
  module.exports = {
369
384
  syncJobsFast,
370
385
  referenceJobs,
371
386
  aggregateJobs,
372
387
  createCollections,
373
- saveJobsDataToCMS,
374
- saveJobsDescriptionsAndLocationApplyUrlToCMS,
375
- aggregateJobsByFieldToCMS,
376
- referenceJobsToField,
377
- createCompanyIdCollectionAndFillIt,
388
+ saveJobsDataToCMS,
389
+ saveJobsDescriptionsAndLocationApplyUrlToCMS,
390
+ aggregateJobsByFieldToCMS,
391
+ referenceJobsToField,
392
+ fillSecretManagerMirror,
393
+ markTemplateAsExternal,
394
+ markTemplateAsInternal,
378
395
  };
@@ -1,19 +1,23 @@
1
1
  const { fetch } = require('wix-fetch');
2
2
  const { items: wixData } = require('@wix/data');
3
- const { COLLECTIONS } = require('./collectionConsts');
4
- const secretsData = require('./secretsData');
5
- async function makeSmartRecruitersRequest(path) {
3
+ const { COLLECTIONS,TEMPLATE_TYPE,TOKEN_NAME } = require('./collectionConsts');
4
+ async function makeSmartRecruitersRequest(path,templateType) {
6
5
  const baseUrl = 'https://api.smartrecruiters.com';
7
6
  const fullUrl = `${baseUrl}${path}`;
8
7
 
9
8
  try {
9
+ const headers = {
10
+ 'Accept-Language': 'en',
11
+ 'accept': 'application/json',
12
+ 'Cookie': 'AWSALB=GYltFw3fLKortMxHR5vIOT1CuUROyhWNIX/qL8ZnPl1/8mhOcnIsBKYslzmNJPEzSy/jvNbO+6tXpH8yqcpQJagYt57MhbKlLqTSzoNq1G/w7TjOxPGR3UTdXW0d; AWSALBCORS=GYltFw3fLKortMxHR5vIOT1CuUROyhWNIX/qL8ZnPl1/8mhOcnIsBKYslzmNJPEzSy/jvNbO+6tXpH8yqcpQJagYt57MhbKlLqTSzoNq1G/w7TjOxPGR3UTdXW0d'
13
+ };
14
+ if (templateType === TEMPLATE_TYPE.INTERNAL) {
15
+ const smartToken = await getTokenFromCMS(TOKEN_NAME.SMART_TOKEN);
16
+ headers['x-smarttoken'] = smartToken;
17
+ }
10
18
  const response = await fetch(fullUrl, {
11
19
  method: 'GET',
12
- headers: {
13
- 'Accept-Language': 'en',
14
- 'accept': 'application/json',
15
- 'Cookie': 'AWSALB=GYltFw3fLKortMxHR5vIOT1CuUROyhWNIX/qL8ZnPl1/8mhOcnIsBKYslzmNJPEzSy/jvNbO+6tXpH8yqcpQJagYt57MhbKlLqTSzoNq1G/w7TjOxPGR3UTdXW0d; AWSALBCORS=GYltFw3fLKortMxHR5vIOT1CuUROyhWNIX/qL8ZnPl1/8mhOcnIsBKYslzmNJPEzSy/jvNbO+6tXpH8yqcpQJagYt57MhbKlLqTSzoNq1G/w7TjOxPGR3UTdXW0d'
16
- }
20
+ headers: headers
17
21
  });
18
22
 
19
23
  if (response.ok) {
@@ -28,25 +32,29 @@ async function makeSmartRecruitersRequest(path) {
28
32
  }
29
33
  }
30
34
 
31
- async function fetchPositionsFromSRAPI() {
35
+ async function fetchPositionsFromSRAPI(testObject=undefined) {
32
36
  let allPositions = [];
33
37
  let totalFound = 0;
34
38
  let page = 0;
35
39
  const MAX_PAGES = 30 // Safety limit to prevent infinite loops
36
- const companyId = await getCompanyIdFromCMS();
37
- const templateType = await getTemplateTypeFromCMS();
40
+
41
+ let companyId, templateType;
42
+ if (testObject) {
43
+ ({ companyId, templateType } = testObject);
44
+ } else {
45
+ ({ companyId, templateType } = await getApiKeys());
46
+ }
38
47
  console.log('Starting to fetch all positions with pagination...');
39
48
  let offset=0;
40
49
 
41
50
  do {
42
51
  try {
43
52
  page++;
44
-
45
53
  // Build the API path - first request has no page parameter, subsequent use nextPageId
46
54
  const apiPath = `/v1/companies/${companyId}/postings?offset=${offset}&destination=${templateType}`;
47
55
 
48
56
  console.log(`Fetching page ${page} with path: ${apiPath}`);
49
- const response = await makeSmartRecruitersRequest(apiPath);
57
+ const response = await makeSmartRecruitersRequest(apiPath,templateType);
50
58
 
51
59
  // Add positions from this page to our collection
52
60
  if (response.content && Array.isArray(response.content)) {
@@ -96,20 +104,20 @@ async function fetchPositionsFromSRAPI() {
96
104
  }
97
105
 
98
106
  async function fetchJobDescription(jobId) {
99
- const companyId = await getCompanyIdFromCMS();
100
- return await makeSmartRecruitersRequest(`/v1/companies/${companyId}/postings/${jobId}`);
107
+ const {companyId,templateType} = await getApiKeys();
108
+ return await makeSmartRecruitersRequest(`/v1/companies/${companyId}/postings/${jobId}`,templateType);
101
109
  }
102
110
 
103
- async function getCompanyIdFromCMS() {
104
- const result = await wixData.query(COLLECTIONS.COMPANY_ID).limit(1).find();
111
+
112
+ async function getTokenFromCMS(tokenName) {
113
+ const result = await wixData.query(COLLECTIONS.SECRET_MANAGER_MIRROR).eq('tokenName',tokenName).find();
105
114
  if (result.items.length > 0) {
106
- return result.items[0].companyId;
115
+ return result.items[0].tokenValue;
107
116
  } else {
108
- throw new Error('[getCompanyIdFromCMS], No companyId found');
117
+ throw new Error(`[getTokenFromCMS], No ${tokenName} found`);
109
118
  }
110
119
  }
111
120
  async function getTemplateTypeFromCMS() {
112
- console.log("COLLECTIONS.TEMPLATE_TYPE is ",COLLECTIONS.TEMPLATE_TYPE)
113
121
  const result = await wixData.query(COLLECTIONS.TEMPLATE_TYPE).limit(1).find();
114
122
  if (result.items.length > 0) {
115
123
  return result.items[0].templateType;
@@ -118,9 +126,15 @@ async function getTemplateTypeFromCMS() {
118
126
  }
119
127
  }
120
128
 
129
+ async function getApiKeys() {
130
+ const companyId = await getTokenFromCMS(TOKEN_NAME.COMPANY_ID);
131
+ const templateType = await getTemplateTypeFromCMS();
132
+ return {companyId,templateType};
133
+ }
121
134
 
122
135
  module.exports = {
123
136
  fetchPositionsFromSRAPI,
124
137
  fetchJobDescription,
125
- getCompanyIdFromCMS
138
+ getTokenFromCMS,
139
+ makeSmartRecruitersRequest
126
140
  };
package/backend/index.js CHANGED
@@ -3,4 +3,5 @@ module.exports = {
3
3
  ...require('./queries'),
4
4
  ...require('./fetchPositionsFromSRAPI'),
5
5
  ...require('./consts'),
6
+ ...require('./data'),
6
7
  };
@@ -2,7 +2,14 @@ const { secrets } = require("@wix/secrets");
2
2
  const { auth } = require('@wix/essentials');
3
3
 
4
4
  const getSecretValue = auth.elevate(secrets.getSecretValue);
5
-
5
+
6
+ function getSmartToken() {
7
+ return getSecretValue("x-smarttoken")
8
+ .then((secret) => {
9
+ return secret;
10
+ })
11
+ }
12
+
6
13
  function getCompanyId() {
7
14
  return getSecretValue("companyID")
8
15
  .then((secret) => {
@@ -10,5 +17,6 @@ const getSecretValue = auth.elevate(secrets.getSecretValue);
10
17
  })
11
18
  }
12
19
  module.exports = {
13
- getCompanyId
20
+ getCompanyId,
21
+ getSmartToken
14
22
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sr-npm",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,12 @@
1
+ const COLLECTIONS = {
2
+ TEMPLATE_TYPE: 'templateType',
3
+ }
4
+ const TEMPLATE_TYPE = {
5
+ INTERNAL: 'INTERNAL',
6
+ EXTERNAL: 'PUBLIC',
7
+ }
8
+
9
+ module.exports = {
10
+ COLLECTIONS,
11
+ TEMPLATE_TYPE
12
+ }
@@ -1,5 +1,5 @@
1
1
  const { executeApiRequest } = require('tests-utils');
2
- const { getRandomPosition } = require('./testsUtils');
2
+ const { getRandomPosition, executeRequestAndTest } = require('./testsUtils');
3
3
 
4
4
  describe('Job details fetch from SR API Tests', () => {
5
5
 
@@ -30,6 +30,35 @@ describe('Job details fetch from SR API Tests', () => {
30
30
  expect(jobFetchResponse.data.result.applyUrl.length).toBeGreaterThan(0);
31
31
  expect(jobFetchResponse.data.result.location).toBeDefined();
32
32
  });
33
-
34
-
35
- });
33
+ });
34
+
35
+ describe('fetchPositionsFromSRAPI error handling', () => {
36
+ test('should throw error if invalid companyId is found external template', async () => {
37
+ const requestBody = `fetchPositionsFromSRAPI({companyId: 'invalid_company_id',templateType: 'EXTERNAL'});`;
38
+ executeRequestAndTest(requestBody)
39
+ });
40
+
41
+ test('should throw error if invalid companyId is found internal template', async () => {
42
+ const requestBody = `fetchPositionsFromSRAPI({companyId: 'invalid_company_id',templateType: 'INTERNAL'});`;
43
+ executeRequestAndTest(requestBody)
44
+ });
45
+
46
+ test('should throw error when a bad URL is used', async () => {
47
+ const requestBody = `makeSmartRecruitersRequest('/v1/error/companyId/postings');`;
48
+ executeRequestAndTest(requestBody)
49
+ });
50
+
51
+
52
+
53
+ });
54
+
55
+ describe('fetchJobDescription error handling', () => {
56
+ test('should throw error if invalid jobId is given', async () => {
57
+ const requestBody = `fetchJobDescription('invalid_job_id');`;
58
+ executeRequestAndTest(requestBody)
59
+ });
60
+ test('should throw error when given a valid but wrong jobId is given', async () => {
61
+ const requestBody = `fetchJobDescription('1234567890');`;
62
+ executeRequestAndTest(requestBody)
63
+ });
64
+ });
@@ -0,0 +1,25 @@
1
+ const { executeApiRequest } = require('tests-utils');
2
+ const { clearCollections } = require('./testsUtils');
3
+ const { TEMPLATE_TYPE } = require('./consts');
4
+
5
+ beforeAll(async () => {
6
+ clearCollections();
7
+ });
8
+
9
+
10
+
11
+ it.each([
12
+ { templateName: 'Internal', templateType: TEMPLATE_TYPE.INTERNAL },
13
+ { templateName: 'External', templateType: TEMPLATE_TYPE.EXTERNAL },
14
+ ])('should successfully mark template as $templateName', async ({ templateName,templateType }) => {
15
+ const requestBody = `markTemplateAs${templateName}();`;
16
+ const response = await executeApiRequest(requestBody);
17
+ expect(response.status).toBe(200);
18
+ expect(response.data).toBeDefined();
19
+ expect(response.data.result.templateType).toBe(templateType);
20
+ });
21
+
22
+
23
+
24
+
25
+
@@ -1,7 +1,28 @@
1
+ const { executeApiRequest } = require('tests-utils');
2
+ const { COLLECTIONS } = require('./consts');
3
+ const { items: wixData } = require('@wix/data');
4
+
1
5
  function getRandomPosition(positions) {
2
6
  return positions[Math.floor(Math.random() * positions.length)];
3
7
  }
8
+ async function executeRequestAndTest(requestBody) {
9
+ try{
10
+ response = await executeApiRequest(requestBody);
11
+ expect(response.status).not.toBe(500);
12
+ }catch(error){
13
+ expect(error.message).toBe('Request failed with status code 500');
14
+ }
15
+ }
16
+
17
+ async function clearCollections() {
18
+
19
+ for (const collection of Object.values(COLLECTIONS)) {
20
+ await wixData.truncate(collection);
21
+ }
22
+ }
4
23
 
5
24
  module.exports = {
6
- getRandomPosition
25
+ getRandomPosition,
26
+ executeRequestAndTest,
27
+ clearCollections
7
28
  }