From 95ac33cfc2e33ae7233cf1eea3f589877c73c84c Mon Sep 17 00:00:00 2001 From: Joyce Yuki <82857964+kathyavini@users.noreply.github.com> Date: Fri, 3 Jan 2025 11:19:41 -0800 Subject: [PATCH] LF-4625 Refactor Ensemble Scientific portion of addSensors controller for readability Remove reduce in favour of more readable array methods; encapsulate all three Ensemble methods into one function; define a constant for 'Ensemble Scientific' --- .../api/src/controllers/sensorController.js | 104 +++++++++--------- packages/api/src/util/ensemble.js | 34 ++++-- 2 files changed, 75 insertions(+), 63 deletions(-) diff --git a/packages/api/src/controllers/sensorController.js b/packages/api/src/controllers/sensorController.js index 4d4e8a8271..170cb822a5 100644 --- a/packages/api/src/controllers/sensorController.js +++ b/packages/api/src/controllers/sensorController.js @@ -29,9 +29,9 @@ import PartnerReadingTypeModel from '../models/PartnerReadingTypeModel.js'; import { transaction, Model } from 'objection'; import { - createOrganization, - registerOrganizationWebhook, - bulkSensorClaim, + ENSEMBLE_BRAND, + extractEsids, + registerFarmAndClaimSensors, unclaimSensor, ENSEMBLE_UNITS_MAPPING, } from '../util/ensemble.js'; @@ -119,7 +119,7 @@ const sensorController = { const { user_id } = req.auth; try { const { access_token } = await IntegratingPartnersModel.getAccessAndRefreshTokens( - 'Ensemble Scientific', + ENSEMBLE_BRAND, ); //TODO: LF-4443 - Sensor should not use User language (unrestricted string), accept as body param or farm level detail @@ -173,65 +173,59 @@ const sensorController = { }, ); } else { - const esids = data.reduce((previous, current) => { - if (current.brand === 'Ensemble Scientific' && current.external_id) { - previous.push(current.external_id); - } - return previous; - }, []); + // Extract Ensemble Scientific sensor IDs (esids) + const esids = extractEsids(data); + let success = []; let already_owned = []; let does_not_exist = []; let occupied = []; - if (esids.length > 0) { - const organization = await createOrganization(farm_id, access_token); - - // register webhook for sensor readings - await registerOrganizationWebhook(farm_id, organization.organization_uuid, access_token); - // Register sensors with Ensemble - ({ success, already_owned, does_not_exist, occupied } = await bulkSensorClaim( + if (esids.length > 0) { + ({ success, already_owned, does_not_exist, occupied } = await registerFarmAndClaimSensors( + farm_id, access_token, - organization.organization_uuid, esids, )); } - // register organization - - // Filter sensors by those successfully registered and those with errors - const { registeredSensors, errorSensors } = data.reduce( - (prev, curr, idx) => { - if (success?.includes(curr.external_id) || already_owned?.includes(curr.external_id)) { - prev.registeredSensors.push(curr); - } else if (curr.brand !== 'Ensemble Scientific') { - prev.registeredSensors.push(curr); - } else if (does_not_exist?.includes(curr.external_id)) { - prev.errorSensors.push({ - row: idx + 2, - column: 'External_ID', - translation_key: sensorErrors.SENSOR_DOES_NOT_EXIST, - variables: { sensorId: curr.external_id }, - }); - } else if (occupied?.includes(curr.external_id)) { - prev.errorSensors.push({ - row: idx + 2, - column: 'External_ID', - translation_key: sensorErrors.SENSOR_ALREADY_OCCUPIED, - variables: { sensorId: curr.external_id }, - }); - } else { - // we know that it is an ESID but for some reason it was not returned in the expected format from the API - prev.errorSensors.push({ - row: idx + 2, - column: 'External_ID', - translation_key: sensorErrors.INTERNAL_ERROR, - variables: { sensorId: curr.external_id }, - }); - } - return prev; - }, - { registeredSensors: [], errorSensors: [] }, - ); + + const registeredSensors = []; + const errorSensors = []; + + // Iterate over each sensor in the data array + data.forEach((sensor, index) => { + if (sensor.brand !== ENSEMBLE_BRAND) { + // All non-ESCI sensors should be considered successfully registered + registeredSensors.push(sensor); + } else if ( + success.includes(sensor.external_id) || + already_owned.includes(sensor.external_id) + ) { + registeredSensors.push(sensor); + } else if (does_not_exist.includes(sensor.external_id)) { + errorSensors.push({ + row: index + 2, + column: 'External_ID', + translation_key: sensorErrors.SENSOR_DOES_NOT_EXIST, + variables: { sensorId: sensor.external_id }, + }); + } else if (occupied.includes(sensor.external_id)) { + errorSensors.push({ + row: index + 2, + column: 'External_ID', + translation_key: sensorErrors.SENSOR_ALREADY_OCCUPIED, + variables: { sensorId: sensor.external_id }, + }); + } else { + // We know that it is an ESID but it was not returned in the expected format from the API + errorSensors.push({ + row: index + 2, + column: 'External_ID', + translation_key: sensorErrors.INTERNAL_ERROR, + variables: { sensorId: sensor.external_id }, + }); + } + }); // Save sensors in database const sensorLocations = []; @@ -630,7 +624,7 @@ const sensorController = { const user_id = req.auth.user_id; const { access_token } = await IntegratingPartnersModel.getAccessAndRefreshTokens( - 'Ensemble Scientific', + ENSEMBLE_BRAND, ); let unclaimResponse; if (partner_name != 'No Integrating Partner' && external_id != '') { diff --git a/packages/api/src/util/ensemble.js b/packages/api/src/util/ensemble.js index b982f28943..edca04f259 100644 --- a/packages/api/src/util/ensemble.js +++ b/packages/api/src/util/ensemble.js @@ -61,6 +61,26 @@ const ENSEMBLE_UNITS_MAPPING = { }, }; +const ENSEMBLE_BRAND = 'Ensemble Scientific'; + +// Return Ensemble Scientific IDs (esids) from sensor data +const extractEsids = (data) => + data + .filter((sensor) => sensor.brand === 'Ensemble Scientific' && sensor.external_id) + .map((sensor) => sensor.external_id); + +// Function to encapsulate the logic for claiming sensors +async function registerFarmAndClaimSensors(farm_id, access_token, esids) { + // Register farm as an organization with Ensemble + const organization = await createOrganization(farm_id, access_token); + + // Create a webhook for the organization + await registerOrganizationWebhook(farm_id, organization.organization_uuid, access_token); + + // Register sensors with Ensemble and return Ensemble API results + return await bulkSensorClaim(access_token, organization.organization_uuid, esids); +} + /** * Sends a request to the Ensemble API for an organization to claim sensors * @param {String} accessToken - a JWT token for accessing the Ensemble API @@ -250,12 +270,10 @@ function isAuthError(error) { */ async function refreshTokens() { try { - const { refresh_token } = await IntegratingPartners.getAccessAndRefreshTokens( - 'Ensemble Scientific', - ); + const { refresh_token } = await IntegratingPartners.getAccessAndRefreshTokens(ENSEMBLE_BRAND); const response = await axios.post(ensembleAPI + '/token/refresh/', { refresh: refresh_token }); await IntegratingPartners.patchAccessAndRefreshTokens( - 'Ensemble Scientific', + ENSEMBLE_BRAND, response.data?.access, response.data?.access, ); @@ -281,7 +299,7 @@ async function authenticateToGetTokens() { const password = process.env.ENSEMBLE_PASSWORD; const response = await axios.post(ensembleAPI + '/token/', { username, password }); await IntegratingPartners.patchAccessAndRefreshTokens( - 'Ensemble Scientific', + ENSEMBLE_BRAND, response.data?.access, response.data?.access, ); @@ -330,9 +348,9 @@ async function unclaimSensor(org_id, external_id, access_token) { } export { - bulkSensorClaim, - registerOrganizationWebhook, - createOrganization, + ENSEMBLE_BRAND, + extractEsids, + registerFarmAndClaimSensors, unclaimSensor, ENSEMBLE_UNITS_MAPPING, };