From ae6ab03d3bb282d8f6730def3bd21a01ce2441bb 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 1/5] 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, }; From 61cb14c3a9640eae46f62aa2beee4ac09399a41a Mon Sep 17 00:00:00 2001 From: Joyce Yuki <82857964+kathyavini@users.noreply.github.com> Date: Fri, 3 Jan 2025 11:23:40 -0800 Subject: [PATCH 2/5] LF-4625 Update registerOrganizationWebhook and bulkSensorClaim for readability Remove unused returned values from registerOrganizationWebhook; un-nest code run when function does not return early; add comments to make more explicit how bulkSensorClaim response is defined from Ensemble API response --- packages/api/src/util/ensemble.js | 42 ++++++++++++++++--------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/packages/api/src/util/ensemble.js b/packages/api/src/util/ensemble.js index edca04f259..95a28c8720 100644 --- a/packages/api/src/util/ensemble.js +++ b/packages/api/src/util/ensemble.js @@ -96,6 +96,7 @@ async function bulkSensorClaim(accessToken, organizationId, esids) { data: { esids }, }; + // partial or complete failures (at least some esids failed to claim) const onError = (error) => { if (error.response?.data && error.response?.status) { return { ...error.response.data, status: error.response.status }; @@ -104,6 +105,7 @@ async function bulkSensorClaim(accessToken, organizationId, esids) { } }; + // full success (all esids successfully claimed) const onResponse = (response) => { return { success: esids, @@ -131,27 +133,27 @@ async function registerOrganizationWebhook(farmId, organizationId, accessToken) .where({ farm_id: farmId, partner_id: 1 }) .first(); if (existingIntegration?.webhook_id) { - return existingIntegration.webhook_id; - } else { - const axiosObject = { - method: 'post', - url: `${ensembleAPI}/organizations/${organizationId}/webhooks/`, - data: { - url: `${baseUrl}/sensor/reading/partner/1/farm/${farmId}`, - authorization_header: authHeader, - frequency: 15, - }, - }; - const onError = (error) => { - console.log(error); - throw new Error('Failed to register webhook with ESCI'); - }; - const onResponse = async (response) => { - await FarmExternalIntegrationsModel.updateWebhookId(farmId, response.data.id); - return { ...response.data, status: response.status }; - }; - return await ensembleAPICall(accessToken, axiosObject, onError, onResponse); + return; } + + const axiosObject = { + method: 'post', + url: `${ensembleAPI}/organizations/${organizationId}/webhooks/`, + data: { + url: `${baseUrl}/sensor/reading/partner/1/farm/${farmId}`, + authorization_header: authHeader, + frequency: 15, + }, + }; + const onError = (error) => { + console.log(error); + throw new Error('Failed to register webhook with ESCI'); + }; + const onResponse = async (response) => { + await FarmExternalIntegrationsModel.updateWebhookId(farmId, response.data.id); + return { ...response.data, status: response.status }; + }; + await ensembleAPICall(accessToken, axiosObject, onError, onResponse); } /** From 8503f814639f1c51e3a74bdcb93cc356ec39ab4e Mon Sep 17 00:00:00 2001 From: Joyce Yuki <82857964+kathyavini@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:52:45 -0800 Subject: [PATCH 3/5] LF-4625 Remove CSV parsing and take data directly from req.body --- .../api/src/controllers/sensorController.js | 278 +++++++----------- 1 file changed, 103 insertions(+), 175 deletions(-) diff --git a/packages/api/src/controllers/sensorController.js b/packages/api/src/controllers/sensorController.js index 170cb822a5..93b68068fd 100644 --- a/packages/api/src/controllers/sensorController.js +++ b/packages/api/src/controllers/sensorController.js @@ -23,7 +23,6 @@ import FarmExternalIntegrationsModel from '../models/farmExternalIntegrationsMod import LocationModel from '../models/locationModel.js'; import PointModel from '../models/pointModel.js'; import FigureModel from '../models/figureModel.js'; -import UserModel from '../models/userModel.js'; import PartnerReadingTypeModel from '../models/PartnerReadingTypeModel.js'; import { transaction, Model } from 'objection'; @@ -36,31 +35,10 @@ import { ENSEMBLE_UNITS_MAPPING, } from '../util/ensemble.js'; import { databaseUnit } from '../util/unit.js'; -import { sensorErrors, parseSensorCsv } from '../../../shared/validation/sensorCSV.js'; +import { sensorErrors } from '../../../shared/validation/sensorCSV.js'; import syncAsyncResponse from '../util/syncAsyncResponse.js'; import knex from '../util/knex.js'; -const getSensorTranslations = async (language) => { - // Remove country identifier from language preference - const parsedLanguage = language.includes('-') ? language.split('-')[0] : language; - let translations; - try { - translations = await import(`../../../shared/locales/${parsedLanguage}/sensorCSV.json`, { - assert: { type: 'json' }, - }); - // Default to english in case where user language not supported - if (!translations) { - throw 'Translations not found'; - } - } catch (error) { - console.log(error); - translations = await import(`../../../shared/locales/en/sensorCSV.json`, { - assert: { type: 'json' }, - }); - } - return translations.default; -}; - const sensorController = { async getSensorReadingTypes(req, res) { const { location_id } = req.params; @@ -122,20 +100,102 @@ const sensorController = { ENSEMBLE_BRAND, ); - //TODO: LF-4443 - Sensor should not use User language (unrestricted string), accept as body param or farm level detail - const [{ language_preference }] = await baseController.getIndividual(UserModel, user_id); + const data = req.body; - const translations = await getSensorTranslations(language_preference); - const { data, errors } = parseSensorCsv( - req.file.buffer.toString(), - language_preference, - translations, - ); + // 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) { + ({ success, already_owned, does_not_exist, occupied } = await registerFarmAndClaimSensors( + farm_id, + access_token, + esids, + )); + } + + 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 = []; + for (const sensor of registeredSensors) { + try { + const value = await SensorModel.createSensor( + sensor, + farm_id, + user_id, + esids.includes(sensor.external_id) ? 1 : 0, + ); + sensorLocations.push({ status: 'fulfilled', value }); + } catch (error) { + sensorLocations.push({ status: 'rejected', reason: error }); + } + } + + const successSensors = sensorLocations.reduce((prev, curr, idx) => { + if (curr.status === 'fulfilled') { + prev.push(curr.value); + } else { + // These are sensors that were not saved to the database as locations + errorSensors.push({ + row: data.findIndex((elem) => elem === registeredSensors[idx]) + 2, + translation_key: sensorErrors.INTERNAL_ERROR, + variables: { + sensorId: registeredSensors[idx].external_id || registeredSensors[idx].name, + }, + }); + } + return prev; + }, []); - if (errors.length > 0) { - return await sendResponse( + if (successSensors.length < data.length) { + return sendResponse( () => { - return res.status(400).send({ error_type: 'validation_failure', errors }); + return res.status(400).send({ + error_type: 'unable_to_claim_all_sensors', + success: successSensors, // We need the full sensor objects to update the redux store + errorSensors, + }); }, async () => { return await sendSensorNotification( @@ -144,162 +204,30 @@ const sensorController = { SensorNotificationTypes.SENSOR_BULK_UPLOAD_FAIL, { error_download: { - errors, + errors: errorSensors, file_name: 'sensor-upload-outcomes.txt', - error_type: 'validation', + success: successSensors.map((s) => s.sensor?.external_id || s.name), // Notification download needs an array of only ESIDs + error_type: 'claim', }, }, ); }, ); - } else if (!data.length > 0) { - return await sendResponse( + } else { + return sendResponse( () => { - return res.status(400).send({ error_type: 'empty_file' }); + return res + .status(200) + .send({ message: 'Successfully uploaded!', sensors: successSensors }); }, async () => { return await sendSensorNotification( user_id, farm_id, - SensorNotificationTypes.SENSOR_BULK_UPLOAD_FAIL, - { - error_download: { - errors: [], - file_name: 'sensor-upload-outcomes.txt', - error_type: 'generic', - }, - }, + SensorNotificationTypes.SENSOR_BULK_UPLOAD_SUCCESS, ); }, ); - } else { - // 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) { - ({ success, already_owned, does_not_exist, occupied } = await registerFarmAndClaimSensors( - farm_id, - access_token, - esids, - )); - } - - 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 = []; - for (const sensor of registeredSensors) { - try { - const value = await SensorModel.createSensor( - sensor, - farm_id, - user_id, - esids.includes(sensor.external_id) ? 1 : 0, - ); - sensorLocations.push({ status: 'fulfilled', value }); - } catch (error) { - sensorLocations.push({ status: 'rejected', reason: error }); - } - } - - const successSensors = sensorLocations.reduce((prev, curr, idx) => { - if (curr.status === 'fulfilled') { - prev.push(curr.value); - } else { - // These are sensors that were not saved to the database as locations - errorSensors.push({ - row: data.findIndex((elem) => elem === registeredSensors[idx]) + 2, - translation_key: sensorErrors.INTERNAL_ERROR, - variables: { - sensorId: registeredSensors[idx].external_id || registeredSensors[idx].name, - }, - }); - } - return prev; - }, []); - - if (successSensors.length < data.length) { - return sendResponse( - () => { - return res.status(400).send({ - error_type: 'unable_to_claim_all_sensors', - success: successSensors, // We need the full sensor objects to update the redux store - errorSensors, - }); - }, - async () => { - return await sendSensorNotification( - user_id, - farm_id, - SensorNotificationTypes.SENSOR_BULK_UPLOAD_FAIL, - { - error_download: { - errors: errorSensors, - file_name: 'sensor-upload-outcomes.txt', - success: successSensors.map((s) => s.sensor?.external_id || s.name), // Notification download needs an array of only ESIDs - error_type: 'claim', - }, - }, - ); - }, - ); - } else { - return sendResponse( - () => { - return res - .status(200) - .send({ message: 'Successfully uploaded!', sensors: successSensors }); - }, - async () => { - return await sendSensorNotification( - user_id, - farm_id, - SensorNotificationTypes.SENSOR_BULK_UPLOAD_SUCCESS, - ); - }, - ); - } } } catch (e) { console.log(e); From b0f00562f6891207884d63be45ab033d2fcec667 Mon Sep 17 00:00:00 2001 From: Joyce Yuki <82857964+kathyavini@users.noreply.github.com> Date: Thu, 16 Jan 2025 13:59:00 -0800 Subject: [PATCH 4/5] LF-4625 Add functionality to associate farm with Ensemble organization via organization_uuid --- .../api/src/controllers/sensorController.js | 38 +++++++++++++++++++ .../models/farmExternalIntegrationsModel.js | 14 +++++++ packages/api/src/routes/sensorRoute.js | 2 + packages/api/src/util/ensemble.js | 19 ++++++++++ 4 files changed, 73 insertions(+) diff --git a/packages/api/src/controllers/sensorController.js b/packages/api/src/controllers/sensorController.js index 93b68068fd..16eee26eb9 100644 --- a/packages/api/src/controllers/sensorController.js +++ b/packages/api/src/controllers/sensorController.js @@ -29,6 +29,7 @@ import { transaction, Model } from 'objection'; import { ENSEMBLE_BRAND, + getEnsembleOrganizations, extractEsids, registerFarmAndClaimSensors, unclaimSensor, @@ -84,6 +85,43 @@ const sensorController = { res.status(404).send('Partner not found'); } }, + async associateEnsembleOrganization(req, res) { + const { farm_id } = req.headers; + const { organization_uuid } = req.body; + + if (!organization_uuid || !organization_uuid.length) { + return res.status(400).send('Organization uuid required'); + } + + try { + const { access_token } = await IntegratingPartnersModel.getAccessAndRefreshTokens( + ENSEMBLE_BRAND, + ); + + const allRegisteredOrganizations = await getEnsembleOrganizations(access_token); + + const organization = allRegisteredOrganizations.find( + ({ uuid }) => uuid === organization_uuid, + ); + + if (!organization) { + return res.status(404).send('Organization not found'); + } + + await FarmExternalIntegrationsModel.upsertOrganizationIntegration({ + farm_id, + partner_id: 1, + organization_uuid, + }); + + return res.status(200).send(); + } catch (error) { + console.log(error); + return res.status(400).json({ + error, + }); + } + }, async addSensors(req, res) { let timeLimit = 5000; const testTimerOverride = Number(req.query?.sensorUploadTimer); diff --git a/packages/api/src/models/farmExternalIntegrationsModel.js b/packages/api/src/models/farmExternalIntegrationsModel.js index c88814f1e5..b94535cd40 100644 --- a/packages/api/src/models/farmExternalIntegrationsModel.js +++ b/packages/api/src/models/farmExternalIntegrationsModel.js @@ -100,6 +100,20 @@ class FarmExternalIntegrations extends Model { .where('partner_id', partnerId) .first(); } + + static async upsertOrganizationIntegration({ farm_id, partner_id, organization_uuid }) { + const existingIntegration = await this.query().findOne({ farm_id, partner_id }); + + if (existingIntegration) { + return this.query().patch({ organization_uuid }).where({ farm_id, partner_id }); + } else { + return this.query().insert({ + farm_id, + partner_id, + organization_uuid, + }); + } + } } export default FarmExternalIntegrations; diff --git a/packages/api/src/routes/sensorRoute.js b/packages/api/src/routes/sensorRoute.js index f570ea96e5..b4f076ab0f 100644 --- a/packages/api/src/routes/sensorRoute.js +++ b/packages/api/src/routes/sensorRoute.js @@ -35,6 +35,8 @@ router.post( SensorController.addSensors, ); +router.post('/associate_esci_org', SensorController.associateEnsembleOrganization); + router.delete('/:location_id', SensorController.deleteSensor); router.patch('/:location_id', SensorController.updateSensorbyID); router.post( diff --git a/packages/api/src/util/ensemble.js b/packages/api/src/util/ensemble.js index 95a28c8720..ab450c0aaa 100644 --- a/packages/api/src/util/ensemble.js +++ b/packages/api/src/util/ensemble.js @@ -156,6 +156,24 @@ async function registerOrganizationWebhook(farmId, organizationId, accessToken) await ensembleAPICall(accessToken, axiosObject, onError, onResponse); } +async function getEnsembleOrganizations(accessToken) { + try { + const axiosObject = { + method: 'get', + url: `${ensembleAPI}/organizations/`, + }; + const onError = () => { + throw new Error('Unable to fetch ESCI organization'); + }; + + const response = await ensembleAPICall(accessToken, axiosObject, onError); + + return response.data; + } catch (error) { + console.log(error); + } +} + /** * Creates a new ESCI organization if one does not already exist. * @param farmId @@ -351,6 +369,7 @@ async function unclaimSensor(org_id, external_id, access_token) { export { ENSEMBLE_BRAND, + getEnsembleOrganizations, extractEsids, registerFarmAndClaimSensors, unclaimSensor, From b97694850477e6871aa1c0996a74579e5fcd748c Mon Sep 17 00:00:00 2001 From: Joyce Yuki <82857964+kathyavini@users.noreply.github.com> Date: Mon, 20 Jan 2025 13:26:05 -0800 Subject: [PATCH 5/5] LF-4625 Rename controller and endpoint to use 'linking' terminology from Loic's screens --- packages/api/src/controllers/sensorController.js | 2 +- packages/api/src/routes/sensorRoute.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/api/src/controllers/sensorController.js b/packages/api/src/controllers/sensorController.js index 16eee26eb9..ecc8ddec4a 100644 --- a/packages/api/src/controllers/sensorController.js +++ b/packages/api/src/controllers/sensorController.js @@ -85,7 +85,7 @@ const sensorController = { res.status(404).send('Partner not found'); } }, - async associateEnsembleOrganization(req, res) { + async linkEnsembleOrganization(req, res) { const { farm_id } = req.headers; const { organization_uuid } = req.body; diff --git a/packages/api/src/routes/sensorRoute.js b/packages/api/src/routes/sensorRoute.js index b4f076ab0f..06f2d8967e 100644 --- a/packages/api/src/routes/sensorRoute.js +++ b/packages/api/src/routes/sensorRoute.js @@ -35,7 +35,7 @@ router.post( SensorController.addSensors, ); -router.post('/associate_esci_org', SensorController.associateEnsembleOrganization); +router.post('/link_esci_org', SensorController.linkEnsembleOrganization); router.delete('/:location_id', SensorController.deleteSensor); router.patch('/:location_id', SensorController.updateSensorbyID);