@plusscommunities/pluss-maintenance-aws 2.1.38 → 2.1.39

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.
@@ -10,291 +10,289 @@ const indexQuery = require("@plusscommunities/pluss-core-aws/db/common/indexQuer
10
10
  const { values } = require("../../values.config");
11
11
 
12
12
  class SeeStuffStrategy extends IntegrationStrategy {
13
- constructor(config) {
14
- super();
15
- this.baseUrl = config.BaseUrl; // base URL for the API
16
- this.apiKeyHeader = config.APIKeyHeader; // header to use for API key
17
- this.apiKey = config.APIKey; // API key
18
-
19
- const url = new URL(this.baseUrl);
20
- this.host = url.host;
21
-
22
- this.siteMap = config.SiteMap ?? {};
23
- }
24
-
25
- /**
26
- * Gets the entity type for the integration
27
- *
28
- * @returns {String} The entity type for the integration
29
- */
30
- getEntityType = () => {
31
- return `${values.serviceKey}_SeeStuff`;
32
- };
33
-
34
- /**
35
- * Validates the integration
36
- *
37
- * @returns {Boolean} Whether the integration is valid
38
- */
39
- isValidIntegration = () => {
40
- return true;
41
- };
42
-
43
- /**
44
- * Gets the refresh interval for the SeeStuff system
45
- *
46
- * @returns {Number} The refresh interval in milliseconds
47
- */
48
- getRefreshInterval = () => {
49
- return Number.MAX_SAFE_INTEGER; // should never refresh
50
- };
51
-
52
- /**
53
- * Creates a request in the SeeStuff system
54
- *
55
- * @param {Object} request - Request definition on Pluss
56
- * @param {Object} mockResponse - Mock response from SeeStuff to simulate response
57
- * @returns {Boolean} Whether the request was created on SeeStuff
58
- */
59
- createRequest = async (request, mockResponse = null) => {
60
- const logId = log("SeeStuff:CreateRequest", "Start", request);
61
-
62
- try {
63
- // Get user details for room/address and CRMID
64
- let userRoom = request.room || "";
65
- let crmResidentId = "";
66
-
67
- // Map site to location code if available
68
- const location = this.siteMap[request.site];
69
- if (!location) {
70
- log("SeeStuff:CreateRequest", "LocationNotFound", request.site, logId);
71
- return false;
72
- }
73
-
74
- if (request.userID) {
75
- try {
76
- // Look up user from users table
77
- const user = await getRef("users", "Id", request.userID);
78
- log("SeeStuff:CreateRequest", "User", user, logId);
79
-
80
- if (user) {
81
- // Get user's address/room
82
- if (user.unit) {
83
- userRoom = user.unit;
84
- }
85
-
86
- // Get CRMID from user fields
87
- try {
88
- const crmIdField = await getRef(
89
- "userfields",
90
- "RowId",
91
- getRowId(request.userID, "CRMID")
92
- );
93
- if (crmIdField && crmIdField.Value) {
94
- crmResidentId = crmIdField.Value;
95
- }
96
- log("SeeStuff:CreateRequest", "CRMID", crmResidentId, logId);
97
- } catch (fieldError) {
98
- log(
99
- "SeeStuff:CreateRequest",
100
- "CRMIDLookupError",
101
- fieldError,
102
- logId
103
- );
104
- }
105
- }
106
- } catch (userError) {
107
- log("SeeStuff:CreateRequest", "UserLookupError", userError, logId);
108
- }
109
- }
110
-
111
- if (_.isEmpty(crmResidentId)) {
112
- // log an issue in the history
113
- throw new Error("NoCRMResidentId");
114
- }
115
-
116
- // Format description similar to Archibus
117
- const description = `${request.title}${
118
- _.isEmpty(request.description) ? "" : `\n\n${request.description}`
119
- }${_.isEmpty(request.room) ? "" : `\n\nLocation: ${request.room}`}${
120
- _.isEmpty(request.userName) ? "" : `\n\nName: ${request.userName}`
121
- }${_.isEmpty(request.phone) ? "" : `\n\nPhone: ${request.phone}`}${
122
- _.isEmpty(request.type) ? "" : `\n\nJob Type: ${request.type}`
123
- }${
124
- _.isEmpty(request.images)
125
- ? ""
126
- : `\n\nImages: ${request.images.join("\n")}`
127
- }`;
128
-
129
- // Format date_reported
130
- const dateReported = moment().format("YYYY-MM-DD HH:mm:ss");
131
-
132
- // Build payload
133
- const data = {
134
- externalId: request.id,
135
- description: description,
136
- priority: "P3",
137
- location: location,
138
- room: userRoom,
139
- date_reported: dateReported,
140
- crmResidentId: crmResidentId,
141
- };
142
-
143
- log("SeeStuff:CreateRequest", "Request", data, logId);
144
-
145
- const response =
146
- mockResponse ??
147
- (await axios({
148
- method: "POST",
149
- url: `${this.baseUrl}/assetmgmt/servicerequests`,
150
- timeout: 30000,
151
- headers: {
152
- [this.apiKeyHeader]: this.apiKey,
153
- "Content-Type": "application/json",
154
- Host: this.host,
155
- },
156
- data,
157
- }));
158
-
159
- log("SeeStuff:CreateRequest", "Response", response.data, logId);
160
-
161
- return true;
162
- } catch (e) {
163
- log("SeeStuff:CreateRequest", "Error", e, logId);
164
-
165
- // Add history entry for failed integration
166
- try {
167
- const failedJob = await getRef(
168
- values.tableNameMaintenance,
169
- "id",
170
- request.id
171
- );
172
- if (!failedJob.history) failedJob.history = [];
173
- failedJob.history.push({
174
- timestamp: moment.utc().valueOf(),
175
- EntryType: "ExternalIDSetFailed",
176
- user: {
177
- displayName: "SeeStuff Integration",
178
- id: "system",
179
- },
180
- systemType: "SeeStuff",
181
- error: e.message === "NoCRMResidentId" ? "No CRM ID found" : null,
182
- });
183
-
184
- await editRef(values.tableNameMaintenance, "id", request.id, {
185
- history: failedJob.history,
186
- });
187
- } catch (historyError) {
188
- log("SeeStuff:CreateRequest", "HistoryError", historyError, logId);
189
- }
190
- }
191
- return false;
192
- };
193
-
194
- /**
195
- * Fetches a request from the SeeStuff system
196
- * Not implemented yet
197
- *
198
- * @param {String} externalId - Id of the request on SeeStuff
199
- * @returns {Object} The request as it exists on SeeStuff
200
- */
201
- getRequest = async (externalId) => {
202
- return null;
203
- };
204
-
205
- /**
206
- * Refreshes a request from the SeeStuff system
207
- * Not implemented yet
208
- *
209
- * @param {String} requestId - Id of the request on Pluss
210
- * @param {String} externalId - Id of the request on SeeStuff
211
- * @param {Object} trackedData - The set of fields tracked
212
- * @returns {Boolean} Whether the request had any changes on SeeStuff
213
- */
214
- refreshFromSource = async (requestId, externalId, trackedData) => {
215
- return false;
216
- };
217
-
218
- /**
219
- * Get SeeStuff Id of the request
220
- * Queries externalentities table to find the external ID
221
- *
222
- * @param {Object} request - Request definition on Pluss
223
- * @returns {String} Id of the request on SeeStuff
224
- */
225
- getExternalId = async (request) => {
226
- const logId = log("SeeStuff:GetExternalId", "Start", { Id: request.id });
227
-
228
- // get external id
229
- const externalEntityQuery = await indexQuery("externalentities", {
230
- IndexName: "InternalIdIndex",
231
- KeyConditionExpression:
232
- "EntityType = :entityType AND InternalId = :internalId",
233
- ExpressionAttributeValues: {
234
- ":entityType": this.getEntityType(),
235
- ":internalId": request.id,
236
- },
237
- });
238
-
239
- log(
240
- "SeeStuff:GetExternalId",
241
- "ExternalLength",
242
- externalEntityQuery.Items.length,
243
- logId
244
- );
245
- if (_.isEmpty(externalEntityQuery.Items)) {
246
- return null;
247
- }
248
-
249
- const externalId = externalEntityQuery.Items[0].ExternalId;
250
- log("SeeStuff:GetExternalId", "ExternalId", externalId, logId);
251
-
252
- return externalId;
253
- };
254
-
255
- /**
256
- * Perform actions when a task's status has changed
257
- * Not implemented yet
258
- *
259
- * @param {Object} request - Request definition on Pluss
260
- * @returns {Boolean} Represents whether the actions were successful
261
- */
262
- onStatusChanged = async (request) => {
263
- return true;
264
- };
265
-
266
- /**
267
- * Perform actions when a comment has been added to a task
268
- * Not implemented yet
269
- *
270
- * @param {Object} request - Request definition on Pluss
271
- * @returns {Boolean} Represents whether the actions were successful
272
- */
273
- onCommentAdded = async (request) => {
274
- return true;
275
- };
276
-
277
- /**
278
- * Perform actions when a note has been added to a task
279
- * Not implemented yet
280
- *
281
- * @param {Object} request - Request definition on Pluss
282
- * @returns {Boolean} Represents whether the actions were successful
283
- */
284
- onNotesAdded = async (request) => {
285
- return true;
286
- };
287
-
288
- /**
289
- * Perform completion actions when a task is completed
290
- * Not implemented yet
291
- *
292
- * @param {Object} request - Request definition on Pluss
293
- * @returns {Boolean} Represents whether the actions were successful
294
- */
295
- onCompleteRequest = async (request) => {
296
- return true;
297
- };
13
+ constructor(config) {
14
+ super();
15
+ this.baseUrl = config.BaseUrl; // base URL for the API
16
+ this.apiKeyHeader = config.APIKeyHeader; // header to use for API key
17
+ this.apiKey = config.APIKey; // API key
18
+
19
+ const url = new URL(this.baseUrl);
20
+ this.host = url.host;
21
+
22
+ this.siteMap = config.SiteMap ?? {};
23
+ }
24
+
25
+ /**
26
+ * Gets the entity type for the integration
27
+ *
28
+ * @returns {String} The entity type for the integration
29
+ */
30
+ getEntityType = () => {
31
+ return `${values.serviceKey}_SeeStuff`;
32
+ };
33
+
34
+ /**
35
+ * Validates the integration
36
+ *
37
+ * @returns {Boolean} Whether the integration is valid
38
+ */
39
+ isValidIntegration = () => {
40
+ return true;
41
+ };
42
+
43
+ /**
44
+ * Gets the refresh interval for the SeeStuff system
45
+ *
46
+ * @returns {Number} The refresh interval in milliseconds
47
+ */
48
+ getRefreshInterval = () => {
49
+ return Number.MAX_SAFE_INTEGER; // should never refresh
50
+ };
51
+
52
+ /**
53
+ * Creates a request in the SeeStuff system
54
+ *
55
+ * @param {Object} request - Request definition on Pluss
56
+ * @param {Object} mockResponse - Mock response from SeeStuff to simulate response
57
+ * @returns {Boolean} Whether the request was created on SeeStuff
58
+ */
59
+ createRequest = async (request, mockResponse = null) => {
60
+ const logId = log("SeeStuff:CreateRequest", "Start", request);
61
+
62
+ try {
63
+ // Get user details for room/address and CRMID
64
+ let userRoom = request.room || "";
65
+ let crmResidentId = "";
66
+
67
+ // Map site to location code if available
68
+ const location = this.siteMap[request.site];
69
+ if (!location) {
70
+ log("SeeStuff:CreateRequest", "LocationNotFound", request.site, logId);
71
+ return false;
72
+ }
73
+
74
+ if (request.userID) {
75
+ try {
76
+ // Look up user from users table
77
+ const user = await getRef("users", "Id", request.userID);
78
+ log("SeeStuff:CreateRequest", "User", user, logId);
79
+
80
+ if (user) {
81
+ // Get user's address/room
82
+ if (user.unit) {
83
+ userRoom = user.unit;
84
+ }
85
+
86
+ // Get CRMID from user fields
87
+ try {
88
+ const crmIdField = await getRef(
89
+ "userfields",
90
+ "RowId",
91
+ getRowId(request.userID, "CRMID"),
92
+ );
93
+ if (crmIdField && crmIdField.Value) {
94
+ crmResidentId = crmIdField.Value;
95
+ }
96
+ log("SeeStuff:CreateRequest", "CRMID", crmResidentId, logId);
97
+ } catch (fieldError) {
98
+ log(
99
+ "SeeStuff:CreateRequest",
100
+ "CRMIDLookupError",
101
+ fieldError,
102
+ logId,
103
+ );
104
+ }
105
+ }
106
+ } catch (userError) {
107
+ log("SeeStuff:CreateRequest", "UserLookupError", userError, logId);
108
+ }
109
+ }
110
+
111
+ if (_.isEmpty(crmResidentId)) {
112
+ // log an issue in the history
113
+ throw new Error("NoCRMResidentId");
114
+ }
115
+
116
+ // Format description similar to Archibus
117
+ const description = `${request.title}${
118
+ _.isEmpty(request.description) ? "" : `\n\n${request.description}`
119
+ }${_.isEmpty(request.room) ? "" : `\n\nLocation: ${request.room}`}${
120
+ _.isEmpty(request.userName) ? "" : `\n\nJob Type: ${request.type}`
121
+ }${
122
+ _.isEmpty(request.images)
123
+ ? ""
124
+ : `\n\nImages: ${request.images.join("\n")}`
125
+ }`;
126
+
127
+ // Format date_reported
128
+ const dateReported = moment().format("YYYY-MM-DD HH:mm:ss");
129
+
130
+ // Build payload
131
+ const data = {
132
+ externalId: request.id,
133
+ description: description,
134
+ priority: "P3",
135
+ location: location,
136
+ room: userRoom,
137
+ date_reported: dateReported,
138
+ crmResidentId: crmResidentId,
139
+ };
140
+
141
+ log("SeeStuff:CreateRequest", "Request", data, logId);
142
+
143
+ const response =
144
+ mockResponse ??
145
+ (await axios({
146
+ method: "POST",
147
+ url: `${this.baseUrl}/assetmgmt/servicerequests`,
148
+ timeout: 30000,
149
+ headers: {
150
+ [this.apiKeyHeader]: this.apiKey,
151
+ "Content-Type": "application/json",
152
+ Host: this.host,
153
+ },
154
+ data,
155
+ }));
156
+
157
+ log("SeeStuff:CreateRequest", "Response", response.data, logId);
158
+
159
+ return true;
160
+ } catch (e) {
161
+ log("SeeStuff:CreateRequest", "Error", e, logId);
162
+
163
+ // Add history entry for failed integration
164
+ try {
165
+ const failedJob = await getRef(
166
+ values.tableNameMaintenance,
167
+ "id",
168
+ request.id,
169
+ );
170
+ if (!failedJob.history) failedJob.history = [];
171
+ failedJob.history.push({
172
+ timestamp: moment.utc().valueOf(),
173
+ EntryType: "ExternalIDSetFailed",
174
+ user: {
175
+ displayName: "SeeStuff Integration",
176
+ id: "system",
177
+ },
178
+ systemType: "SeeStuff",
179
+ error: e.message === "NoCRMResidentId" ? "No CRM ID found" : null,
180
+ });
181
+
182
+ await editRef(values.tableNameMaintenance, "id", request.id, {
183
+ history: failedJob.history,
184
+ });
185
+ } catch (historyError) {
186
+ log("SeeStuff:CreateRequest", "HistoryError", historyError, logId);
187
+ }
188
+ }
189
+ return false;
190
+ };
191
+
192
+ /**
193
+ * Fetches a request from the SeeStuff system
194
+ * Not implemented yet
195
+ *
196
+ * @param {String} externalId - Id of the request on SeeStuff
197
+ * @returns {Object} The request as it exists on SeeStuff
198
+ */
199
+ getRequest = async (externalId) => {
200
+ return null;
201
+ };
202
+
203
+ /**
204
+ * Refreshes a request from the SeeStuff system
205
+ * Not implemented yet
206
+ *
207
+ * @param {String} requestId - Id of the request on Pluss
208
+ * @param {String} externalId - Id of the request on SeeStuff
209
+ * @param {Object} trackedData - The set of fields tracked
210
+ * @returns {Boolean} Whether the request had any changes on SeeStuff
211
+ */
212
+ refreshFromSource = async (requestId, externalId, trackedData) => {
213
+ return false;
214
+ };
215
+
216
+ /**
217
+ * Get SeeStuff Id of the request
218
+ * Queries externalentities table to find the external ID
219
+ *
220
+ * @param {Object} request - Request definition on Pluss
221
+ * @returns {String} Id of the request on SeeStuff
222
+ */
223
+ getExternalId = async (request) => {
224
+ const logId = log("SeeStuff:GetExternalId", "Start", { Id: request.id });
225
+
226
+ // get external id
227
+ const externalEntityQuery = await indexQuery("externalentities", {
228
+ IndexName: "InternalIdIndex",
229
+ KeyConditionExpression:
230
+ "EntityType = :entityType AND InternalId = :internalId",
231
+ ExpressionAttributeValues: {
232
+ ":entityType": this.getEntityType(),
233
+ ":internalId": request.id,
234
+ },
235
+ });
236
+
237
+ log(
238
+ "SeeStuff:GetExternalId",
239
+ "ExternalLength",
240
+ externalEntityQuery.Items.length,
241
+ logId,
242
+ );
243
+ if (_.isEmpty(externalEntityQuery.Items)) {
244
+ return null;
245
+ }
246
+
247
+ const externalId = externalEntityQuery.Items[0].ExternalId;
248
+ log("SeeStuff:GetExternalId", "ExternalId", externalId, logId);
249
+
250
+ return externalId;
251
+ };
252
+
253
+ /**
254
+ * Perform actions when a task's status has changed
255
+ * Not implemented yet
256
+ *
257
+ * @param {Object} request - Request definition on Pluss
258
+ * @returns {Boolean} Represents whether the actions were successful
259
+ */
260
+ onStatusChanged = async (request) => {
261
+ return true;
262
+ };
263
+
264
+ /**
265
+ * Perform actions when a comment has been added to a task
266
+ * Not implemented yet
267
+ *
268
+ * @param {Object} request - Request definition on Pluss
269
+ * @returns {Boolean} Represents whether the actions were successful
270
+ */
271
+ onCommentAdded = async (request) => {
272
+ return true;
273
+ };
274
+
275
+ /**
276
+ * Perform actions when a note has been added to a task
277
+ * Not implemented yet
278
+ *
279
+ * @param {Object} request - Request definition on Pluss
280
+ * @returns {Boolean} Represents whether the actions were successful
281
+ */
282
+ onNotesAdded = async (request) => {
283
+ return true;
284
+ };
285
+
286
+ /**
287
+ * Perform completion actions when a task is completed
288
+ * Not implemented yet
289
+ *
290
+ * @param {Object} request - Request definition on Pluss
291
+ * @returns {Boolean} Represents whether the actions were successful
292
+ */
293
+ onCompleteRequest = async (request) => {
294
+ return true;
295
+ };
298
296
  }
299
297
 
300
298
  module.exports = SeeStuffStrategy;