abmp-npm 1.0.0 → 1.7.2
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/README.md +1 -0
- package/backend/consts.js +52 -0
- package/backend/index.js +6 -0
- package/backend/updateMemberData.js +210 -0
- package/backend/utils.js +134 -0
- package/index.js +5 -5
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# abmp-npm
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default display settings for member profiles
|
|
3
|
+
*/
|
|
4
|
+
const DEFAULT_MEMBER_DISPLAY_SETTINGS = {
|
|
5
|
+
showLicenseNo: true,
|
|
6
|
+
showName: true,
|
|
7
|
+
showBookingUrl: false,
|
|
8
|
+
showWebsite: false,
|
|
9
|
+
showWixUrl: true,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Address display status types
|
|
14
|
+
*/
|
|
15
|
+
const ADDRESS_STATUS_TYPES = {
|
|
16
|
+
FULL_ADDRESS: 'full_address',
|
|
17
|
+
STATE_CITY_ZIP: 'state_city_zip',
|
|
18
|
+
DONT_SHOW: 'dont_show',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Address visibility configuration options
|
|
23
|
+
*/
|
|
24
|
+
const ADDRESS_VISIBILITY_OPTIONS = {
|
|
25
|
+
ALL: 'all',
|
|
26
|
+
NONE: 'none',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const COLLECTIONS = {
|
|
30
|
+
MEMBERS_DATA: 'MembersDataLatest',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
const MEMBER_ACTIONS = {
|
|
36
|
+
UPDATE: 'update',
|
|
37
|
+
NEW: 'new',
|
|
38
|
+
DROP: 'drop',
|
|
39
|
+
NONE: 'none'
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const PRECISION = 3;
|
|
43
|
+
|
|
44
|
+
module.exports = {
|
|
45
|
+
DEFAULT_MEMBER_DISPLAY_SETTINGS,
|
|
46
|
+
ADDRESS_STATUS_TYPES,
|
|
47
|
+
ADDRESS_VISIBILITY_OPTIONS,
|
|
48
|
+
PRECISION,
|
|
49
|
+
COLLECTIONS,
|
|
50
|
+
MEMBER_ACTIONS,
|
|
51
|
+
};
|
|
52
|
+
|
package/backend/index.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { generateGeoHash } from './utils.js';
|
|
2
|
+
import {
|
|
3
|
+
ADDRESS_STATUS_TYPES,
|
|
4
|
+
DEFAULT_MEMBER_DISPLAY_SETTINGS,
|
|
5
|
+
MEMBER_ACTIONS,
|
|
6
|
+
} from './consts.js';
|
|
7
|
+
import {
|
|
8
|
+
determineAddressDisplayStatus,
|
|
9
|
+
isValidArray,
|
|
10
|
+
processInterests,
|
|
11
|
+
createFullName,
|
|
12
|
+
} from './utils.js';
|
|
13
|
+
import { findMemberById } from './utils.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Validates core member data requirements
|
|
17
|
+
* @param {Object} inputMemberData - Raw member data from API to validate
|
|
18
|
+
* @returns {boolean} - True if all required fields are valid, false otherwise
|
|
19
|
+
*/
|
|
20
|
+
const validateCoreMemberData = (inputMemberData) => {
|
|
21
|
+
// Check memberid
|
|
22
|
+
if (!inputMemberData?.memberid) {
|
|
23
|
+
console.warn('validateCoreMemberData: Missing required field - memberid is mandatory');
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Check email
|
|
28
|
+
if (!inputMemberData?.email ||
|
|
29
|
+
typeof inputMemberData.email !== 'string' ||
|
|
30
|
+
!inputMemberData.email.trim()) {
|
|
31
|
+
console.warn('validateCoreMemberData: Missing required field - email (valid string) is mandatory');
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Check memberships
|
|
36
|
+
if (!inputMemberData?.memberships || !Array.isArray(inputMemberData.memberships) || inputMemberData.memberships.length === 0) {
|
|
37
|
+
console.warn('validateCoreMemberData: Missing required field - memberships (non-empty array) is mandatory');
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return true;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Creates base member data structure with core properties
|
|
46
|
+
* @param {Object} inputMemberData - Raw member data from API
|
|
47
|
+
* @param {Object} existingDbMember - Existing member data from database
|
|
48
|
+
* @param {number} currentPageNumber - Current page number being processed
|
|
49
|
+
* @returns {Object|null} - Structured base member data or null if required fields are missing
|
|
50
|
+
*/
|
|
51
|
+
const createCoreMemberData = (
|
|
52
|
+
inputMemberData,
|
|
53
|
+
existingDbMember,
|
|
54
|
+
currentPageNumber
|
|
55
|
+
) => {
|
|
56
|
+
// Validate required fields
|
|
57
|
+
if (!validateCoreMemberData(inputMemberData)) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const sanitizedFirstName = inputMemberData.firstname?.trim() || '';
|
|
62
|
+
const sanitizedLastName = inputMemberData.lastname?.trim() || '';
|
|
63
|
+
const bookingUrl = inputMemberData.migrationData?.schedule_code?.startsWith('http') ? inputMemberData.migrationData?.schedule_code : '';
|
|
64
|
+
return {
|
|
65
|
+
...existingDbMember,
|
|
66
|
+
memberId: inputMemberData.memberid,
|
|
67
|
+
firstName: sanitizedFirstName,
|
|
68
|
+
lastName: sanitizedLastName,
|
|
69
|
+
fullName: createFullName(sanitizedFirstName, sanitizedLastName),
|
|
70
|
+
email: inputMemberData.email.trim(),
|
|
71
|
+
phones: inputMemberData.phones || [],
|
|
72
|
+
toShowPhone: inputMemberData.migrationData?.show_phone || '',
|
|
73
|
+
action: inputMemberData.action,
|
|
74
|
+
licenses: inputMemberData.licenses || [],
|
|
75
|
+
memberships: inputMemberData.memberships,
|
|
76
|
+
pageNumber: currentPageNumber,
|
|
77
|
+
optOut: inputMemberData.migrationData?.opted_out || false,
|
|
78
|
+
showABMP: inputMemberData.migrationData?.show_member_since || false,
|
|
79
|
+
locHash: generateGeoHash(inputMemberData.addresses || []),
|
|
80
|
+
...DEFAULT_MEMBER_DISPLAY_SETTINGS,
|
|
81
|
+
isVisible: inputMemberData.action !== MEMBER_ACTIONS.DROP,
|
|
82
|
+
url: inputMemberData.url,
|
|
83
|
+
bookingUrl,
|
|
84
|
+
APIBookingUrl: inputMemberData.migrationData?.schedule_code,//keeping it as a ref if in future they want original
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Enriches member data with optional migration properties
|
|
90
|
+
* @param {Object} memberDataToUpdate - Member data object to enhance
|
|
91
|
+
* @param {Object} migrationData - Migration data containing optional properties
|
|
92
|
+
*/
|
|
93
|
+
const enrichWithMigrationData = (memberDataToUpdate, migrationData) => {
|
|
94
|
+
if (!migrationData) return;
|
|
95
|
+
|
|
96
|
+
memberDataToUpdate.logoImage = migrationData.logo_url;
|
|
97
|
+
memberDataToUpdate.aboutYouHtml = migrationData.detailtext;
|
|
98
|
+
memberDataToUpdate.addressInfo = migrationData.addressinfo;
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if (migrationData.website) {
|
|
102
|
+
memberDataToUpdate.website = migrationData.website;
|
|
103
|
+
memberDataToUpdate.showWebsite = true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (migrationData.interests) {
|
|
107
|
+
memberDataToUpdate.areasOfPractices = processInterests(
|
|
108
|
+
migrationData.interests
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Processes multiple addresses with their display statuses
|
|
115
|
+
* @param {Array} addressesList - Array of address objects
|
|
116
|
+
* @param {Object} displayConfiguration - Address display configuration
|
|
117
|
+
* @returns {Array} - Processed addresses with status information
|
|
118
|
+
*/
|
|
119
|
+
const processAddressesWithStatus = (
|
|
120
|
+
addressesList,
|
|
121
|
+
displayConfiguration = {}
|
|
122
|
+
) => {
|
|
123
|
+
if (!isValidArray(addressesList)) {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return addressesList.map((address) => {
|
|
128
|
+
const displayStatus = displayConfiguration[address.key]
|
|
129
|
+
? determineAddressDisplayStatus(displayConfiguration[address.key])
|
|
130
|
+
: ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
...address,
|
|
134
|
+
addressStatus: displayStatus,
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Processes and adds address data with proper status
|
|
141
|
+
* @param {Object} memberDataToUpdate - Member data object to enhance
|
|
142
|
+
* @param {Array} addressesList - Array of address objects
|
|
143
|
+
* @param {Object} addressDisplayInfo - Address visibility configuration
|
|
144
|
+
*/
|
|
145
|
+
const enrichWithAddressData = (
|
|
146
|
+
memberDataToUpdate,
|
|
147
|
+
addressesList,
|
|
148
|
+
addressDisplayInfo
|
|
149
|
+
) => {
|
|
150
|
+
if (isValidArray(addressesList)) {
|
|
151
|
+
memberDataToUpdate.addresses = processAddressesWithStatus(
|
|
152
|
+
addressesList,
|
|
153
|
+
addressDisplayInfo
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Generates complete updated member data by combining existing and migration data
|
|
160
|
+
* @param {Object} inputMemberData - Raw member data from API
|
|
161
|
+
* @param {number} currentPageNumber - Current page number being processed
|
|
162
|
+
* @returns {Promise<Object|null>} - Complete updated member data or null if validation fails
|
|
163
|
+
*/
|
|
164
|
+
const generateUpdatedMemberData = async (
|
|
165
|
+
inputMemberData,
|
|
166
|
+
currentPageNumber,
|
|
167
|
+
isVelo = false
|
|
168
|
+
) => {
|
|
169
|
+
if (!validateCoreMemberData(inputMemberData)) {
|
|
170
|
+
throw new Error('Invalid member data: memberid, email (valid string), and memberships (array) are required');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
|
|
175
|
+
const existingDbMember = isVelo? await findMemberById(inputMemberData.memberid) : {};
|
|
176
|
+
const updatedMemberData = createCoreMemberData(
|
|
177
|
+
inputMemberData,
|
|
178
|
+
existingDbMember,
|
|
179
|
+
currentPageNumber
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// If createCoreMemberData returns null due to validation failure, return null
|
|
183
|
+
if (!updatedMemberData) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
enrichWithMigrationData(updatedMemberData, inputMemberData.migrationData);
|
|
188
|
+
|
|
189
|
+
enrichWithAddressData(
|
|
190
|
+
updatedMemberData,
|
|
191
|
+
inputMemberData.addresses,
|
|
192
|
+
inputMemberData.migrationData?.addressinfo
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
return updatedMemberData;
|
|
196
|
+
} catch (error) {
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = {
|
|
202
|
+
validateCoreMemberData,
|
|
203
|
+
createCoreMemberData,
|
|
204
|
+
enrichWithMigrationData,
|
|
205
|
+
processAddressesWithStatus,
|
|
206
|
+
enrichWithAddressData,
|
|
207
|
+
generateUpdatedMemberData,
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
|
package/backend/utils.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
const {
|
|
2
|
+
ADDRESS_STATUS_TYPES,
|
|
3
|
+
ADDRESS_VISIBILITY_OPTIONS,
|
|
4
|
+
COLLECTIONS,
|
|
5
|
+
} = require('./consts.js');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Safely adds optional properties from source to target object
|
|
9
|
+
* @param {Object} targetObject - Object to add properties to
|
|
10
|
+
* @param {Object} sourceObject - Object to get properties from
|
|
11
|
+
* @param {string} sourcePropertyKey - Key to get from source
|
|
12
|
+
* @param {string} targetPropertyKey - Key to set on target (defaults to sourcePropertyKey)
|
|
13
|
+
*/
|
|
14
|
+
const addOptionalProperty = (
|
|
15
|
+
targetObject,
|
|
16
|
+
sourceObject,
|
|
17
|
+
sourcePropertyKey,
|
|
18
|
+
targetPropertyKey = sourcePropertyKey
|
|
19
|
+
) => {
|
|
20
|
+
if (sourceObject && sourceObject[sourcePropertyKey]) {
|
|
21
|
+
targetObject[targetPropertyKey] = sourceObject[sourcePropertyKey];
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Safely trims a string value with fallback
|
|
27
|
+
* @param {string} value - The string to trim
|
|
28
|
+
* @param {string} fallback - Fallback value if input is invalid
|
|
29
|
+
* @returns {string} - The trimmed string or fallback
|
|
30
|
+
*/
|
|
31
|
+
const safeTrim = (value, fallback = '') => {
|
|
32
|
+
return value ? value.toString().trim() : fallback;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Determines address display status based on visibility settings
|
|
37
|
+
* @param {string} visibilityValue - The address visibility value from migration data
|
|
38
|
+
* @returns {string} - The corresponding address status
|
|
39
|
+
*/
|
|
40
|
+
const determineAddressDisplayStatus = (visibilityValue) => {
|
|
41
|
+
if (!visibilityValue) {
|
|
42
|
+
return ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const normalizedValue = visibilityValue.trim().toLowerCase();
|
|
46
|
+
|
|
47
|
+
switch (normalizedValue) {
|
|
48
|
+
case ADDRESS_VISIBILITY_OPTIONS.ALL:
|
|
49
|
+
return ADDRESS_STATUS_TYPES.FULL_ADDRESS;
|
|
50
|
+
case ADDRESS_VISIBILITY_OPTIONS.NONE:
|
|
51
|
+
return ADDRESS_STATUS_TYPES.DONT_SHOW;
|
|
52
|
+
default:
|
|
53
|
+
return ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Validates if input is a non-empty array
|
|
59
|
+
* @param {*} input - Input to validate
|
|
60
|
+
* @returns {boolean} - True if input is a non-empty array
|
|
61
|
+
*/
|
|
62
|
+
const isValidArray = (input) => {
|
|
63
|
+
return Array.isArray(input) && input.length > 0;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Processes interests string into clean array
|
|
68
|
+
* @param {string} interestsString - Comma-separated interests string
|
|
69
|
+
* @returns {Array} - Array of trimmed, non-empty interests
|
|
70
|
+
*/
|
|
71
|
+
const processInterests = (interestsString) => {
|
|
72
|
+
if (!interestsString) return [];
|
|
73
|
+
|
|
74
|
+
return interestsString
|
|
75
|
+
.split(',')
|
|
76
|
+
.map((interest) => interest.trim())
|
|
77
|
+
.filter((interest) => interest.length > 0);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Creates a full name from first and last name components
|
|
82
|
+
* @param {string} firstName - First name
|
|
83
|
+
* @param {string} lastName - Last name
|
|
84
|
+
* @returns {string} - Combined full name
|
|
85
|
+
*/
|
|
86
|
+
const createFullName = (firstName, lastName) => {
|
|
87
|
+
const trimmedFirst = firstName?.trim() || '';
|
|
88
|
+
const trimmedLast = lastName?.trim() || '';
|
|
89
|
+
return `${trimmedFirst} ${trimmedLast}`.trim();
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function generateGeoHash(addresses) {
|
|
93
|
+
const geohash = addresses
|
|
94
|
+
?.filter((address) =>
|
|
95
|
+
isNaN(address?.latitude) && isNaN(address?.longitude) ? false : address
|
|
96
|
+
)
|
|
97
|
+
?.map((address) =>
|
|
98
|
+
ngeohash.encode(address.latitude, address.longitude, PRECISION)
|
|
99
|
+
);
|
|
100
|
+
return geohash && geohash.length > 0 ? geohash : [];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Retrieves member data by member ID
|
|
105
|
+
* @param {string} memberId - The member ID to search for
|
|
106
|
+
* @returns {Promise<Object|null>} - Member data or null if not found
|
|
107
|
+
*/
|
|
108
|
+
const findMemberById = async (memberId) => {
|
|
109
|
+
if (!memberId) {
|
|
110
|
+
throw new Error('Member ID is required');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const queryResult = await wixData.query(COLLECTIONS.MEMBERS_DATA)
|
|
115
|
+
.eq("memberId", memberId)
|
|
116
|
+
.find({ suppressAuth: true });
|
|
117
|
+
|
|
118
|
+
return queryResult.items.length > 0 ? queryResult.items[0] : null;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
throw new Error(`Failed to retrieve member data: ${error.message}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = {
|
|
125
|
+
addOptionalProperty,
|
|
126
|
+
safeTrim,
|
|
127
|
+
determineAddressDisplayStatus,
|
|
128
|
+
isValidArray,
|
|
129
|
+
processInterests,
|
|
130
|
+
createFullName,
|
|
131
|
+
generateGeoHash,
|
|
132
|
+
findMemberById,
|
|
133
|
+
};
|
|
134
|
+
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
module.exports = {
|
|
2
|
+
...require('./public'),
|
|
3
|
+
...require('./pages'),
|
|
4
|
+
...require('./backend'),
|
|
5
|
+
};
|