@plusscommunities/pluss-maintenance-aws 2.1.21 → 2.1.23
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/getData.js +7 -0
- package/integration/archibus/ArchibusStrategy.js +2 -2
- package/integration/index.js +3 -0
- package/integration/seestuff/SeeStuffStrategy.js +293 -0
- package/package-lock.json +1 -1
- package/package.json +1 -1
- package/requests/getExternalSync.js +171 -0
- package/requests/setExternalJobId.js +1 -1
package/getData.js
CHANGED
|
@@ -5,6 +5,7 @@ const generateJsonResponse = require("@plusscommunities/pluss-core-aws/helper/ge
|
|
|
5
5
|
const getAssignees = require("./requests/getAssignees");
|
|
6
6
|
const getRequests = require("./requests/getRequests");
|
|
7
7
|
const getRequest = require("./requests/getRequest");
|
|
8
|
+
const getExternalSync = require("./requests/getExternalSync");
|
|
8
9
|
|
|
9
10
|
module.exports.getData = async (event, context, callback) => {
|
|
10
11
|
init(config);
|
|
@@ -34,6 +35,12 @@ module.exports.getData = async (event, context, callback) => {
|
|
|
34
35
|
log(action, "ResponseLength", response.data.Users.length, logId);
|
|
35
36
|
}
|
|
36
37
|
break;
|
|
38
|
+
case "externalsync":
|
|
39
|
+
response = await getExternalSync(event);
|
|
40
|
+
if (response.status === 200) {
|
|
41
|
+
log(action, "ExternalSystem", response.data.externalSystem, logId);
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
37
44
|
default:
|
|
38
45
|
break;
|
|
39
46
|
}
|
|
@@ -139,7 +139,7 @@ class ArchibusStrategy extends IntegrationStrategy {
|
|
|
139
139
|
if (!updatedJob.history) updatedJob.history = [];
|
|
140
140
|
updatedJob.history.push({
|
|
141
141
|
timestamp: moment.utc().valueOf(),
|
|
142
|
-
|
|
142
|
+
EntryType: "ExternalIDSet",
|
|
143
143
|
externalId: response.data.wrId,
|
|
144
144
|
user: {
|
|
145
145
|
displayName: "Archibus Integration",
|
|
@@ -176,7 +176,7 @@ class ArchibusStrategy extends IntegrationStrategy {
|
|
|
176
176
|
if (!failedJob.history) failedJob.history = [];
|
|
177
177
|
failedJob.history.push({
|
|
178
178
|
timestamp: moment.utc().valueOf(),
|
|
179
|
-
|
|
179
|
+
EntryType: "ExternalIDSetFailed",
|
|
180
180
|
user: {
|
|
181
181
|
displayName: "Archibus Integration",
|
|
182
182
|
id: "system",
|
package/integration/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const getString = require("@plusscommunities/pluss-core-aws/db/strings/getString");
|
|
2
2
|
const { getRowId, log } = require("@plusscommunities/pluss-core-aws/helper");
|
|
3
3
|
const ArchibusStrategy = require("./archibus/ArchibusStrategy");
|
|
4
|
+
const SeeStuffStrategy = require("./seestuff/SeeStuffStrategy");
|
|
4
5
|
const IntegrationStrategy = require("./IntegrationStrategy");
|
|
5
6
|
const { values } = require("../values.config");
|
|
6
7
|
|
|
@@ -16,6 +17,8 @@ exports.getStrategy = async (site) => {
|
|
|
16
17
|
switch (importConfig.Strategy) {
|
|
17
18
|
case "Archibus":
|
|
18
19
|
return new ArchibusStrategy(importConfig);
|
|
20
|
+
case "SeeStuff":
|
|
21
|
+
return new SeeStuffStrategy(importConfig);
|
|
19
22
|
default:
|
|
20
23
|
return new IntegrationStrategy();
|
|
21
24
|
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
const axios = require("axios");
|
|
2
|
+
const moment = require("moment");
|
|
3
|
+
const _ = require("lodash");
|
|
4
|
+
const IntegrationStrategy = require("../IntegrationStrategy");
|
|
5
|
+
const { log, getRowId } = require("@plusscommunities/pluss-core-aws/helper");
|
|
6
|
+
const editRef = require("@plusscommunities/pluss-core-aws/db/common/editRef");
|
|
7
|
+
const updateRef = require("@plusscommunities/pluss-core-aws/db/common/updateRef");
|
|
8
|
+
const getRef = require("@plusscommunities/pluss-core-aws/db/common/getRef");
|
|
9
|
+
const indexQuery = require("@plusscommunities/pluss-core-aws/db/common/indexQuery");
|
|
10
|
+
const { values } = require("../../values.config");
|
|
11
|
+
|
|
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
|
+
// Format description similar to Archibus
|
|
112
|
+
const description = `${request.title}${
|
|
113
|
+
_.isEmpty(request.description) ? "" : `\n\n${request.description}`
|
|
114
|
+
}${_.isEmpty(request.room) ? "" : `\n\nLocation: ${request.room}`}${
|
|
115
|
+
_.isEmpty(request.userName) ? "" : `\n\nName: ${request.userName}`
|
|
116
|
+
}${_.isEmpty(request.phone) ? "" : `\n\nPhone: ${request.phone}`}${
|
|
117
|
+
_.isEmpty(request.type) ? "" : `\n\nJob Type: ${request.type}`
|
|
118
|
+
}${
|
|
119
|
+
_.isEmpty(request.images)
|
|
120
|
+
? ""
|
|
121
|
+
: `\n\nImages: ${request.images.join("\n")}`
|
|
122
|
+
}`;
|
|
123
|
+
|
|
124
|
+
// Format date_reported
|
|
125
|
+
const dateReported = moment().format("YYYY-MM-DD HH:mm:ss");
|
|
126
|
+
|
|
127
|
+
// Build payload
|
|
128
|
+
const data = {
|
|
129
|
+
externalId: request.id,
|
|
130
|
+
description: description,
|
|
131
|
+
priority: "P3",
|
|
132
|
+
location: location,
|
|
133
|
+
room: userRoom,
|
|
134
|
+
date_reported: dateReported,
|
|
135
|
+
crmResidentId: crmResidentId,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
log("SeeStuff:CreateRequest", "Request", data, logId);
|
|
139
|
+
|
|
140
|
+
const response =
|
|
141
|
+
mockResponse ??
|
|
142
|
+
(await axios({
|
|
143
|
+
method: "POST",
|
|
144
|
+
url: `${this.baseUrl}/assetmgmt/servicerequests`,
|
|
145
|
+
timeout: 30000,
|
|
146
|
+
headers: {
|
|
147
|
+
[this.apiKeyHeader]: this.apiKey,
|
|
148
|
+
"Content-Type": "application/json",
|
|
149
|
+
Host: this.host,
|
|
150
|
+
},
|
|
151
|
+
data,
|
|
152
|
+
}));
|
|
153
|
+
|
|
154
|
+
log("SeeStuff:CreateRequest", "Response", response.data, logId);
|
|
155
|
+
|
|
156
|
+
return true;
|
|
157
|
+
} catch (e) {
|
|
158
|
+
log("SeeStuff:CreateRequest", "Error", e, logId);
|
|
159
|
+
|
|
160
|
+
// Add history entry for failed integration
|
|
161
|
+
try {
|
|
162
|
+
const failedJob = await getRef(
|
|
163
|
+
values.tableNameMaintenance,
|
|
164
|
+
"id",
|
|
165
|
+
request.id
|
|
166
|
+
);
|
|
167
|
+
if (!failedJob.history) failedJob.history = [];
|
|
168
|
+
failedJob.history.push({
|
|
169
|
+
timestamp: moment.utc().valueOf(),
|
|
170
|
+
EntryType: "ExternalIDSetFailed",
|
|
171
|
+
user: {
|
|
172
|
+
displayName: "SeeStuff Integration",
|
|
173
|
+
id: "system",
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
await editRef(values.tableNameMaintenance, "id", request.id, {
|
|
178
|
+
history: failedJob.history,
|
|
179
|
+
});
|
|
180
|
+
} catch (historyError) {
|
|
181
|
+
log("SeeStuff:CreateRequest", "HistoryError", historyError, logId);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return false;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Fetches a request from the SeeStuff system
|
|
189
|
+
* Not implemented yet
|
|
190
|
+
*
|
|
191
|
+
* @param {String} externalId - Id of the request on SeeStuff
|
|
192
|
+
* @returns {Object} The request as it exists on SeeStuff
|
|
193
|
+
*/
|
|
194
|
+
getRequest = async (externalId) => {
|
|
195
|
+
return null;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Refreshes a request from the SeeStuff system
|
|
200
|
+
* Not implemented yet
|
|
201
|
+
*
|
|
202
|
+
* @param {String} requestId - Id of the request on Pluss
|
|
203
|
+
* @param {String} externalId - Id of the request on SeeStuff
|
|
204
|
+
* @param {Object} trackedData - The set of fields tracked
|
|
205
|
+
* @returns {Boolean} Whether the request had any changes on SeeStuff
|
|
206
|
+
*/
|
|
207
|
+
refreshFromSource = async (requestId, externalId, trackedData) => {
|
|
208
|
+
return false;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get SeeStuff Id of the request
|
|
213
|
+
* Queries externalentities table to find the external ID
|
|
214
|
+
*
|
|
215
|
+
* @param {Object} request - Request definition on Pluss
|
|
216
|
+
* @returns {String} Id of the request on SeeStuff
|
|
217
|
+
*/
|
|
218
|
+
getExternalId = async (request) => {
|
|
219
|
+
const logId = log("SeeStuff:GetExternalId", "Start", { Id: request.id });
|
|
220
|
+
|
|
221
|
+
// get external id
|
|
222
|
+
const externalEntityQuery = await indexQuery("externalentities", {
|
|
223
|
+
IndexName: "InternalIdIndex",
|
|
224
|
+
KeyConditionExpression:
|
|
225
|
+
"EntityType = :entityType AND InternalId = :internalId",
|
|
226
|
+
ExpressionAttributeValues: {
|
|
227
|
+
":entityType": this.getEntityType(),
|
|
228
|
+
":internalId": request.id,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
log(
|
|
233
|
+
"SeeStuff:GetExternalId",
|
|
234
|
+
"ExternalLength",
|
|
235
|
+
externalEntityQuery.Items.length,
|
|
236
|
+
logId
|
|
237
|
+
);
|
|
238
|
+
if (_.isEmpty(externalEntityQuery.Items)) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const externalId = externalEntityQuery.Items[0].ExternalId;
|
|
243
|
+
log("SeeStuff:GetExternalId", "ExternalId", externalId, logId);
|
|
244
|
+
|
|
245
|
+
return externalId;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Perform actions when a task's status has changed
|
|
250
|
+
* Not implemented yet
|
|
251
|
+
*
|
|
252
|
+
* @param {Object} request - Request definition on Pluss
|
|
253
|
+
* @returns {Boolean} Represents whether the actions were successful
|
|
254
|
+
*/
|
|
255
|
+
onStatusChanged = async (request) => {
|
|
256
|
+
return true;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Perform actions when a comment has been added to a task
|
|
261
|
+
* Not implemented yet
|
|
262
|
+
*
|
|
263
|
+
* @param {Object} request - Request definition on Pluss
|
|
264
|
+
* @returns {Boolean} Represents whether the actions were successful
|
|
265
|
+
*/
|
|
266
|
+
onCommentAdded = async (request) => {
|
|
267
|
+
return true;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Perform actions when a note has been added to a task
|
|
272
|
+
* Not implemented yet
|
|
273
|
+
*
|
|
274
|
+
* @param {Object} request - Request definition on Pluss
|
|
275
|
+
* @returns {Boolean} Represents whether the actions were successful
|
|
276
|
+
*/
|
|
277
|
+
onNotesAdded = async (request) => {
|
|
278
|
+
return true;
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Perform completion actions when a task is completed
|
|
283
|
+
* Not implemented yet
|
|
284
|
+
*
|
|
285
|
+
* @param {Object} request - Request definition on Pluss
|
|
286
|
+
* @returns {Boolean} Represents whether the actions were successful
|
|
287
|
+
*/
|
|
288
|
+
onCompleteRequest = async (request) => {
|
|
289
|
+
return true;
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
module.exports = SeeStuffStrategy;
|
package/package-lock.json
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
const getRef = require("@plusscommunities/pluss-core-aws/db/common/getRef");
|
|
2
|
+
const indexQuery = require("@plusscommunities/pluss-core-aws/db/common/indexQuery");
|
|
3
|
+
const validateMasterAuth = require("@plusscommunities/pluss-core-aws/helper/auth/validateMasterAuth");
|
|
4
|
+
const isValidAssignee = require("./helper/isValidAssignee");
|
|
5
|
+
const getSessionUserFromReqAuthKey = require("@plusscommunities/pluss-core-aws/helper/auth/getSessionUserFromReqAuthKey");
|
|
6
|
+
const { log } = require("@plusscommunities/pluss-core-aws/helper");
|
|
7
|
+
const { values } = require("../values.config");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Retrieves external system sync information for a maintenance request
|
|
11
|
+
*
|
|
12
|
+
* Checks both SeeStuff and Archibus external entity tables to find
|
|
13
|
+
* if the request has been synced to an external system.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} event - Lambda event object
|
|
16
|
+
* @param {Object} params - Query parameters (optional, uses event.queryStringParameters if not provided)
|
|
17
|
+
* @returns {Object} Response object with status and data
|
|
18
|
+
*
|
|
19
|
+
* Response format (200):
|
|
20
|
+
* {
|
|
21
|
+
* externalSystem: "SeeStuff" | "Archibus",
|
|
22
|
+
* externalId: string,
|
|
23
|
+
* syncedAt: number (Unix timestamp),
|
|
24
|
+
* trackedData: object
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* Error responses:
|
|
28
|
+
* - 422: Missing required field (id)
|
|
29
|
+
* - 404: Request not found OR no external sync exists
|
|
30
|
+
* - 403: Not authorized (user doesn't have permission to view request)
|
|
31
|
+
* - 500: Internal server error
|
|
32
|
+
*/
|
|
33
|
+
module.exports = async (event, params) => {
|
|
34
|
+
const data = params || event.queryStringParameters || {};
|
|
35
|
+
const logId = log("getExternalSync", "Params", data);
|
|
36
|
+
|
|
37
|
+
// Validate input
|
|
38
|
+
if (!data.id) {
|
|
39
|
+
log("getExternalSync", "MissingId", true, logId);
|
|
40
|
+
return {
|
|
41
|
+
status: 422,
|
|
42
|
+
data: { error: "Missing required field: id" },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Get the request to verify it exists and get site for permission check
|
|
48
|
+
let request;
|
|
49
|
+
try {
|
|
50
|
+
request = await getRef(values.tableNameMaintenance, "id", data.id);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
log("getExternalSync", "RequestNotFound", { id: data.id }, logId);
|
|
53
|
+
return {
|
|
54
|
+
status: 404,
|
|
55
|
+
data: { error: "Request not found" },
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!request) {
|
|
60
|
+
log("getExternalSync", "RequestNotFound", { id: data.id }, logId);
|
|
61
|
+
return {
|
|
62
|
+
status: 404,
|
|
63
|
+
data: { error: "Request not found" },
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check permissions (same pattern as getRequest.js)
|
|
68
|
+
// Three levels of access:
|
|
69
|
+
// 1. Full admin with maintenance tracking permission
|
|
70
|
+
// 2. User assigned to the request
|
|
71
|
+
// 3. User who created the request
|
|
72
|
+
const authorised = await validateMasterAuth(
|
|
73
|
+
event,
|
|
74
|
+
values.permissionMaintenanceTracking,
|
|
75
|
+
request.site
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const assignAuthorised = await isValidAssignee(
|
|
79
|
+
event,
|
|
80
|
+
request.site,
|
|
81
|
+
request.AssigneeId
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (!authorised && !assignAuthorised) {
|
|
85
|
+
// Check if user is the request owner
|
|
86
|
+
const userId = await getSessionUserFromReqAuthKey(event);
|
|
87
|
+
if (userId !== request.userID) {
|
|
88
|
+
log(
|
|
89
|
+
"getExternalSync",
|
|
90
|
+
"NotAuthorised",
|
|
91
|
+
{ userId, requestOwner: request.userID },
|
|
92
|
+
logId
|
|
93
|
+
);
|
|
94
|
+
return {
|
|
95
|
+
status: 403,
|
|
96
|
+
data: { error: "Not authorised" },
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
log("getExternalSync", "Authorised", true, logId);
|
|
102
|
+
|
|
103
|
+
// Query externalentities table for this request
|
|
104
|
+
// Check both SeeStuff and Archibus entity types
|
|
105
|
+
const entityTypes = [
|
|
106
|
+
`${values.serviceKey}_SeeStuff`,
|
|
107
|
+
`${values.serviceKey}_Archibus`,
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
log("getExternalSync", "CheckingEntityTypes", entityTypes, logId);
|
|
111
|
+
|
|
112
|
+
let externalSync = null;
|
|
113
|
+
|
|
114
|
+
// Check each external system type
|
|
115
|
+
for (const entityType of entityTypes) {
|
|
116
|
+
const query = await indexQuery("externalentities", {
|
|
117
|
+
IndexName: "InternalIdIndex",
|
|
118
|
+
KeyConditionExpression:
|
|
119
|
+
"EntityType = :entityType AND InternalId = :internalId",
|
|
120
|
+
ExpressionAttributeValues: {
|
|
121
|
+
":entityType": entityType,
|
|
122
|
+
":internalId": request.id,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
log(
|
|
127
|
+
"getExternalSync",
|
|
128
|
+
`${entityType}_Results`,
|
|
129
|
+
query.Items.length,
|
|
130
|
+
logId
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (query.Items && query.Items.length > 0) {
|
|
134
|
+
const item = query.Items[0];
|
|
135
|
+
// Extract system name from entity type (e.g., "maintenance_SeeStuff" -> "SeeStuff")
|
|
136
|
+
const systemName = entityType.split("_")[1];
|
|
137
|
+
|
|
138
|
+
externalSync = {
|
|
139
|
+
externalSystem: systemName,
|
|
140
|
+
externalId: item.ExternalId,
|
|
141
|
+
syncedAt: item.LastUpdated,
|
|
142
|
+
trackedData: item.TrackedData || {},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
log("getExternalSync", "FoundSync", externalSync, logId);
|
|
146
|
+
break; // Stop after finding first match
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// If no external sync found, return 404
|
|
151
|
+
if (!externalSync) {
|
|
152
|
+
log("getExternalSync", "NoSyncFound", { id: data.id }, logId);
|
|
153
|
+
return {
|
|
154
|
+
status: 404,
|
|
155
|
+
data: { error: "No external sync found" },
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
log("getExternalSync", "Success", externalSync, logId);
|
|
160
|
+
return {
|
|
161
|
+
status: 200,
|
|
162
|
+
data: externalSync,
|
|
163
|
+
};
|
|
164
|
+
} catch (error) {
|
|
165
|
+
log("getExternalSync", "InternalError", error.toString(), logId);
|
|
166
|
+
return {
|
|
167
|
+
status: 500,
|
|
168
|
+
data: { error: "Internal error", message: error.message },
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
};
|