From b23b6a15d9aad6b22b96335803befafd7a5f84a0 Mon Sep 17 00:00:00 2001 From: Frantz Arty Date: Mon, 29 Aug 2022 08:26:14 -0400 Subject: [PATCH] feat: add transfer project endpoint --- src/controllers/project.controller.js | 38 +++++++++++++++++-- .../20220825124702-add-isTransfer-column.js | 23 +++++++++++ src/datalayer/syncService.js | 14 +++++++ src/models/staging/staging.modeltypes.cjs | 5 +++ src/routes/v1/resources/projects.js | 6 +++ 5 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/database/migrations/20220825124702-add-isTransfer-column.js diff --git a/src/controllers/project.controller.js b/src/controllers/project.controller.js index c49211d9..8c0e9437 100644 --- a/src/controllers/project.controller.js +++ b/src/controllers/project.controller.js @@ -22,6 +22,7 @@ import { assertNoPendingCommits, assertRecordExistance, assertIfReadOnlyMode, + assertStagingTableIsEmpty, } from '../utils/data-assertions'; import { createProjectRecordsFromCsv } from '../utils/csv-utils'; @@ -248,7 +249,19 @@ export const updateFromXLS = async (req, res) => { } }; -export const update = async (req, res) => { +export const transfer = async (req, res) => { + try { + await assertStagingTableIsEmpty(); + return update(req, res, true); + } catch (err) { + res.status(400).json({ + message: err.message, + }); + logger.error('Error adding update to stage', err); + } +}; + +const update = async (req, res, isTransfer = false) => { try { await assertIfReadOnlyMode(); await assertHomeOrgExists(); @@ -258,12 +271,19 @@ export const update = async (req, res) => { req.body.warehouseProjectId, ); - await assertOrgIsHomeOrg(originalRecord.orgUid); + // transfers can operate on project records that belong to someone else + // none transfers operations must belong to your orgUid + if (!isTransfer) { + await assertOrgIsHomeOrg(originalRecord.orgUid); + } const newRecord = _.cloneDeep(req.body); - const { orgUid } = await Organization.getHomeOrg(); - newRecord.orgUid = orgUid; + // Why do we need this? the orgUid should already be in the record + //const { orgUid } = await Organization.getHomeOrg(); + //newRecord.orgUid = orgUid; + + const { orgUid } = newRecord; const childRecordsKeys = [ 'projectLocations', @@ -316,8 +336,16 @@ export const update = async (req, res) => { action: 'UPDATE', table: Project.stagingTableName, data: JSON.stringify(stagedRecord), + // If this is a transfer staging, push it directly into commited status + // Since we are downloading a offer file and shouldnt be allowed to operate + // until that file is either accepted or cancelled + commited: isTransfer, }; + if (isTransfer) { + stagedData.isTransfer = true; + } + await Staging.upsert(stagedData); res.json({ @@ -331,6 +359,8 @@ export const update = async (req, res) => { } }; +export { update }; + export const destroy = async (req, res) => { try { await assertIfReadOnlyMode(); diff --git a/src/database/migrations/20220825124702-add-isTransfer-column.js b/src/database/migrations/20220825124702-add-isTransfer-column.js new file mode 100644 index 00000000..82390a68 --- /dev/null +++ b/src/database/migrations/20220825124702-add-isTransfer-column.js @@ -0,0 +1,23 @@ +'use strict'; + +export default { + async up(queryInterface, Sequelize) { + await Promise.all( + ['staging'].map((table) => { + queryInterface.addColumn(table, 'isTransfer', { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: false, + }); + }), + ); + }, + + async down(queryInterface) { + await Promise.all( + ['staging'].map((table) => { + queryInterface.removeColumn(table, 'isTransfer'); + }), + ); + }, +}; diff --git a/src/datalayer/syncService.js b/src/datalayer/syncService.js index 515ccca3..024320d4 100644 --- a/src/datalayer/syncService.js +++ b/src/datalayer/syncService.js @@ -164,6 +164,20 @@ const dataLayerWasUpdated = async () => { }), ); + // When a transfer is made, the climate warehouse is locked from making updates + // while waiting for the transfer to either be completed or rejected. + // This means that we know the transfer completed when the root hash changed + // and we can remove it from the pending staging table. + const homeOrg = organizations.find((org) => org.isHome === 1); + if (homeOrg) { + const updatedHomeInfo = updateStoreInfo.find( + (info) => info.storeId === homeOrg.registryId, + ); + if (updatedHomeInfo.rootHash !== homeOrg.registryHash) { + await Staging.destroy({ where: { isTransfer: true } }); + } + } + return updateStoreInfo; }; diff --git a/src/models/staging/staging.modeltypes.cjs b/src/models/staging/staging.modeltypes.cjs index 425c0a63..0d24368d 100644 --- a/src/models/staging/staging.modeltypes.cjs +++ b/src/models/staging/staging.modeltypes.cjs @@ -23,6 +23,11 @@ module.exports = { allowNull: false, defaultValue: false, }, + isTransfer: { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: false, + }, createdAt: { type: Sequelize.DATE, defaultValue: Sequelize.NOW, diff --git a/src/routes/v1/resources/projects.js b/src/routes/v1/resources/projects.js index 6ce37f48..1d026448 100644 --- a/src/routes/v1/resources/projects.js +++ b/src/routes/v1/resources/projects.js @@ -32,6 +32,12 @@ ProjectRouter.put( ProjectController.update, ); +ProjectRouter.put( + '/transfer', + validator.body(projectsUpdateSchema), + ProjectController.transfer, +); + ProjectRouter.put('/xlsx', ProjectController.updateFromXLS); ProjectRouter.delete(