diff --git a/seeders/20211209205139-add-units.cjs b/seeders/20211209205139-add-units.cjs index dd8d7370..6217cd6b 100644 --- a/seeders/20211209205139-add-units.cjs +++ b/seeders/20211209205139-add-units.cjs @@ -1,17 +1,13 @@ 'use strict'; -/* TODO: Fix seeddata for latest models const UnitStub = require('../src/models/units/units.stub.json'); const LabelUnitStub = require('../src/models/labelUnits/labelUnits.stub.json'); -*/ module.exports = { // eslint-disable-next-line no-unused-vars up: async (queryInterface) => { - /* await queryInterface.bulkInsert('units', UnitStub, {}); await queryInterface.bulkInsert('label_unit', LabelUnitStub, {}); - */ }, down: async (queryInterface) => { diff --git a/src/controllers/project.controller.js b/src/controllers/project.controller.js index e28c4462..1507aeca 100644 --- a/src/controllers/project.controller.js +++ b/src/controllers/project.controller.js @@ -46,6 +46,32 @@ export const create = async (req, res) => { const { orgUid } = await Organization.getHomeOrg(); newRecord.orgUid = orgUid; + const childRecords = [ + 'projectLocations', + 'issuances', + 'coBenefits', + 'relatedProjects', + 'projectRatings', + 'estimations', + 'labels', + ]; + + childRecords.forEach((childRecordKey) => { + if (newRecord[childRecordKey]) { + newRecord[childRecordKey].map((childRecord) => { + childRecord.id = uuidv4(); + childRecord.orgUid = orgUid; + + // labels use a junction table + if (childRecordKey !== 'labels') { + childRecord.warehouseProjectId = uuid; + } + + return childRecord; + }); + } + }); + // The new project is getting created in this registry newRecord.currentRegistry = orgUid; diff --git a/src/controllers/staging.controller.js b/src/controllers/staging.controller.js index 909683ac..924dddc9 100644 --- a/src/controllers/staging.controller.js +++ b/src/controllers/staging.controller.js @@ -1,7 +1,10 @@ import _ from 'lodash'; import { Staging } from '../models'; -import { assertStagingRecordExists } from '../utils/data-assertions'; +import { + assertStagingRecordExists, + assertHomeOrgExists, +} from '../utils/data-assertions'; export const findAll = async (req, res) => { try { @@ -35,6 +38,8 @@ export const findAll = async (req, res) => { export const commit = async (req, res) => { try { + await assertHomeOrgExists(); + await Staging.pushToDataLayer(); res.json({ message: 'Staging Table committed to full node' }); } catch (error) { @@ -42,13 +47,12 @@ export const commit = async (req, res) => { message: 'Error commiting staging table', error: error.message, }); - - console.trace(error); } }; export const destroy = async (req, res) => { try { + await assertHomeOrgExists(); await assertStagingRecordExists(req.body.uuid); await Staging.destroy({ where: { @@ -68,6 +72,7 @@ export const destroy = async (req, res) => { export const clean = async (req, res) => { try { + await assertHomeOrgExists(); await Staging.destroy({ where: {}, truncate: true, diff --git a/src/controllers/units.controller.js b/src/controllers/units.controller.js index ff8769b0..c490bec1 100644 --- a/src/controllers/units.controller.js +++ b/src/controllers/units.controller.js @@ -39,6 +39,25 @@ export const create = async (req, res) => { const { orgUid } = await Organization.getHomeOrg(); newRecord.orgUid = orgUid; + if (newRecord.labels) { + newRecord.labels.map((childRecord) => { + childRecord.id = uuidv4(); + childRecord.orgUid = orgUid; + childRecord.label_unit = {}; + childRecord.label_unit.id = uuidv4(); + childRecord.label_unit.orgUid = orgUid; + childRecord.label_unit.warehouseUnitId = uuid; + childRecord.label_unit.labelId = childRecord.id; + + return childRecord; + }); + } + + if (newRecord.issuance) { + newRecord.labels.id = uuidv4(); + newRecord.labels.orgUid = orgUid; + } + const stagedData = { uuid, action: 'INSERT', diff --git a/src/fullnode/syncService.js b/src/fullnode/syncService.js index 84e7881a..1be7abcb 100644 --- a/src/fullnode/syncService.js +++ b/src/fullnode/syncService.js @@ -12,10 +12,9 @@ import { ProjectLocation, LabelUnit, Staging, + Rating, } from '../models'; -import { sequelize, sequelizeMirror } from '../models/database'; - import * as dataLayer from './persistance'; import * as simulator from './simulator'; @@ -63,63 +62,84 @@ export const syncDataLayerStoreToClimateWarehouse = async (storeId) => { try { // Create a transaction for both the main db and the mirror db - await sequelize.transaction(async () => { - return sequelizeMirror.transaction(async () => { - if (organizationToTrucate) { - await Promise.all([ - // the child table records should cascade delete so we only need to - // truncate the primary tables - Unit.destroy({ where: { orgUid: organizationToTrucate.orgUid } }), - Project.destroy({ - where: { orgUid: organizationToTrucate.orgUid }, - }), - LabelUnit.destroy({ - where: { orgUid: organizationToTrucate.orgUid }, - }), - ]); + //await sequelize.transaction(async () => { + // return sequelizeMirror.transaction(async () => { + if (organizationToTrucate) { + await Promise.all([ + // the child table records should cascade delete so we only need to + // truncate the primary tables + Unit.destroy({ where: { orgUid: organizationToTrucate.orgUid } }), + Project.destroy({ + where: { orgUid: organizationToTrucate.orgUid }, + }), + LabelUnit.destroy({ + where: { orgUid: organizationToTrucate.orgUid }, + }), + RelatedProject.destroy({ + where: { orgUid: organizationToTrucate.orgUid }, + }), + CoBenefit.destroy({ + where: { orgUid: organizationToTrucate.orgUid }, + }), + Issuance.destroy({ + where: { orgUid: organizationToTrucate.orgUid }, + }), + Label.destroy({ + where: { orgUid: organizationToTrucate.orgUid }, + }), + Rating.destroy({ + where: { orgUid: organizationToTrucate.orgUid }, + }), + ProjectLocation.destroy({ + where: { orgUid: organizationToTrucate.orgUid }, + }), + ]); + } + + await Promise.all( + storeData.keys_values.map(async (kv) => { + const key = new Buffer( + kv.key.replace(`${storeId}_`, ''), + 'hex', + ).toString(); + const value = JSON.parse(new Buffer(kv.value, 'hex').toString()); + if (key.includes('unit_')) { + await Unit.upsert(value); + await Staging.destroy({ + where: { uuid: value.warehouseUnitId }, + }); + } else if (key.includes('project_')) { + await Project.upsert(value); + await Staging.destroy({ + where: { uuid: value.warehouseProjectId }, + }); + } else if (key.includes('relatedProjects_')) { + await RelatedProject.upsert(value); + } else if (key.includes('label_units_')) { + await LabelUnit.upsert(value); + } else if (key.includes('coBenefits_')) { + await CoBenefit.upsert(value); + } else if (key.includes('issuances_')) { + await Issuance.upsert(value); + } else if (key.includes('projectLocations_')) { + console.log(value); + await ProjectLocation.upsert(value); + } else if (key.includes('labels_')) { + await Label.upsert(value); + } else if (key.includes('projectRatings_')) { + await Rating.upsert(value); } - - await Promise.all( - storeData.keys_values.map(async (kv) => { - const key = new Buffer( - kv.key.replace(`${storeId}_`, ''), - 'hex', - ).toString(); - const value = JSON.parse(new Buffer(kv.value, 'hex').toString()); - if (key.includes('unit')) { - await Unit.upsert(value); - await Staging.destroy({ - where: { uuid: value.warehouseUnitId }, - }); - } else if (key.includes('project')) { - await Project.upsert(value); - await Staging.destroy({ - where: { uuid: value.warehouseProjectId }, - }); - } else if (key.includes('relatedProjects')) { - await RelatedProject.upsert(value); - } else if (key.includes('labels_units')) { - await LabelUnit.upsert(value); - } else if (key.includes('coBenefits')) { - await CoBenefit.upsert(value); - } else if (key.includes('issuances')) { - await Issuance.upsert(value); - } else if (key.includes('projectLocations')) { - await ProjectLocation.upsert(value); - } else if (key.includes('labels')) { - await Label.upsert(value); - } - }), - ); - - // clean up any staging records than involved delete commands, - // since we cant track that they came in through the uuid, - // we can infer this because diff.original is null instead of empty object. - await Staging.cleanUpCommitedAndInvalidRecords(); - }); - }); + }), + ); + + // clean up any staging records than involved delete commands, + // since we cant track that they came in through the uuid, + // we can infer this because diff.original is null instead of empty object. + await Staging.cleanUpCommitedAndInvalidRecords(); + // }); + // }); } catch (error) { - console.log('ERROR DURING SYNC TRANSACTION', error); + console.trace('ERROR DURING SYNC TRANSACTION', error); } }; diff --git a/src/models/co-benefits/co-benefits.modeltypes.cjs b/src/models/co-benefits/co-benefits.modeltypes.cjs index dfcaa9e4..f9ae6642 100644 --- a/src/models/co-benefits/co-benefits.modeltypes.cjs +++ b/src/models/co-benefits/co-benefits.modeltypes.cjs @@ -4,6 +4,7 @@ const Sequelize = require('sequelize'); module.exports = { id: { type: Sequelize.STRING, + allowNull: false, unique: true, defaultValue: () => uuidv4(), primaryKey: true, diff --git a/src/models/estimations/estimations.modeltypes.cjs b/src/models/estimations/estimations.modeltypes.cjs index 9bd40868..97184858 100644 --- a/src/models/estimations/estimations.modeltypes.cjs +++ b/src/models/estimations/estimations.modeltypes.cjs @@ -4,6 +4,7 @@ const Sequelize = require('sequelize'); module.exports = { id: { type: Sequelize.STRING, + allowNull: false, unique: true, defaultValue: () => uuidv4(), primaryKey: true, diff --git a/src/models/issuances/issuances.modeltypes.cjs b/src/models/issuances/issuances.modeltypes.cjs index 860033e2..b1430a51 100644 --- a/src/models/issuances/issuances.modeltypes.cjs +++ b/src/models/issuances/issuances.modeltypes.cjs @@ -4,6 +4,7 @@ const Sequelize = require('sequelize'); module.exports = { id: { type: Sequelize.STRING, + allowNull: false, unique: true, defaultValue: () => uuidv4(), primaryKey: true, diff --git a/src/models/labelUnits/labelUnits.modeltypes.cjs b/src/models/labelUnits/labelUnits.modeltypes.cjs index 8f705e83..45b3a37f 100644 --- a/src/models/labelUnits/labelUnits.modeltypes.cjs +++ b/src/models/labelUnits/labelUnits.modeltypes.cjs @@ -1,6 +1,14 @@ +const { uuid: uuidv4 } = require('uuidv4'); const Sequelize = require('sequelize'); module.exports = { + id: { + type: Sequelize.STRING, + allowNull: false, + unique: true, + defaultValue: () => uuidv4(), + primaryKey: true, + }, orgUid: { type: Sequelize.STRING, required: true, diff --git a/src/models/labelUnits/labelUnits.stub.json b/src/models/labelUnits/labelUnits.stub.json index cf6c75f3..0db63edd 100644 --- a/src/models/labelUnits/labelUnits.stub.json +++ b/src/models/labelUnits/labelUnits.stub.json @@ -1,10 +1,12 @@ [ { + "id": "5c960ac1-a180-45a4-9850-be177e26d2fb", "warehouseUnitId": "5c960ac1-a180-45a4-9850-be177e26d2fb", "labelId": "702cafbb-c624-4273-9cdc-c617ad5675df", "orgUid": "f1c54511-865e-4611-976c-7c3c1f704662" }, { + "id": "f1c54511-865e-4611-976c-7c3c1f704662", "warehouseUnitId": "5c960ac1-a180-45a4-9850-be177e26d2fb", "labelId": "76903895-840e-406c-b2a0-f90244acf02d", "orgUid": "f1c54511-865e-4611-976c-7c3c1f704662" diff --git a/src/models/labels/labels.modeltypes.cjs b/src/models/labels/labels.modeltypes.cjs index d39da171..ff188a2d 100644 --- a/src/models/labels/labels.modeltypes.cjs +++ b/src/models/labels/labels.modeltypes.cjs @@ -4,6 +4,7 @@ const Sequelize = require('sequelize'); module.exports = { id: { type: Sequelize.STRING, + allowNull: false, unique: true, defaultValue: () => uuidv4(), primaryKey: true, @@ -30,7 +31,7 @@ module.exports = { type: Sequelize.DATE, require: true, }, - validityStartDate: { + validityPeriodStartDate: { type: Sequelize.DATE, require: true, }, diff --git a/src/models/labels/labels.stub.json b/src/models/labels/labels.stub.json index 1d7ee9a4..45af024f 100644 --- a/src/models/labels/labels.stub.json +++ b/src/models/labels/labels.stub.json @@ -6,7 +6,7 @@ "warehouseProjectId": "11954678-f7a5-47d2-94f8-f4f3138a529c", "creditingPeriodStartDate": "2011-10-05T14:48:00.000Z", "creditingPeriodEndDate": "2022-10-05T14:48:00.000Z", - "validityStartDate": "2022-01-18 00:05:45.701 +00:00", + "validityPeriodStartDate": "2022-01-18 00:05:45.701 +00:00", "validityPeriodEndDate": "2022-01-18 00:05:45.701 +00:00", "unitQuantity": 5, "labelLink": "https://label.link/1", diff --git a/src/models/locations/locations.modeltypes.cjs b/src/models/locations/locations.modeltypes.cjs index 2ecf5da6..609bc35a 100644 --- a/src/models/locations/locations.modeltypes.cjs +++ b/src/models/locations/locations.modeltypes.cjs @@ -4,6 +4,7 @@ const Sequelize = require('sequelize'); module.exports = { id: { type: Sequelize.STRING, + allowNull: false, unique: true, defaultValue: () => uuidv4(), primaryKey: true, diff --git a/src/models/projects/projects.modeltypes.cjs b/src/models/projects/projects.modeltypes.cjs index 629f76bd..bdc41e51 100644 --- a/src/models/projects/projects.modeltypes.cjs +++ b/src/models/projects/projects.modeltypes.cjs @@ -4,6 +4,7 @@ const Sequelize = require('sequelize'); module.exports = { warehouseProjectId: { type: Sequelize.STRING, + allowNull: false, unique: true, defaultValue: () => uuidv4(), primaryKey: true, @@ -26,7 +27,7 @@ module.exports = { type: Sequelize.STRING, required: true, }, - // Need to add 'originProjectID' field and make it a required field with type STRING. + // Need to add 'originProjectID' field and make it a required field with type STRING. // This field could be the exact same as 'projectID', but it could also be different. Both are required fields. program: { type: Sequelize.STRING, @@ -62,7 +63,7 @@ module.exports = { ndcInformation: { type: Sequelize.STRING, required: true, - // 'ndcInformation' field should be optional. + // 'ndcInformation' field should be optional. }, projectStatus: { type: Sequelize.STRING, diff --git a/src/models/ratings/ratings.modeltypes.cjs b/src/models/ratings/ratings.modeltypes.cjs index a4df75e8..3da30b24 100644 --- a/src/models/ratings/ratings.modeltypes.cjs +++ b/src/models/ratings/ratings.modeltypes.cjs @@ -4,6 +4,7 @@ const Sequelize = require('sequelize'); module.exports = { id: { type: Sequelize.STRING, + allowNull: false, unique: true, defaultValue: () => uuidv4(), primaryKey: true, diff --git a/src/models/related-projects/related-projects.modeltypes.cjs b/src/models/related-projects/related-projects.modeltypes.cjs index d7ab579e..236d7baa 100644 --- a/src/models/related-projects/related-projects.modeltypes.cjs +++ b/src/models/related-projects/related-projects.modeltypes.cjs @@ -4,6 +4,7 @@ const Sequelize = require('sequelize'); module.exports = { id: { type: Sequelize.STRING, + allowNull: false, unique: true, defaultValue: () => uuidv4(), primaryKey: true, diff --git a/src/models/units/units.model.js b/src/models/units/units.model.js index 682ae7ca..8f34ce61 100644 --- a/src/models/units/units.model.js +++ b/src/models/units/units.model.js @@ -294,6 +294,8 @@ class Unit extends Model { true, ); + console.log(insertXslsSheets); + const updateXslsSheets = createXlsFromSequelizeResults( updateRecords, Unit, @@ -304,7 +306,7 @@ class Unit extends Model { const primaryKeyMap = { unit: 'warehouseUnitId', labels: 'id', - label_units: 'labelunitId', + label_units: 'id', issuances: 'id', }; diff --git a/src/models/units/units.modeltypes.cjs b/src/models/units/units.modeltypes.cjs index 9e618836..ba885c62 100644 --- a/src/models/units/units.modeltypes.cjs +++ b/src/models/units/units.modeltypes.cjs @@ -4,6 +4,7 @@ const Sequelize = require('sequelize'); module.exports = { warehouseUnitId: { type: Sequelize.STRING, + allowNull: false, unique: true, defaultValue: () => uuidv4(), primaryKey: true, diff --git a/src/models/units/units.stub.json b/src/models/units/units.stub.json index ce756eff..0fca568b 100644 --- a/src/models/units/units.stub.json +++ b/src/models/units/units.stub.json @@ -6,123 +6,19 @@ "countryJurisdictionOfOwner": "USA", "serialNumberBlock": "AXJJFSLGHSHEJ1000-AXJJFSLGHSHEJ1010", "serialNumberPattern": "[.*\\D]+([0-9]+)+[-][.*\\D]+([0-9]+)$", - "unitIdentifier": "XYZ", "unitType": "heard reduction", - "intendedBuyerOrgUid": "70150fde-57f6-44a6-9486-1fef49528475", "marketplace": "Demo Marketplace", - "tags": "Demo, Wind Energy", + "unitTags": "Demo, Wind Energy", "inCountryJurisdictionOfOwner": "Maryland", "unitStatus": "Held", - "unitTransactionType": "TRANSACTION_TYPE", "unitStatusReason": "Retired Status", - "tokenIssuanceHash": "0x668d82fd009d805e99559eaf6ed43c38fc835aa5ce4fdf64796f32c47682d1ab", "marketplaceIdentifier": "AKFEE3", "unitRegistryLink": "http://climateWarehouse.com/myRegistry", - "unitMarketplaceLink": "http://climateWarehouse.com/myMarketplace", + "marketplaceLink": "http://climateWarehouse.com/myMarketplace", "correspondingAdjustmentDeclaration": "Commited", "correspondingAdjustmentStatus": "Not Started", "issuanceId": "a6745831-5d5e-45ed-b9fe-fd6aa129df25", "createdAt": "2022-01-18 00:05:45.701 +00:00", "updatedAt": "2022-01-18 00:05:45.701 +00:00" - }, - { - "warehouseUnitId": "50aa22fe-20c4-4af9-a9e9-8472e73a9222", - "orgUid": "f1c54511-865e-4611-976c-7c3c1f704662", - "unitOwner": "f1c54511-865e-4611-976c-7c3c1f704662", - "countryJurisdictionOfOwner": "USA", - "serialNumberBlock": "AXJJFSLGHSHEJ2000-AXJJFSLGHSHEJ2010", - "serialNumberPattern": "[.*\\D]+([0-9]+)+[-][.*\\D]+([0-9]+)$", - "unitIdentifier": "XYZ", - "unitType": "heard reduction", - "intendedBuyerOrgUid": "70150fde-57f6-44a6-9486-1fef49528475", - "marketplace": "Demo Marketplace", - "tags": "Demo, Wind Energy", - "inCountryJurisdictionOfOwner": "Maryland", - "unitStatus": "Held", - "unitTransactionType": "TRANSACTION_TYPE", - "unitStatusReason": "Retired Status", - "tokenIssuanceHash": "0x668d82fd009d805e99559eaf6ed43c38fc835aa5ce4fdf64796f32c47682d1ab", - "marketplaceIdentifier": "AKFEE3", - "unitRegistryLink": "http://climateWarehouse.com/myRegistry", - "unitMarketplaceLink": "http://climateWarehouse.com/myMarketplace", - "correspondingAdjustmentDeclaration": "Commited", - "correspondingAdjustmentStatus": "Not Started", - "createdAt": "2022-01-18 00:05:45.701 +00:00", - "updatedAt": "2022-01-18 00:05:45.701 +00:00" - }, - { - "warehouseUnitId": "7703e6be-1781-4322-9a9c-502ca0dca29b", - "orgUid": "35f92331-c8d7-4e9e-a8d2-cd0a86cbb2cf", - "unitOwner": "f1c54511-865e-4611-976c-7c3c1f704662", - "countryJurisdictionOfOwner": "USA", - "serialNumberBlock": "AXJJFSLGHSHEJ3000-AXJJFSLGHSHEJ3010", - "serialNumberPattern": "[.*\\D]+([0-9]+)+[-][.*\\D]+([0-9]+)$", - "unitIdentifier": "XYZ", - "unitType": "heard reduction", - "intendedBuyerOrgUid": "70150fde-57f6-44a6-9486-1fef49528475", - "marketplace": "Demo Marketplace", - "tags": "Demo, Wind Energy", - "inCountryJurisdictionOfOwner": "Maryland", - "unitStatus": "Held", - "unitTransactionType": "TRANSACTION_TYPE", - "unitStatusReason": "Retired Status", - "tokenIssuanceHash": "0x668d82fd009d805e99559eaf6ed43c38fc835aa5ce4fdf64796f32c47682d1ab", - "marketplaceIdentifier": "AKFEE3", - "unitRegistryLink": "http://climateWarehouse.com/myRegistry", - "unitMarketplaceLink": "http://climateWarehouse.com/myMarketplace", - "correspondingAdjustmentDeclaration": "Commited", - "correspondingAdjustmentStatus": "Not Started", - "createdAt": "2022-01-18 00:05:45.701 +00:00", - "updatedAt": "2022-01-18 00:05:45.701 +00:00" - }, - { - "warehouseUnitId": "04582672-47a9-411b-896f-3d45974e360e", - "orgUid": "f1c54511-865e-4611-976c-7c3c1f704662", - "unitOwner": "f1c54511-865e-4611-976c-7c3c1f704662", - "countryJurisdictionOfOwner": "USA", - "serialNumberBlock": "AXJJFSLGHSHEJ4000-AXJJFSLGHSHEJ4010", - "serialNumberPattern": "[.*\\D]+([0-9]+)+[-][.*\\D]+([0-9]+)$", - "unitIdentifier": "XYZ", - "unitType": "heard reduction", - "intendedBuyerOrgUid": "70150fde-57f6-44a6-9486-1fef49528475", - "marketplace": "Demo Marketplace", - "tags": "Demo, Wind Energy", - "inCountryJurisdictionOfOwner": "Maryland", - "unitStatus": "Held", - "unitTransactionType": "TRANSACTION_TYPE", - "unitStatusReason": "Retired Status", - "tokenIssuanceHash": "0x668d82fd009d805e99559eaf6ed43c38fc835aa5ce4fdf64796f32c47682d1ab", - "marketplaceIdentifier": "AKFEE3", - "unitRegistryLink": "http://climateWarehouse.com/myRegistry", - "unitMarketplaceLink": "http://climateWarehouse.com/myMarketplace", - "correspondingAdjustmentDeclaration": "Commited", - "correspondingAdjustmentStatus": "Not Started", - "createdAt": "2022-01-18 00:05:45.701 +00:00", - "updatedAt": "2022-01-18 00:05:45.701 +00:00" - }, - { - "warehouseUnitId": "cdc90991-a00d-4567-a09a-cbe5430fdc56", - "orgUid": "35f92331-c8d7-4e9e-a8d2-cd0a86cbb2cf", - "unitOwner": "f1c54511-865e-4611-976c-7c3c1f704662", - "serialNumberBlock": "AXJJFSLGHSHEJ5000-AXJJFSLGHSHEJ5010", - "serialNumberPattern": "[.*\\D]+([0-9]+)+[-][.*\\D]+([0-9]+)$", - "unitIdentifier": "XYZ", - "unitType": "heard reduction", - "countryJurisdictionOfOwner": "USA", - "inCountryJurisdictionOfOwner": "Maryland", - "intendedBuyerOrgUid": "70150fde-57f6-44a6-9486-1fef49528475", - "marketplace": "Demo Marketplace", - "tags": "Demo, Wind Energy", - "unitStatus": "Held", - "unitTransactionType": "TRANSACTION_TYPE", - "unitStatusReason": "Retired Status", - "tokenIssuanceHash": "0x668d82fd009d805e99559eaf6ed43c38fc835aa5ce4fdf64796f32c47682d1ab", - "marketplaceIdentifier": "AKFEE3", - "unitRegistryLink": "http://climateWarehouse.com/myRegistry", - "unitMarketplaceLink": "http://climateWarehouse.com/myMarketplace", - "correspondingAdjustmentDeclaration": "Commited", - "correspondingAdjustmentStatus": "Not Started", - "createdAt": "2022-01-18 00:05:45.701 +00:00", - "updatedAt": "2022-01-18 00:05:45.701 +00:00" } ] diff --git a/src/routes/index.js b/src/routes/index.js index 4887163f..e1949626 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -28,7 +28,6 @@ app.use((err, req, res, next) => { return res.status(400).json({ message: 'API Validation error', errors: err.error.details.map((detail) => { - console.log(detail); return _.get(detail, 'context.message', detail.message); }), }); diff --git a/src/utils/xls.js b/src/utils/xls.js index 11805dcd..4aa58c4a 100644 --- a/src/utils/xls.js +++ b/src/utils/xls.js @@ -189,44 +189,42 @@ export const transformFullXslsToChangeList = ( return item === primaryKeyNames[key]; }); - changeList[key] = []; + if (!changeList[key]) { + changeList[key] = []; + } - sheet.data.forEach((row) => { + // filter out the header row + _.tail(sheet.data).forEach((row) => { const rows = checkArrayOfArrays(row) ? row : [row]; - return ( - rows - // filter out the header row - .filter((r) => r[primaryKeyIndex] !== headerRow[primaryKeyIndex]) - .forEach((r) => { - const dataLayerKey = Buffer.from( - `${key}_${r[primaryKeyIndex]}`, - ).toString('hex'); - - if (action === 'update') { - changeList[key].push( - { - action: 'delete', - key: dataLayerKey, - }, - { - action: 'insert', - key: dataLayerKey, - value: Buffer.from( - JSON.stringify(_.zipObject(headerRow, r)), - ).toString('hex'), - }, - ); - } else { - changeList[key].push({ - action: action, - key: dataLayerKey, - value: Buffer.from( - JSON.stringify(_.zipObject(headerRow, r)), - ).toString('hex'), - }); - } - }) - ); + return rows.forEach((r) => { + const dataLayerKey = Buffer.from( + `${key}_${r[primaryKeyIndex]}`, + ).toString('hex'); + + if (action === 'update') { + changeList[key].push( + { + action: 'delete', + key: dataLayerKey, + }, + { + action: 'insert', + key: dataLayerKey, + value: Buffer.from( + JSON.stringify(_.zipObject(headerRow, r)), + ).toString('hex'), + }, + ); + } else { + changeList[key].push({ + action: action, + key: dataLayerKey, + value: Buffer.from( + JSON.stringify(_.zipObject(headerRow, r)), + ).toString('hex'), + }); + } + }); }); } }); diff --git a/src/validations/co-benefits.validations.js b/src/validations/co-benefits.validations.js index 1aa115e4..68f5fe44 100644 --- a/src/validations/co-benefits.validations.js +++ b/src/validations/co-benefits.validations.js @@ -1,7 +1,8 @@ import Joi from 'joi'; export const cobenefitSchema = Joi.object({ + // orgUid - derived upon creation + // warehouseProjectId - derived upon creation id: Joi.string().optional(), cobenefit: Joi.string().required(), - // cobenefit should be optional. }); diff --git a/src/validations/estimations.validations.js b/src/validations/estimations.validations.js index f9429c6f..86c3335b 100644 --- a/src/validations/estimations.validations.js +++ b/src/validations/estimations.validations.js @@ -1,6 +1,6 @@ import Joi from 'joi'; -const baseSchema = { +export const estimationSchema = Joi.object({ // orgUid - derived upon creation // warehouseProjectId - derived upon creation creditingPeriodStart: Joi.date().required(), @@ -9,14 +9,4 @@ const baseSchema = { .min(Joi.ref('startDate')) .required(), unitCount: Joi.number().integer().required(), - verificationDate: Joi.date().required(), - verificationBody: Joi.string().required(), - // Not sure where 'verificationDate' and 'verificationBody' fields are coming from. They are not supposed to be in the estimations table. -}; - -export const newEstimationSchema = Joi.object({ ...baseSchema }); - -export const existingEstimationSchema = Joi.object({ - id: Joi.string().required(), - ...baseSchema, }); diff --git a/src/validations/issuances.validation.js b/src/validations/issuances.validation.js index f23ded1d..6352739d 100644 --- a/src/validations/issuances.validation.js +++ b/src/validations/issuances.validation.js @@ -2,11 +2,12 @@ import Joi from 'joi'; export const issuanceSchema = Joi.object({ // orgUid - derived upon creation - // warehouseProjectId - derived upon creation id: Joi.string().optional(), + warehouseProjectId: Joi.string().optional(), startDate: Joi.date().required(), - endDate: Joi.date().timestamp().min(Joi.ref('startDate')).required(), + endDate: Joi.date().min(Joi.ref('startDate')).required(), verificationApproach: Joi.string().required(), verificationDate: Joi.date().required(), verificationBody: Joi.string().required(), + verificationReportDate: Joi.date().required(), }); diff --git a/src/validations/labels.validations.js b/src/validations/labels.validations.js index d9160bea..d5a6f152 100644 --- a/src/validations/labels.validations.js +++ b/src/validations/labels.validations.js @@ -1,17 +1,20 @@ import Joi from 'joi'; export const labelSchema = Joi.object({ + // orgUid - derived upon creation + // warehouseProjectId - derived upon creation id: Joi.string().optional(), + warehouseProjectId: Joi.string().optional(), label: Joi.string().required(), - // Need to include 'labelType' as a required STRING - creditingPeriodStartDate: Joi.string().required(), - // This should be DATE instead of STRING. - creditingPeriodEndDate: Joi.string().required(), - // This should be DATE instead of STRING. + labelType: Joi.string().required(), + creditingPeriodStartDate: Joi.date().required(), + creditingPeriodEndDate: Joi.date() + .min(Joi.ref('creditingPeriodStartDate')) + .required(), validityPeriodStartDate: Joi.string().required(), - // This should be DATE instead of STRING. - validityPeriodEndDate: Joi.string().required(), - // This should be DATE instead of STRING. + validityPeriodEndDate: Joi.date() + .min(Joi.ref('validityPeriodStartDate')) + .required(), unitQuantity: Joi.number().integer().required(), labelLink: Joi.string().required(), }); diff --git a/src/validations/locations.validations.js b/src/validations/locations.validations.js index ef8c0c39..44a77749 100644 --- a/src/validations/locations.validations.js +++ b/src/validations/locations.validations.js @@ -1,6 +1,8 @@ import Joi from 'joi'; export const locationSchema = Joi.object({ + // orgUid - derived upon creation + // warehouseProjectId - derived upon creation id: Joi.string().optional(), country: Joi.string().required(), inCountryRegion: Joi.string().required(), diff --git a/src/validations/projects.validations.js b/src/validations/projects.validations.js index 629c9305..4a89e14b 100644 --- a/src/validations/projects.validations.js +++ b/src/validations/projects.validations.js @@ -6,6 +6,7 @@ import { relatedProjectSchema, labelSchema, issuanceSchema, + estimationSchema, } from '../validations'; export const baseSchema = { @@ -43,6 +44,7 @@ export const baseSchema = { relatedProjects: Joi.array().items(relatedProjectSchema).min(1).optional(), projectLocations: Joi.array().items(locationSchema).min(1).optional(), projectRatings: Joi.array().items(ratingSchema).min(1).optional(), + estimations: Joi.array().items(estimationSchema).min(1).optional(), }; export const projectsGetQuerySchema = Joi.object() diff --git a/src/validations/ratings.validations.js b/src/validations/ratings.validations.js index 0f14e7ad..5fadd0de 100644 --- a/src/validations/ratings.validations.js +++ b/src/validations/ratings.validations.js @@ -1,11 +1,19 @@ import Joi from 'joi'; export const ratingSchema = Joi.object({ + // orgUid - derived upon creation + // warehouseProjectId - derived upon creation id: Joi.string().optional(), ratingType: Joi.string().required(), - ratingRangeHighest: Joi.number().integer().required(), ratingRangeLowest: Joi.number().integer().required(), - rating: Joi.number().integer().required(), - ratingLink: Joi.string().optional(), - //'ratingLink' should be required. + ratingRangeHighest: Joi.number() + .integer() + .min(Joi.ref('ratingRangeLowest')) + .required(), + rating: Joi.number() + .integer() + .min(Joi.ref('ratingRangeLowest')) + .max(Joi.ref('ratingRangeHighest')) + .required(), + ratingLink: Joi.string().required(), }); diff --git a/src/validations/relatedProjects.validations.js b/src/validations/relatedProjects.validations.js index 65cde2dc..4a3b2234 100644 --- a/src/validations/relatedProjects.validations.js +++ b/src/validations/relatedProjects.validations.js @@ -1,6 +1,7 @@ import Joi from 'joi'; export const relatedProjectSchema = Joi.object({ + // orgUid - derived upon creation // warehouseProjectId - derived upon creation id: Joi.string().optional(), // Need to add 'relatedProjectId' as an optional field with STRING type. diff --git a/tests/test-data/new-project.json b/tests/test-data/new-project.json index 19dfd113..bcf95b25 100644 --- a/tests/test-data/new-project.json +++ b/tests/test-data/new-project.json @@ -1,90 +1,70 @@ -[ - { - "currentRegistry": "TEST", - "projectId": "c9d147e2-bc07-4e68-a76d-43424fa8cd4e", - "registryOfOrigin": "TEST", - "program": "Eimbee", - "projectName": "Zoomcast", - "projectLink": "http://dailymotion.com/ligula/suspendisse/ornare/consequat/lectus/in.jpg?aliquam=consequat&lacus=nulla&morbi=nisl&quis=nunc&tortor=nisl&id=duis&nulla=bibendum&ultrices=felis&aliquet=sed&maecenas=interdum&leo=venenatis&odio=turpis&condimentum=enim&id=blandit&luctus=mi&nec=in&molestie=porttitor&sed=pede&justo=justo&pellentesque=eu&viverra=massa&pede=donec&ac=dapibus&diam=duis&cras=at&pellentesque=velit&volutpat=eu&dui=est&maecenas=congue&tristique=elementum&est=in&et=hac&tempus=habitasse&semper=platea&est=dictumst&quam=morbi&pharetra=vestibulum&magna=velit&ac=id&consequat=pretium&metus=iaculis&sapien=diam&ut=erat&nunc=fermentum&vestibulum=justo&ante=nec&ipsum=condimentum", - "projectDeveloper": "Cogibox", - "sector": "Viva", - "projectType": "Topicshots", - "projectTags": "Kaymbo", - "coveredByNDC": "TEST", - "ndcInformation": "TEST", - "projectStatus": "in magna bibendum imperdiet nullam orci pede venenatis non sodales sed tincidunt eu felis fusce posuere felis sed lacus", - "projectStatusDate": null, - "unitMetric": "Fuscia", - "methodology": "Quatz", - "validationBody": null, - "validationDate": null, - "createdAt": "2022-01-18T00:05:45.701Z", - "updatedAt": "2022-01-18T00:05:45.701Z", - "projectLocations": [ - { - "warehouseProjectId": "11954678-f7a5-47d2-94f8-f4f3138a529c", - "country": "United States", - "inCountryRegion": "United States", - "geographicIdentifier": "TEST", - "createdAt": "2022-01-18T00:05:45.701Z", - "updatedAt": "2022-01-18T00:05:45.701Z" - } - ], - "labels": [ - { - "warehouseProjectId": "11954678-f7a5-47d2-94f8-f4f3138a529c", - "label": "label 1", - "creditingPeriodStartDate": null, - "creditingPeriodEndDate": null, - "validityStartDate": "2022-01-18T00:05:45.701Z", - "validityPeriodEndDate": "2022-01-18T00:05:45.701Z", - "unitQuantity": 5, - "labelLink": "https://label.link/1", - "createdAt": "2022-01-18T00:05:45.701Z", - "updatedAt": "2022-01-18T00:05:45.701Z" - } - ], - "issuances": [ - { - "warehouseProjectId": "11954678-f7a5-47d2-94f8-f4f3138a529c", - "startDate": "2019-02-03T00:05:45.701Z", - "endDate": "2029-03-12T00:05:45.701Z", - "verificationApproach": "TEST", - "verificationReportDate": "2022-01-18T00:05:45.701Z", - "verificationBody": "This is verified", - "createdAt": "2022-01-18T00:05:45.701Z", - "updatedAt": "2022-01-18T00:05:45.701Z" - } - ], - "coBenefits": [ - { - "warehouseProjectId": "11954678-f7a5-47d2-94f8-f4f3138a529c", - "cobenefit": "TEST_COBENEFIT_1", - "createdAt": "2022-01-18T00:05:45.701Z", - "updatedAt": "2022-01-18T00:05:45.701Z" - } - ], - "relatedProjects": [ - { - "warehouseProjectId": "11954678-f7a5-47d2-94f8-f4f3138a529c", - "relationshipType": null, - "registry": null, - "createdAt": "2022-01-18T00:05:45.701Z", - "updatedAt": "2022-01-18T00:05:45.701Z" - } - ], - "projectRatings": [ - { - "warehouseProjectId": "11954678-f7a5-47d2-94f8-f4f3138a529c", - "ratingType": "number", - "ratingRangeHighest": 1, - "ratingRangeLowest": 10, - "rating": 5, - "ratingLink": null, - "createdAt": "2022-01-18T00:05:45.701Z", - "updatedAt": "2022-01-18T00:05:45.701Z" - } - ], - "estimations": [] - } -] +{ + "currentRegistry": "TEST", + "projectId": "c9d147e2-bc07-4e68-a76d-43424fa8cd4e", + "registryOfOrigin": "TEST", + "program": "Eimbee", + "projectName": "Zoomcast", + "projectLink": "http://dailymotion.com/ligula/suspendisse/ornare/consequat/lectus/in.jpg?aliquam=consequat&lacus=nulla&morbi=nisl&quis=nunc&tortor=nisl&id=duis&nulla=bibendum&ultrices=felis&aliquet=sed&maecenas=interdum&leo=venenatis&odio=turpis&condimentum=enim&id=blandit&luctus=mi&nec=in&molestie=porttitor&sed=pede&justo=justo&pellentesque=eu&viverra=massa&pede=donec&ac=dapibus&diam=duis&cras=at&pellentesque=velit&volutpat=eu&dui=est&maecenas=congue&tristique=elementum&est=in&et=hac&tempus=habitasse&semper=platea&est=dictumst&quam=morbi&pharetra=vestibulum&magna=velit&ac=id&consequat=pretium&metus=iaculis&sapien=diam&ut=erat&nunc=fermentum&vestibulum=justo&ante=nec&ipsum=condimentum", + "projectDeveloper": "Cogibox", + "sector": "Viva", + "projectType": "Topicshots", + "projectTags": "Kaymbo", + "coveredByNDC": "TEST", + "ndcInformation": "TEST", + "projectStatus": "in magna bibendum imperdiet nullam orci pede venenatis non sodales sed tincidunt eu felis fusce posuere felis sed lacus", + "projectStatusDate": "2022-01-18", + "unitMetric": "Fuscia", + "methodology": "Quatz", + "validationBody": "TEST", + "validationDate": "TEST", + "projectLocations": [ + { + "country": "United States", + "inCountryRegion": "United States", + "geographicIdentifier": "TEST" + } + ], + "labels": [ + { + "label": "label 1", + "labelType": "TEST", + "creditingPeriodStartDate": "2022-01-18T00:05:45.701Z", + "creditingPeriodEndDate": "2022-01-18T00:05:45.701Z", + "validityPeriodStartDate": "2022-01-18T00:05:45.701Z", + "validityPeriodEndDate": "2022-01-18T00:05:45.701Z", + "unitQuantity": 5, + "labelLink": "https://label.link/1" + } + ], + "issuances": [ + { + "startDate": "2019-02-03T00:05:45.701Z", + "endDate": "2029-03-12T00:05:45.701Z", + "verificationApproach": "TEST", + "verificationReportDate": "2022-01-18T00:05:45.701Z", + "verificationBody": "This is verified", + "verificationDate": "2029-03-12T00:05:45.701Z" + } + ], + "coBenefits": [ + { + "cobenefit": "TEST_COBENEFIT_1" + } + ], + "relatedProjects": [ + { + "relationshipType": "TEST", + "registry": "TEST" + } + ], + "projectRatings": [ + { + "ratingType": "number", + "ratingRangeHighest": 1, + "ratingRangeLowest": 10, + "rating": 5, + "ratingLink": "TEST" + } + ], + "estimations": [] +} diff --git a/tests/test-data/new-unit.json b/tests/test-data/new-unit.json new file mode 100644 index 00000000..11f30a1d --- /dev/null +++ b/tests/test-data/new-unit.json @@ -0,0 +1,41 @@ +{ + "unitOwner": "f1c54511-865e-4611-976c-7c3c1f704662", + "countryJurisdictionOfOwner": "USA", + "projectLocationId": "TEST", + "inCountryJurisdictionOfOwner": "Maryland", + "serialNumberBlock": "AXJJFSLGHSHEJ1000-AXJJFSLGHSHEJ1010", + "serialNumberPattern": "[.*\\D]+([0-9]+)+[-][.*\\D]+([0-9]+)$", + "vintageYear": 1998, + "unitType": "heard reduction", + "marketplace": "Demo Marketplace", + "marketplaceLink": "http://climateWarehouse.com/myMarketplace", + "marketplaceIdentifier": "AKFEE3", + "unitTags": "Demo, Wind Energy", + "unitStatus": "Held", + "unitStatusReason": "Retired Status", + "unitRegistryLink": "http://climateWarehouse.com/myRegistry", + "correspondingAdjustmentDeclaration": "Commited", + "correspondingAdjustmentStatus": "Not Started", + "labels": [ + { + "warehouseProjectId": "11954678-f7a5-47d2-94f8-f4f3138a529c", + "label": "label 1", + "labelType": "TEST", + "creditingPeriodStartDate": "2022-01-10T00:05:45.701Z", + "creditingPeriodEndDate": "2022-01-18T00:05:45.701Z", + "validityPeriodStartDate": "2022-01-18T00:05:45.701Z", + "validityPeriodEndDate": "2022-01-18T00:05:45.701Z", + "unitQuantity": 5, + "labelLink": "https://label.link/1" + } + ], + "issuance": { + "warehouseProjectId": "11954678-f7a5-47d2-94f8-f4f3138a529c", + "startDate": "2019-02-03T00:05:45.701Z", + "endDate": "2029-03-12T00:05:45.701Z", + "verificationApproach": "TEST", + "verificationReportDate": "2022-01-18T00:05:45.701Z", + "verificationBody": "This is verified", + "verificationDate": "2029-03-12T00:05:45.701Z" + } +}