diff --git a/functions/src/dooraccess/essl.ts b/functions/src/dooraccess/essl.ts
index 231b06a..c45ea64 100644
--- a/functions/src/dooraccess/essl.ts
+++ b/functions/src/dooraccess/essl.ts
@@ -9,6 +9,23 @@ import { DOMParser } from 'xmldom';
const logger = getLogger();
const corsHandler = getCorsHandler();
+export interface GetEmployeeDetailsRquest {
+ employeeCode: string;
+}
+
+export interface UpdateEmployeeExRequest {
+ employeeCode: string;
+ employeeName: string;
+ employeeLocation: string;
+ employeeRole: string;
+ employeeVerificationType: string;
+ employeeExpiryFrom: string;
+ employeeExpiryTo: string;
+ employeeCardNumber: string;
+ groupId: string;
+ employeePhoto: string;
+}
+
const escapeXml = (str: string) => {
return str
.replace(/&/g, '&')
@@ -102,6 +119,52 @@ function parseGetEmployeeDetailsResponse(soapResponse: string): DoorAccessUser {
return userDetails;
}
+function isValidDateString(dateString: string): boolean {
+ const dateRegex = /^\d{4}\-\d{2}\-\d{2}$/;
+ return dateRegex.test(dateString);
+}
+
+function createUpdateEmployeeExRequest(username: string, password: string, request: UpdateEmployeeExRequest) {
+
+ if (!username || !password || !request.employeeCode || !request.employeeName || !request.employeeLocation) {
+ throw new Error('Missing required fields');
+ }
+
+ if (!isValidDateString(request.employeeExpiryFrom) || !isValidDateString(request.employeeExpiryTo)) {
+ throw new Error('Invalid date format');
+ }
+
+ const soapRequest = `
+
+
+
+ ${escapeXml(username)}
+ ${escapeXml(password)}
+ ${escapeXml(request.employeeCode)}
+ ${escapeXml(request.employeeName)}
+ ${escapeXml(request.employeeLocation)}
+ ${escapeXml(request.employeeRole)}
+ Card
+ ${escapeXml(request.employeeExpiryFrom)}
+ ${escapeXml(request.employeeExpiryTo)}
+ ${escapeXml(request.employeeCardNumber)}
+
+
+
+
+
+`;
+ return soapRequest;
+}
+
+function parseUpdateEmployeeExResponse(soapResponse: string): string | null {
+ const parser = new DOMParser();
+ const xmlDoc = parser.parseFromString(soapResponse, "text/xml");
+ const currentElement = xmlDoc.documentElement.firstChild as HTMLElement;
+ const resultText = currentElement.textContent;
+ return resultText;
+}
+
async function sendSoapRequest(soapRequest: string, endpoint: string) {
try {
const headers: any = {
@@ -134,24 +197,63 @@ async function getUserDetails(username: string,
return parsedResponse;
}
+async function updateEmployeeEx(username: string,
+ password: string,
+ request: UpdateEmployeeExRequest,
+ endpoint: string) {
+ const soapRequest = createUpdateEmployeeExRequest(username, password, request);
+ const soapResponse = await sendSoapRequest(soapRequest, endpoint);
+ const parsedResponse = parseUpdateEmployeeExResponse(soapResponse);
+ return parsedResponse;
+}
+
export const esslGetUserDetails = onRequest({
region: '#{SERVICES_RGN}#'
}, async (request: Request, response: Response) => {
return corsHandler(request, response, async () => {
try {
- const username = request.body.username;
- const password = request.body.password;
- const employeeCode = request.body.employeeCode;
- const endpoint = request.body.endpoint;
- if ((!username) || (!password)) {
+ let username: string | null = request.body.username as string;
+ let password: string | null = request.body.password as string;
+ let endpoint: string | null = request.body.endpoint as string;
+ let gymId: string | null = request.body.gymId as string;
+
+ const getEmployeeDetailsRequest = request.body.params as GetEmployeeDetailsRquest;
+
+ if (!username) {
throw new Error('Missing username or password');
}
+ username = username.trim();
+ if (!password) {
+ if (!gymId) {
+ throw new Error('Missing password or gymId');
+ }
+ // todo: Get password from gym configuration by decrypting with private key
+ }
+ password = password.trim();
+ if (!getEmployeeDetailsRequest) {
+ throw new Error('Missing request params');
+ }
+ const employeeCode = getEmployeeDetailsRequest.employeeCode;
if (!employeeCode) {
- throw new Error('Missing employee code');
+ throw new Error('Missing employeeCode');
}
if (!endpoint) {
throw new Error('Missing endpoint');
}
+ if (!endpoint || endpoint.trim() === '') {
+ throw new Error('Missing endpoint');
+ }
+ try {
+ new URL(endpoint);
+ } catch (_) {
+ throw new Error('Endpoint is not a valid URI or URL');
+ }
+ if (!endpoint.endsWith('/webservice.asmx')) {
+ if (endpoint.endsWith('/')) {
+ endpoint = endpoint.substring(0, endpoint.length - 1);
+ }
+ endpoint += '/webservice.asmx';
+ }
const userDetails = await getUserDetails(username, password, employeeCode, endpoint);
response.send(userDetails);
} catch (error: any) {
@@ -160,3 +262,54 @@ export const esslGetUserDetails = onRequest({
}
})
});
+
+export const esslUpdateUser = onRequest({
+ region: '#{SERVICES_RGN}#'
+}, async (request: Request, response: Response) => {
+ return corsHandler(request, response, async () => {
+ try {
+ let username: string | null = request.body.username as string;
+ let password: string | null = request.body.password as string;
+ let endpoint: string | null = request.body.endpoint as string;
+ let gymId: string | null = request.body.gymId as string;
+
+ const updateEmployeeExRequest = request.body.params as UpdateEmployeeExRequest;
+ if (!username) {
+ throw new Error('Missing username or password');
+ }
+ username = username.trim();
+ if (!password) {
+ if (!gymId) {
+ throw new Error('Missing password or gymId');
+ }
+ // todo: Get password from gym configuration by decrypting with private key
+ }
+ if (!endpoint) {
+ throw new Error('Missing endpoint');
+ }
+ endpoint = endpoint.trim();
+ if (!endpoint || endpoint.trim() === '') {
+ throw new Error('Missing endpoint');
+ }
+ try {
+ new URL(endpoint);
+ } catch (_) {
+ throw new Error('Endpoint is not a valid URI or URL');
+ }
+ if (!endpoint.endsWith('/webservice.asmx')) {
+ if (endpoint.endsWith('/')) {
+ endpoint = endpoint.substring(0, endpoint.length - 1);
+ }
+ endpoint += '/webservice.asmx';
+ }
+ if (!updateEmployeeExRequest) {
+ throw new Error('Missing request params');
+ }
+ const result = await updateEmployeeEx(username, password, updateEmployeeExRequest, endpoint);
+ response.send(result);
+ } catch (error: any) {
+ logger.error(error);
+ response.status(500).send({ error: error.message });
+ }
+ });
+});
diff --git a/functions/src/dooraccess/index.ts b/functions/src/dooraccess/index.ts
index a855858..497e0cb 100644
--- a/functions/src/dooraccess/index.ts
+++ b/functions/src/dooraccess/index.ts
@@ -1 +1 @@
-export { esslGetUserDetails } from './essl';
\ No newline at end of file
+export { esslGetUserDetails, esslUpdateUser } from './essl';
\ No newline at end of file
diff --git a/functions/src/email/sendEmailSES.ts b/functions/src/email/sendEmailSES.ts
index 8d0437c..5efc40a 100644
--- a/functions/src/email/sendEmailSES.ts
+++ b/functions/src/email/sendEmailSES.ts
@@ -136,7 +136,7 @@ async function downloadFileFromUrl(url: string): Promise {
}
export const sendEmailSES = onRequest({
- region: 'asia-south1'
+ region: '#{SERVICES_RGN}#'
}, (request: Request, response: Response) => {
return corsHandler(request, response, async () => {
try {
diff --git a/functions/src/index.ts b/functions/src/index.ts
index 12cacad..362d85d 100644
--- a/functions/src/index.ts
+++ b/functions/src/index.ts
@@ -17,4 +17,4 @@ export { processNotificationOnCreate } from './notifications';
export * from './payments';
export { getPlaceDetails, getPlacesAutocomplete } from './places';
export { registerClient } from './users';
-export { esslGetUserDetails } from './dooraccess';
+export { esslGetUserDetails, esslUpdateUser } from './dooraccess';