abmp-npm 1.8.30 → 1.8.32
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/backend/cms-data-methods.js +32 -2
- package/backend/consts.js +10 -2
- package/backend/contacts-methods.js +130 -0
- package/backend/daily-pull/bulk-process-methods.js +65 -0
- package/backend/daily-pull/consts.js +34 -0
- package/backend/daily-pull/index.js +4 -0
- package/backend/daily-pull/process-member-methods.js +290 -0
- package/backend/daily-pull/sync-to-cms-methods.js +114 -0
- package/backend/daily-pull/utils.js +78 -0
- package/backend/index.js +7 -2
- package/backend/jobs.js +30 -0
- package/backend/members-area-methods.js +48 -1
- package/backend/members-data-methods.js +95 -109
- package/backend/pac-api-methods.js +35 -0
- package/backend/tasks.js +37 -0
- package/backend/utils.js +74 -41
- package/eslint.config.js +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const { taskManager } = require('psdev-task-manager');
|
|
2
|
+
|
|
3
|
+
const { TASKS_NAMES } = require('../consts');
|
|
4
|
+
const { fetchPACMembers } = require('../pac-api-methods');
|
|
5
|
+
|
|
6
|
+
const { bulkProcessAndSaveMemberData } = require('./bulk-process-methods');
|
|
7
|
+
const { isUpdatedMember, isABMPMember } = require('./utils');
|
|
8
|
+
|
|
9
|
+
async function syncMembersDataPerAction(action) {
|
|
10
|
+
try {
|
|
11
|
+
const firstPageResponse = await fetchPACMembers(1, action);
|
|
12
|
+
|
|
13
|
+
if (
|
|
14
|
+
!firstPageResponse ||
|
|
15
|
+
!firstPageResponse.results ||
|
|
16
|
+
firstPageResponse.results.length === 0
|
|
17
|
+
) {
|
|
18
|
+
return {
|
|
19
|
+
success: true,
|
|
20
|
+
totalPagesProcessed: 0,
|
|
21
|
+
lastPageProcessed: 0,
|
|
22
|
+
completedAt: new Date().toISOString(),
|
|
23
|
+
message: 'No data found',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Calculate total pages from API response
|
|
28
|
+
const totalResults = firstPageResponse.total_results || 0;
|
|
29
|
+
const perPage = firstPageResponse.results.length;
|
|
30
|
+
const totalPages = firstPageResponse.total_pages || 0;
|
|
31
|
+
|
|
32
|
+
// Cap at 1000 pages as safety measure
|
|
33
|
+
const pagesToProcess = Math.min(totalPages, 1000);
|
|
34
|
+
|
|
35
|
+
console.log(
|
|
36
|
+
`Scheduling ${pagesToProcess} pages for processing (${totalResults} total records, ${perPage} per page)`
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Schedule tasks for all pages at once
|
|
40
|
+
const toScheduleTasks = Array.from(
|
|
41
|
+
{ length: pagesToProcess },
|
|
42
|
+
(_, i) => i + 1 // API expects page number to start from 1
|
|
43
|
+
).map(pageNumber => ({
|
|
44
|
+
name: TASKS_NAMES.SyncMembers,
|
|
45
|
+
data: {
|
|
46
|
+
pageNumber,
|
|
47
|
+
action,
|
|
48
|
+
},
|
|
49
|
+
type: 'scheduled',
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
// Wait for all scheduling to complete
|
|
53
|
+
await taskManager().scheduleInBulk(toScheduleTasks);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
totalPagesProcessed: pagesToProcess,
|
|
58
|
+
lastPageProcessed: pagesToProcess,
|
|
59
|
+
totalRecords: totalResults,
|
|
60
|
+
recordsPerPage: perPage,
|
|
61
|
+
completedAt: new Date().toISOString(),
|
|
62
|
+
};
|
|
63
|
+
} catch (error) {
|
|
64
|
+
throw new Error(`Synchronization failed: ${error.message}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Synchronizes a single page of member data
|
|
70
|
+
* @param {Object} taskObject - Task object containing page data
|
|
71
|
+
* @returns {Promise<Object>} - Page synchronization result
|
|
72
|
+
*/
|
|
73
|
+
async function synchronizeSinglePage(taskObject) {
|
|
74
|
+
const { pageNumber, action } = taskObject.data;
|
|
75
|
+
try {
|
|
76
|
+
const memberDataResponse = await fetchPACMembers(pageNumber, action);
|
|
77
|
+
|
|
78
|
+
if (
|
|
79
|
+
!memberDataResponse ||
|
|
80
|
+
!memberDataResponse.results ||
|
|
81
|
+
memberDataResponse.results.length === 0
|
|
82
|
+
) {
|
|
83
|
+
throw new Error(`No data found for page ${pageNumber}`);
|
|
84
|
+
}
|
|
85
|
+
const toSyncMembers = memberDataResponse.results.filter(
|
|
86
|
+
member => isUpdatedMember(member) && isABMPMember(member)
|
|
87
|
+
);
|
|
88
|
+
if (toSyncMembers.length === 0) {
|
|
89
|
+
return {
|
|
90
|
+
success: true,
|
|
91
|
+
pageNumber,
|
|
92
|
+
totalPageSize: memberDataResponse.results.length,
|
|
93
|
+
filteredPageSize: toSyncMembers.length,
|
|
94
|
+
message: 'No to be updated, or ABMP members found',
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const result = await bulkProcessAndSaveMemberData(toSyncMembers, pageNumber);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
success: true,
|
|
101
|
+
pageNumber,
|
|
102
|
+
totalPageSize: memberDataResponse.results.length,
|
|
103
|
+
filteredPageSize: toSyncMembers.length,
|
|
104
|
+
...result,
|
|
105
|
+
};
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw new Error(`Page ${pageNumber} synchronization failed: ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = {
|
|
112
|
+
syncMembersDataPerAction,
|
|
113
|
+
synchronizeSinglePage,
|
|
114
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const { updateWixMemberLoginEmail } = require('../members-area-methods');
|
|
2
|
+
|
|
3
|
+
const { MEMBER_ACTIONS } = require('./consts');
|
|
4
|
+
|
|
5
|
+
const isUpdatedMember = member => member.action !== MEMBER_ACTIONS.NONE;
|
|
6
|
+
const isABMPMember = member =>
|
|
7
|
+
member.memberships.some(membership => membership.association === 'ABMP');
|
|
8
|
+
|
|
9
|
+
const changeWixMembersEmails = async toChangeWixMembersEmails => {
|
|
10
|
+
console.log(
|
|
11
|
+
`Changing login emails for ${toChangeWixMembersEmails.length} members with ids: [${toChangeWixMembersEmails.map(member => member.memberId).join(', ')}]`
|
|
12
|
+
);
|
|
13
|
+
return await Promise.all(
|
|
14
|
+
toChangeWixMembersEmails.map(member => updateWixMemberLoginEmail(member, {}))
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Validates core member data requirements
|
|
20
|
+
* @param {Object} inputMemberData - Raw member data from API to validate
|
|
21
|
+
* @returns {boolean} - True if all required fields are valid, false otherwise
|
|
22
|
+
*/
|
|
23
|
+
const validateCoreMemberData = inputMemberData => {
|
|
24
|
+
// Check memberid
|
|
25
|
+
if (!inputMemberData?.memberid) {
|
|
26
|
+
console.warn('validateCoreMemberData: Missing required field - memberid is mandatory');
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check email
|
|
31
|
+
if (
|
|
32
|
+
!inputMemberData?.email ||
|
|
33
|
+
typeof inputMemberData.email !== 'string' ||
|
|
34
|
+
!inputMemberData.email.trim()
|
|
35
|
+
) {
|
|
36
|
+
console.warn(
|
|
37
|
+
'validateCoreMemberData: Missing required field - email (valid string) is mandatory'
|
|
38
|
+
);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check memberships
|
|
43
|
+
if (
|
|
44
|
+
!inputMemberData?.memberships ||
|
|
45
|
+
!Array.isArray(inputMemberData.memberships) ||
|
|
46
|
+
inputMemberData.memberships.length === 0
|
|
47
|
+
) {
|
|
48
|
+
console.warn(
|
|
49
|
+
'validateCoreMemberData: Missing required field - memberships (non-empty array) is mandatory'
|
|
50
|
+
);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return true;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const containsNonEnglish = str => /[^a-zA-Z0-9]/.test(str); // if it contains any non-english characters, test1 is allowed, but any others are not
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Creates a full name from first and last name components
|
|
61
|
+
* @param {string} firstName - First name
|
|
62
|
+
* @param {string} lastName - Last name
|
|
63
|
+
* @returns {string} - Combined full name
|
|
64
|
+
*/
|
|
65
|
+
const createFullName = (firstName, lastName) => {
|
|
66
|
+
const trimmedFirst = firstName?.trim() || '';
|
|
67
|
+
const trimmedLast = lastName?.trim() || '';
|
|
68
|
+
return `${trimmedFirst} ${trimmedLast}`.trim();
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
isUpdatedMember,
|
|
73
|
+
isABMPMember,
|
|
74
|
+
changeWixMembersEmails,
|
|
75
|
+
validateCoreMemberData,
|
|
76
|
+
containsNonEnglish,
|
|
77
|
+
createFullName,
|
|
78
|
+
};
|
package/backend/index.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
...require('./forms-methods'),
|
|
3
3
|
...require('./search-filters-methods'),
|
|
4
|
-
...require('./
|
|
5
|
-
...require('./utils'),
|
|
4
|
+
...require('./jobs'),
|
|
5
|
+
...require('./utils'), //TODO: remove it once we finish NPM movement
|
|
6
|
+
...require('./daily-pull'), //TODO: remove it once we finish NPM movement
|
|
7
|
+
...require('./pac-api-methods'), //TODO: remove it once we finish NPM movement
|
|
8
|
+
...require('./members-area-methods'), //TODO: remove it once we finish NPM movement
|
|
9
|
+
...require('./members-data-methods'), //TODO: remove it once we finish NPM movement
|
|
10
|
+
...require('./cms-data-methods'), //TODO: remove it once we finish NPM movement
|
|
6
11
|
};
|
package/backend/jobs.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const { taskManager } = require('psdev-task-manager');
|
|
2
|
+
|
|
3
|
+
const { TASKS_NAMES } = require('./consts');
|
|
4
|
+
const { TASKS } = require('./tasks');
|
|
5
|
+
|
|
6
|
+
async function runScheduledTasks() {
|
|
7
|
+
try {
|
|
8
|
+
console.log('runScheduledTasks started');
|
|
9
|
+
return await taskManager().runScheduledTasks(TASKS);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
console.error(`Failed to runScheduledTasks: ${error.message}`);
|
|
12
|
+
throw new Error(`Failed to runScheduledTasks: ${error.message}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function scheduleDailyPullTask() {
|
|
17
|
+
try {
|
|
18
|
+
console.log('scheduleDailyPullTask started!');
|
|
19
|
+
return await taskManager().schedule({
|
|
20
|
+
name: TASKS_NAMES.ScheduleDailyMembersDataSync,
|
|
21
|
+
data: {},
|
|
22
|
+
type: 'scheduled',
|
|
23
|
+
});
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error(`Failed to scheduleDailyPullTask: ${error.message}`);
|
|
26
|
+
throw new Error(`Failed to scheduleDailyPullTask: ${error.message}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { runScheduledTasks, scheduleDailyPullTask };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { auth } = require('@wix/essentials');
|
|
2
|
-
const { members } = require('@wix/members');
|
|
2
|
+
const { members, authentication } = require('@wix/members');
|
|
3
3
|
const elevatedCreateMember = auth.elevate(members.createMember);
|
|
4
4
|
|
|
5
5
|
function prepareContactData(partner) {
|
|
@@ -36,7 +36,54 @@ const getCurrentMember = async () => {
|
|
|
36
36
|
return member.member;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Updates Wix member login email if the member has a contactId (registered Wix member)
|
|
41
|
+
* @param {Object} member - Member object with contactId and email
|
|
42
|
+
* @param {Object} result - Result object to track Wix member updates
|
|
43
|
+
*/
|
|
44
|
+
async function updateWixMemberLoginEmail(member, result = {}) {
|
|
45
|
+
if (!member.contactId) {
|
|
46
|
+
console.log(`Member ${member.memberId} has no contactId - skipping Wix login email update`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
console.log(
|
|
52
|
+
`Updating Wix login email for member ${member.memberId} (contactId: ${member.contactId})`
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const updatedWixMember = await authentication.changeLoginEmail(member.contactId, member.email);
|
|
56
|
+
|
|
57
|
+
console.log(
|
|
58
|
+
`✅ Successfully updated Wix login email for member ${member.memberId}: ${updatedWixMember.loginEmail}`
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (!result.wixMemberUpdates) {
|
|
62
|
+
result.wixMemberUpdates = { successful: 0, failed: 0 };
|
|
63
|
+
}
|
|
64
|
+
result.wixMemberUpdates.successful++;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error(`❌ Failed to update Wix login email for member ${member.memberId}:`, error);
|
|
67
|
+
|
|
68
|
+
if (!result.wixMemberUpdates) {
|
|
69
|
+
result.wixMemberUpdates = { successful: 0, failed: 0 };
|
|
70
|
+
}
|
|
71
|
+
result.wixMemberUpdates.failed++;
|
|
72
|
+
|
|
73
|
+
if (!result.wixMemberErrors) {
|
|
74
|
+
result.wixMemberErrors = [];
|
|
75
|
+
}
|
|
76
|
+
result.wixMemberErrors.push({
|
|
77
|
+
memberId: member.memberId,
|
|
78
|
+
contactId: member.contactId,
|
|
79
|
+
email: member.email,
|
|
80
|
+
error: error.message,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
39
85
|
module.exports = {
|
|
40
86
|
createSiteMember,
|
|
41
87
|
getCurrentMember,
|
|
88
|
+
updateWixMemberLoginEmail,
|
|
42
89
|
};
|
|
@@ -1,22 +1,19 @@
|
|
|
1
|
-
const { contacts } = require('@wix/crm');
|
|
2
|
-
const { auth } = require('@wix/essentials');
|
|
3
|
-
|
|
4
1
|
const { COLLECTIONS } = require('../public/consts');
|
|
5
2
|
|
|
6
3
|
const { MEMBER_ACTIONS } = require('./consts');
|
|
4
|
+
const { updateMemberContactInfo } = require('./contacts-methods');
|
|
7
5
|
const { wixData } = require('./elevated-modules');
|
|
8
6
|
const { createSiteMember, getCurrentMember } = require('./members-area-methods');
|
|
9
7
|
const {
|
|
8
|
+
createBatches,
|
|
9
|
+
normalizeUrlForComparison,
|
|
10
|
+
queryAllItems,
|
|
10
11
|
formatDateToMonthYear,
|
|
11
12
|
getAddressDisplayOptions,
|
|
12
13
|
isStudent,
|
|
13
|
-
generateGeoHash,
|
|
14
14
|
urlExists,
|
|
15
|
+
generateGeoHash,
|
|
15
16
|
} = require('./utils');
|
|
16
|
-
|
|
17
|
-
const elevatedGetContact = auth.elevate(contacts.getContact);
|
|
18
|
-
const elevatedUpdateContact = auth.elevate(contacts.updateContact);
|
|
19
|
-
|
|
20
17
|
/**
|
|
21
18
|
* Retrieves member data by member ID
|
|
22
19
|
* @param {string} memberId - The member ID to search for
|
|
@@ -136,126 +133,112 @@ async function validateMemberToken(memberIdInput) {
|
|
|
136
133
|
}
|
|
137
134
|
}
|
|
138
135
|
|
|
139
|
-
/**
|
|
140
|
-
*
|
|
141
|
-
* @
|
|
142
|
-
* @param {function} updateInfoCallback - Function that returns the updated info object
|
|
143
|
-
* @param {string} operationName - Name of the operation for logging
|
|
136
|
+
/** Performs bulk save operation for member data
|
|
137
|
+
* @param { Array } memberDataList - Array of member data objects to save
|
|
138
|
+
* @returns { Promise < Object >} - Bulk save operation result
|
|
144
139
|
*/
|
|
145
|
-
async function
|
|
146
|
-
if (!
|
|
147
|
-
throw new Error('
|
|
140
|
+
async function bulkSaveMembers(memberDataList) {
|
|
141
|
+
if (!Array.isArray(memberDataList) || memberDataList.length === 0) {
|
|
142
|
+
throw new Error('Invalid member data list provided for bulk save');
|
|
148
143
|
}
|
|
149
144
|
|
|
150
145
|
try {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
await elevatedUpdateContact(contactId, { info: updatedInfo }, contact.revision);
|
|
146
|
+
// bulkSave all with batches of 1000 items as this is the Velo limit for bulkSave
|
|
147
|
+
const batches = createBatches(memberDataList, 1000);
|
|
148
|
+
return await Promise.all(
|
|
149
|
+
batches.map(batch => wixData.bulkSave(COLLECTIONS.MEMBERS_DATA, batch))
|
|
150
|
+
);
|
|
157
151
|
} catch (error) {
|
|
158
|
-
console.error(
|
|
159
|
-
throw new Error(`
|
|
152
|
+
console.error('Error bulk saving members:', error);
|
|
153
|
+
throw new Error(`Bulk save failed: ${error.message}`);
|
|
160
154
|
}
|
|
161
155
|
}
|
|
162
156
|
|
|
163
157
|
/**
|
|
164
|
-
*
|
|
165
|
-
* @param {string}
|
|
166
|
-
* @
|
|
158
|
+
* Retrieves member data by member ID
|
|
159
|
+
* @param {string} memberId - The member ID to search for
|
|
160
|
+
* @returns {Promise<Object|null>} - Member data or null if not found
|
|
167
161
|
*/
|
|
168
|
-
async function
|
|
169
|
-
if (!
|
|
170
|
-
throw new Error('
|
|
162
|
+
async function findMemberById(memberId) {
|
|
163
|
+
if (!memberId) {
|
|
164
|
+
throw new Error('Member ID is required');
|
|
171
165
|
}
|
|
172
166
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
items: [
|
|
179
|
-
{
|
|
180
|
-
email: newEmail,
|
|
181
|
-
primary: true,
|
|
182
|
-
},
|
|
183
|
-
],
|
|
184
|
-
},
|
|
185
|
-
}),
|
|
186
|
-
'update contact email'
|
|
187
|
-
);
|
|
188
|
-
}
|
|
167
|
+
try {
|
|
168
|
+
const queryResult = await wixData
|
|
169
|
+
.query(COLLECTIONS.MEMBERS_DATA)
|
|
170
|
+
.eq('memberId', memberId)
|
|
171
|
+
.find();
|
|
189
172
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
* @param {string} firstName - The new first name
|
|
194
|
-
* @param {string} lastName - The new last name
|
|
195
|
-
*/
|
|
196
|
-
async function updateContactNames(contactId, firstName, lastName) {
|
|
197
|
-
if (!firstName && !lastName) {
|
|
198
|
-
throw new Error('At least one name field is required');
|
|
173
|
+
return queryResult.items.length > 0 ? queryResult.items[0] : null;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
throw new Error(`Failed to retrieve member data: ${error.message}`);
|
|
199
176
|
}
|
|
200
|
-
|
|
201
|
-
return await updateContactInfo(
|
|
202
|
-
contactId,
|
|
203
|
-
currentInfo => ({
|
|
204
|
-
...currentInfo,
|
|
205
|
-
name: {
|
|
206
|
-
first: firstName || currentInfo?.name?.first || '',
|
|
207
|
-
last: lastName || currentInfo?.name?.last || '',
|
|
208
|
-
},
|
|
209
|
-
}),
|
|
210
|
-
'update contact names'
|
|
211
|
-
);
|
|
212
177
|
}
|
|
213
178
|
|
|
214
179
|
/**
|
|
215
|
-
*
|
|
216
|
-
* @param {
|
|
217
|
-
* @param {
|
|
218
|
-
* @param {
|
|
219
|
-
* @param {
|
|
180
|
+
* Method to get member by slug with flexible filtering options
|
|
181
|
+
* @param {Object} options - Query options
|
|
182
|
+
* @param {string} options.slug - The slug to search for
|
|
183
|
+
* @param {boolean} options.excludeDropped - Whether to exclude dropped members (default: true)
|
|
184
|
+
* @param {boolean} options.excludeSearchedMember - Whether to exclude a specific member (default: false)
|
|
185
|
+
* @param {string|number} [options.memberId] - Member ID to exclude when excludeSearchedMember is true (optional)
|
|
186
|
+
* @param {boolean} [options.queryAllMatches=false] - Whether to query all matches or just the first one (default: false)
|
|
187
|
+
* @returns {Promise<Object|null>} - Member data or null if not found
|
|
220
188
|
*/
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
189
|
+
async function getMemberBySlug({
|
|
190
|
+
slug,
|
|
191
|
+
excludeDropped = true,
|
|
192
|
+
excludeSearchedMember = false,
|
|
193
|
+
memberId = null,
|
|
194
|
+
queryAllMatches = false,
|
|
195
|
+
}) {
|
|
196
|
+
if (!slug) return null;
|
|
226
197
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
198
|
+
try {
|
|
199
|
+
let query = wixData.query(COLLECTIONS.MEMBERS_DATA).contains('url', slug);
|
|
200
|
+
|
|
201
|
+
if (excludeDropped) {
|
|
202
|
+
query = query.ne('action', 'drop');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (excludeSearchedMember && memberId) {
|
|
206
|
+
query = query.ne('memberId', memberId);
|
|
207
|
+
}
|
|
208
|
+
let membersList;
|
|
209
|
+
if (queryAllMatches) {
|
|
210
|
+
query = query.limit(1000);
|
|
211
|
+
membersList = await queryAllItems(query);
|
|
212
|
+
} else {
|
|
213
|
+
membersList = await query.find().then(res => res.items);
|
|
214
|
+
}
|
|
215
|
+
let matchingMembers = membersList.filter(
|
|
216
|
+
item => item.url && item.url.toLowerCase() === slug.toLowerCase()
|
|
217
|
+
);
|
|
218
|
+
if (queryAllMatches) {
|
|
219
|
+
matchingMembers = membersList
|
|
220
|
+
.filter(
|
|
221
|
+
//remove trailing "-1", "-2", etc.
|
|
222
|
+
item => item.url && normalizeUrlForComparison(item.url) === slug.toLowerCase()
|
|
223
|
+
)
|
|
224
|
+
.sort((a, b) => b.url.toLowerCase().localeCompare(a.url.toLowerCase()));
|
|
225
|
+
}
|
|
226
|
+
if (matchingMembers.length > 1) {
|
|
227
|
+
const queryResultMsg = `Multiple members found with same slug ${slug} membersIds are : [${matchingMembers
|
|
228
|
+
.map(member => member.memberId)
|
|
229
|
+
.join(', ')}]`;
|
|
230
|
+
if (!queryAllMatches) {
|
|
231
|
+
throw new Error(queryResultMsg);
|
|
232
|
+
} else {
|
|
233
|
+
console.log(queryResultMsg);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return matchingMembers[0] || null;
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.error('Error getting member by slug:', error);
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
259
242
|
|
|
260
243
|
/**
|
|
261
244
|
* Saves member registration data
|
|
@@ -305,4 +288,7 @@ module.exports = {
|
|
|
305
288
|
createContactAndMemberIfNew,
|
|
306
289
|
validateMemberToken,
|
|
307
290
|
saveRegistrationData,
|
|
291
|
+
bulkSaveMembers,
|
|
292
|
+
findMemberById,
|
|
293
|
+
getMemberBySlug,
|
|
308
294
|
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const { secrets } = require('@wix/secrets');
|
|
2
|
+
|
|
3
|
+
const { PAC_API_URL } = require('./daily-pull/consts');
|
|
4
|
+
|
|
5
|
+
const getHeaders = async () => {
|
|
6
|
+
const AUTH_TOKEN = await secrets.getSecretValue('members-data-api-key');
|
|
7
|
+
const headers = {
|
|
8
|
+
Authorization: `Bearer ${AUTH_TOKEN}`,
|
|
9
|
+
};
|
|
10
|
+
return headers;
|
|
11
|
+
};
|
|
12
|
+
const fetchPACMembers = async (pageNum, actionFilter) => {
|
|
13
|
+
const url = `${PAC_API_URL}/Members?page=${pageNum}&actionFilter=${actionFilter}`;
|
|
14
|
+
const headers = await getHeaders();
|
|
15
|
+
const fetchOptions = {
|
|
16
|
+
method: 'get',
|
|
17
|
+
headers: headers,
|
|
18
|
+
};
|
|
19
|
+
const response = await fetch(url, fetchOptions);
|
|
20
|
+
const responseType = response.headers.get('content-type');
|
|
21
|
+
if (!responseType.includes('application/json')) {
|
|
22
|
+
const errorMessage = `[fetchPACMembers] got invalid responseType: ${responseType} for page ${pageNum} and actionFilter ${actionFilter}`;
|
|
23
|
+
console.error(errorMessage);
|
|
24
|
+
throw new Error(errorMessage);
|
|
25
|
+
}
|
|
26
|
+
if (response.ok) {
|
|
27
|
+
return response.json();
|
|
28
|
+
} else {
|
|
29
|
+
const errorMessage = `[fetchPACMembers] failed with status ${response.status} for page ${pageNum} and actionFilter ${actionFilter}`;
|
|
30
|
+
console.error(errorMessage);
|
|
31
|
+
throw new Error(errorMessage);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
module.exports = { fetchPACMembers, getHeaders }; //TODO: remove getHeaders from exported methods once npm movement finishes
|
package/backend/tasks.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const { TASKS_NAMES } = require('./consts');
|
|
2
|
+
const { MEMBER_ACTIONS, synchronizeSinglePage, syncMembersDataPerAction } = require('./daily-pull');
|
|
3
|
+
|
|
4
|
+
const getDailyMembersDataSyncChildTasks = () => {
|
|
5
|
+
// we don't want to sync none action as it means this members data hasn't changed and we don't need to sync it
|
|
6
|
+
const MEMBER_ACTIONS_EXCEPT_NONE = Object.values(MEMBER_ACTIONS).filter(
|
|
7
|
+
action => action !== MEMBER_ACTIONS.NONE
|
|
8
|
+
);
|
|
9
|
+
return MEMBER_ACTIONS_EXCEPT_NONE.map(action => ({
|
|
10
|
+
name: TASKS_NAMES.ScheduleMembersDataPerAction,
|
|
11
|
+
data: { action },
|
|
12
|
+
}));
|
|
13
|
+
};
|
|
14
|
+
const TASKS = {
|
|
15
|
+
[TASKS_NAMES.ScheduleDailyMembersDataSync]: {
|
|
16
|
+
name: TASKS_NAMES.ScheduleDailyMembersDataSync,
|
|
17
|
+
scheduleChildrenSequentially: false,
|
|
18
|
+
estimatedDurationSec: 60,
|
|
19
|
+
childTasks: getDailyMembersDataSyncChildTasks(),
|
|
20
|
+
},
|
|
21
|
+
[TASKS_NAMES.ScheduleMembersDataPerAction]: {
|
|
22
|
+
name: TASKS_NAMES.ScheduleMembersDataPerAction,
|
|
23
|
+
getIdentifier: task => task.data.action,
|
|
24
|
+
process: syncMembersDataPerAction,
|
|
25
|
+
shouldSkipCheck: () => false,
|
|
26
|
+
estimatedDurationSec: 6,
|
|
27
|
+
},
|
|
28
|
+
[TASKS_NAMES.SyncMembers]: {
|
|
29
|
+
name: TASKS_NAMES.SyncMembers,
|
|
30
|
+
getIdentifier: task => task,
|
|
31
|
+
process: synchronizeSinglePage,
|
|
32
|
+
shouldSkipCheck: () => false,
|
|
33
|
+
estimatedDurationSec: 6,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = { TASKS };
|