diff --git a/migrations/20220122141750-create-simulator-table.cjs b/migrations/20220122141750-create-simulator-table.cjs new file mode 100644 index 00000000..a19dc2f5 --- /dev/null +++ b/migrations/20220122141750-create-simulator-table.cjs @@ -0,0 +1,13 @@ +'use strict'; + +const modelTypes = require('../src/models/simulator/simulator.modeltypes.cjs'); + +module.exports = { + async up(queryInterface) { + await queryInterface.createTable('simulator', modelTypes); + }, + + async down(queryInterface) { + await queryInterface.dropTable('simulator'); + }, +}; diff --git a/migrations/20220122164836-create-organizatin-tablec.js b/migrations/20220122164836-create-organizatin-tablec.js new file mode 100644 index 00000000..e94b78c2 --- /dev/null +++ b/migrations/20220122164836-create-organizatin-tablec.js @@ -0,0 +1,13 @@ +'use strict'; + +const modelTypes = require('../src/models/organizations/organizations.modeltypes.cjs'); + +module.exports = { + async up(queryInterface) { + await queryInterface.createTable('organizations', modelTypes); + }, + + async down(queryInterface) { + await queryInterface.dropTable('organizations'); + }, +}; diff --git a/package-lock.json b/package-lock.json index 25a5a4e9..a8869a8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "joi": "^17.5.0", "lodash": "^4.17.21", "mysql2": "^2.3.3", + "random-hash": "^4.0.1", "request-promise": "^4.2.6", "rxjs": "^7.5.1", "sequelize": "^6.12.0-alpha.1", @@ -12640,6 +12641,11 @@ "integrity": "sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==", "dev": true }, + "node_modules/random-hash": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/random-hash/-/random-hash-4.0.1.tgz", + "integrity": "sha1-NC19FFAeZk8L7i2aE+6unJc8t1U=" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -25082,6 +25088,11 @@ "integrity": "sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==", "dev": true }, + "random-hash": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/random-hash/-/random-hash-4.0.1.tgz", + "integrity": "sha1-NC19FFAeZk8L7i2aE+6unJc8t1U=" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", diff --git a/package.json b/package.json index 96fde78b..eb85127d 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "joi": "^17.5.0", "lodash": "^4.17.21", "mysql2": "^2.3.3", + "random-hash": "^4.0.1", "request-promise": "^4.2.6", "rxjs": "^7.5.1", "sequelize": "^6.12.0-alpha.1", diff --git a/seeders/20220121232631-add-test-organization.cjs b/seeders/20220121232631-add-test-organization.cjs new file mode 100644 index 00000000..b725cf72 --- /dev/null +++ b/seeders/20220121232631-add-test-organization.cjs @@ -0,0 +1,26 @@ +'use strict'; +const dotenv = require('dotenv'); +dotenv.config(); + +const metaStub = [ + { + metaKey: 'organizationId', + metaValue: 'f1c54511-865e-4611-976c-7c3c1f704662', + }, + { + metaKey: 'organizationName', + metaValue: 'Demo Org', + }, +]; + +module.exports = { + up: async (queryInterface) => { + if (process.env.USE_SIMULATOR === 'true') { + await queryInterface.bulkInsert('meta', metaStub, {}); + } + }, + + down: async (queryInterface) => { + await queryInterface.bulkDelete('meta'); + }, +}; diff --git a/src/controllers/organization.controller.js b/src/controllers/organization.controller.js index c7bf2ce2..846643e5 100644 --- a/src/controllers/organization.controller.js +++ b/src/controllers/organization.controller.js @@ -1,13 +1,16 @@ import { Organization } from '../models/organizations'; -import { updateOrganization } from '../fullnode/dataLayerService'; + export const findAll = async (req, res) => { return res.json(await Organization.getOrgsMap()); }; export const create = async (req, res) => { - const { name, icon, website } = req.body; + const { name, icon } = req.body; return res.json({ message: 'New organization created successfully.', - orgId: await updateOrganization(name, icon, website), + orgId: await Organization.createHomeOrganization(name, icon, 'v1'), }); }; + +// eslint-disable-next-line +export const importOrg = async (req, res) => {}; diff --git a/src/controllers/staging.controller.js b/src/controllers/staging.controller.js index f078240b..5ded4cb2 100644 --- a/src/controllers/staging.controller.js +++ b/src/controllers/staging.controller.js @@ -1,12 +1,7 @@ import _ from 'lodash'; -import * as fullNode from '../fullnode'; import { Staging, Project, Unit } from '../models'; -import { - assertStagingRecordExists, - assertUnitRecordExists, - assertProjectRecordExists, -} from '../utils/data-assertions'; +import { assertStagingRecordExists } from '../utils/data-assertions'; export const findAll = async (req, res) => { try { @@ -76,69 +71,15 @@ export const findAll = async (req, res) => { export const commit = async (req, res) => { try { - const queryResponses = await Staging.findAll(); - - await Promise.all( - queryResponses.map(async (queryResponse) => { - const stagingRecord = queryResponse.dataValues; - - const { - id: stagingRecordId, - uuid, - table, - action, - commited, - data: rawData, - } = stagingRecord; - let data = JSON.parse(rawData); - - if (table === 'Projects' && !commited) { - const customAssertionMessage = `The project record for the warehouseProjectId: ${uuid} does not exist. Please remove ${uuid} from the staging table and try to commit again.`; - switch (action) { - case 'INSERT': - data.warehouseUnitId = uuid; - fullNode.createProjectRecord(uuid, data, stagingRecordId); - break; - case 'UPDATE': - await assertProjectRecordExists(uuid, customAssertionMessage); - fullNode.updateProjectRecord(uuid, data, stagingRecordId); - break; - case 'DELETE': - await assertProjectRecordExists(uuid, customAssertionMessage); - fullNode.deleteProjectRecord(uuid, stagingRecordId); - break; - } - } else if (table === 'Units' && !commited) { - const customAssertionMessage = `The unit record for the warehouseUnitId: ${uuid} does not exist. Please remove ${uuid} from the staging table and try to commit again.`; - switch (action) { - case 'INSERT': - fullNode.createUnitRecord(uuid, data, stagingRecordId); - break; - case 'UPDATE': - await assertUnitRecordExists(uuid, customAssertionMessage); - fullNode.updateUnitRecord(uuid, data, stagingRecordId); - break; - case 'DELETE': - await assertUnitRecordExists(uuid, customAssertionMessage); - fullNode.deleteUnitRecord(uuid, stagingRecordId); - break; - } - } - - // set the commited flag to true - await Staging.update( - { commited: true }, - { where: { id: stagingRecordId } }, - ); - }), - ); - + await Staging.pushToDataLayer(); res.json({ message: 'Staging Table committed to full node' }); } catch (error) { res.status(400).json({ message: 'Error commiting staging table', error: error.message, }); + + console.trace(error); } }; @@ -176,135 +117,3 @@ export const clean = async (req, res) => { }); } }; - -export const commitV2 = async (req, res) => { - try { - const queryResponses = await Staging.findAll(); - - const changeList = []; - - await Promise.all( - queryResponses.map(async (queryResponse) => { - const stagingRecord = queryResponse.dataValues; - - const { - id: stagingRecordId, - uuid, - table, - action, - commited, - data: rawData, - } = stagingRecord; - let data = JSON.parse(rawData); - - if (table === 'Projects' && !commited) { - const customAssertionMessage = `The project record for the warehouseProjectId: ${uuid} does not exist. Please remove ${uuid} from the staging table and try to commit again.`; - const projectRecord = _.omit(data, 'qualifications', 'vintages'); - // const qualificationsRecords = data.qualifications; - // const vintageRecords = data.vintages; - - console.log('!@@'); - - switch (action) { - case 'INSERT': - data.warehouseProjectId = uuid; - - changeList.push([ - { - action: 'delete', - key: new Buffer(`project_${uuid}`).toString('hex'), - }, - { - action: 'insert', - key: new Buffer(`project_${uuid}`).toString('hex'), - value: new Buffer(JSON.stringify(projectRecord)).toString( - 'hex', - ), - }, - ]); - break; - case 'UPDATE': - await assertProjectRecordExists(uuid, customAssertionMessage); - changeList.push([ - { - action: 'delete', - key: new Buffer(`project_${uuid}`).toString('hex'), - }, - { - action: 'insert', - key: new Buffer(`project_${uuid}`).toString('hex'), - value: new Buffer(JSON.stringify(data)).toString('hex'), - }, - ]); - break; - case 'DELETE': - await assertProjectRecordExists(uuid, customAssertionMessage); - changeList.push([ - { - action: 'delete', - key: new Buffer(`project_${uuid}`).toString('hex'), - }, - ]); - break; - } - } else if (table === 'Units' && !commited) { - const customAssertionMessage = `The unit record for the warehouseUnitId: ${uuid} does not exist. Please remove ${uuid} from the staging table and try to commit again.`; - const unitRecord = _.omit(data, 'qualifications', 'vintage'); - switch (action) { - case 'INSERT': - changeList.push({ - action: 'delete', - key: new Buffer(`unit_${uuid}`).toString('hex'), - }); - changeList.push({ - action: 'insert', - key: new Buffer(`unit_${uuid}`).toString('hex'), - value: new Buffer(JSON.stringify(unitRecord)).toString('hex'), - }); - - break; - case 'UPDATE': - await assertUnitRecordExists(uuid, customAssertionMessage); - changeList.push([ - { - action: 'delete', - key: new Buffer(`unit_${uuid}`).toString('hex'), - }, - { - action: 'insert', - key: new Buffer(`unit_${uuid}`).toString('hex'), - value: new Buffer(JSON.stringify(unitRecord)).toString('hex'), - }, - ]); - break; - case 'DELETE': - await assertUnitRecordExists(uuid, customAssertionMessage); - changeList.push([ - { - action: 'delete', - key: new Buffer(`unit_${uuid}`).toString('hex'), - }, - ]); - break; - } - } - - // set the commited flag to true - await Staging.update( - { commited: true }, - { where: { id: stagingRecordId } }, - ); - }), - ); - - console.log(changeList); - await fullNode.pushChangeListToRegistryTable('units', changeList); - - res.json({ message: 'Staging Table committed to full node' }); - } catch (error) { - res.status(400).json({ - message: 'Error commiting staging table', - error: error.message, - }); - } -}; diff --git a/src/controllers/units.controller.js b/src/controllers/units.controller.js index 55e20b2f..29f31ec6 100644 --- a/src/controllers/units.controller.js +++ b/src/controllers/units.controller.js @@ -36,7 +36,7 @@ export const create = async (req, res) => { newRecord.warehouseUnitId = uuid; // All new units are assigned to the home orgUid - const orgUid = _.head(Object.keys(await Organization.getHomeOrg())); + const { orgUid } = await Organization.getHomeOrg(); newRecord.orgUid = orgUid; newRecord.unitOwnerOrgUid = orgUid; diff --git a/src/fullnode/data-layer-utils.js b/src/fullnode/data-layer-utils.js new file mode 100644 index 00000000..9b88894c --- /dev/null +++ b/src/fullnode/data-layer-utils.js @@ -0,0 +1,28 @@ +export const changeListFactory = (action, id, record) => { + console.log({ action, id, record }); + switch (action) { + case 'INSERT': + return { + action: 'insert', + key: Buffer.from(id).toString('hex'), + value: Buffer.from(JSON.stringify(record)).toString('hex'), + }; + case 'UPDATE': + return [ + { + action: 'delete', + key: Buffer.from(id).toString('hex'), + }, + { + action: 'insert', + key: Buffer.from(id).toString('hex'), + value: Buffer.from(JSON.stringify(record)).toString('hex'), + }, + ]; + case 'DELETE': + return { + action: 'delete', + key: Buffer.from(id).toString('hex'), + }; + } +}; diff --git a/src/fullnode/dataLayerReadService.js b/src/fullnode/dataLayerReadService.js new file mode 100644 index 00000000..74fcc75d --- /dev/null +++ b/src/fullnode/dataLayerReadService.js @@ -0,0 +1,69 @@ +import _ from 'lodash'; +import { Sequelize } from 'sequelize'; + +import { Meta } from '../models'; +import * as dataLayer from './persistance'; +import * as simulator from './simulatorV2'; + +const Op = Sequelize.Op; + +let updateInterval; + +export const startDataLayerUpdatePolling = () => { + console.log('Start Dataayer Update Polling'); + /* updateInterval = setInterval(async () => { + const tablesToUpdate = await dataLayerWasUpdated(); + _.keys(tablesToUpdate).forEach((tableMetaId) => {}); + }, 10000);*/ +}; + +export const stopDataLayerUpdatePolling = () => { + clearInterval(updateInterval); +}; + +export const dataLayerWasUpdated = async () => { + const tableIdsMeta = await Meta.findAll({ + where: { + metaKey: { + [Op.like]: '%TableStoreId', + }, + }, + raw: true, + }); + + const tableHashesMeta = await Meta.findAll({ + where: { + metaKey: { + [Op.like]: '%TableStoreHash', + }, + }, + raw: true, + }); + + const tableHashMap = {}; + + tableIdsMeta.forEach((meta) => { + tableHashMap[meta.metaKey] = null; + }); + + tableHashesMeta.forEach((meta) => { + const tableKey = meta.metaKey.replace('Hash', 'Id'); + tableHashMap[tableKey] = meta.metaValue; + }); + + let newHashes; + if (process.env.USE_SIMULATOR === 'true') { + newHashes = await simulator.getRoots(_.keys(tableHashMap)); + } else { + newHashes = await dataLayer.getRoots(_.keys(tableHashMap)); + } + + console.log(newHashes); + + const tablesWereUpdatedMap = {}; + _.keys(tableHashMap).map((key, index) => { + tablesWereUpdatedMap[key] = tableHashMap[key] !== newHashes[index]; + }); + + return tablesWereUpdatedMap; +}; diff --git a/src/fullnode/dataLayerWriteService.js b/src/fullnode/dataLayerWriteService.js new file mode 100644 index 00000000..57326f8a --- /dev/null +++ b/src/fullnode/dataLayerWriteService.js @@ -0,0 +1,37 @@ +import * as dataLayer from './persistance'; +import * as simulator from './simulatorV2'; + +export const createDataLayerStore = async () => { + let storeId; + if (process.env.USE_SIMULATOR === 'true') { + storeId = await simulator.createDataLayerStore(); + } else { + storeId = await dataLayer.createDataLayerStore(); + } + + return storeId; +}; + +export const syncDataLayer = async (storeId, data) => { + const changeList = Object.keys(data).map((key) => { + return { + action: 'insert', + key, + value: data[key], + }; + }); + + if (process.env.USE_SIMULATOR === 'true') { + return simulator.pushChangeListToDataLayer(storeId, changeList); + } else { + return dataLayer.pushChangeListToDataLayer(storeId, changeList); + } +}; + +export const pushDataLayerChangeList = async (storeId, changeList) => { + if (process.env.USE_SIMULATOR === 'true') { + return simulator.pushChangeListToDataLayer(storeId, changeList); + } else { + return dataLayer.pushChangeListToDataLayer(storeId, changeList); + } +}; diff --git a/src/fullnode/dataLayerService.js b/src/fullnode/dataLayerWriteServiceOld.js similarity index 57% rename from src/fullnode/dataLayerService.js rename to src/fullnode/dataLayerWriteServiceOld.js index 35156490..5bda33d1 100644 --- a/src/fullnode/dataLayerService.js +++ b/src/fullnode/dataLayerWriteServiceOld.js @@ -1,14 +1,12 @@ -import { Meta } from '../models'; +import { Meta, Organization } from '../models'; import * as dataLayer from './persistance'; import * as simulator from './simulatorV2'; const strToHex = (str) => { - console.log('str', str); return new Buffer(str).toString('hex'); }; const createChangeObject = (type, key, value) => { - console.log(type, key, value); return { action: type, key: strToHex(key), value: strToHex(value) }; }; @@ -17,8 +15,6 @@ const ensureDataLayerStore = async (metaKey) => { where: { metaKey }, }); - console.log('@@@', storeMeta); - if (!storeMeta) { let storeId; if (process.env.USE_SIMULATOR === 'true') { @@ -27,9 +23,7 @@ const ensureDataLayerStore = async (metaKey) => { storeId = await dataLayer.createDataLayerStore(); } - console.log({ [metaKey]: storeId }); - - await Meta.create({ [metaKey]: storeId }); + await Meta.create({ metaKey, metaValue: storeId }); return storeId; } @@ -37,74 +31,42 @@ const ensureDataLayerStore = async (metaKey) => { }; const ensureRegistryStore = async (orgUid) => { - const storeId = await ensureDataLayerStore('registryId'); + const storeId = await ensureDataLayerStore(`registryId`); const changeList = [createChangeObject('insert', 'registryId', storeId)]; - console.log('#####orgUid', orgUid, changeList); - - let registryId; if (process.env.USE_SIMULATOR === 'true') { - registryId = await simulator.pushChangeListToDataLayer(orgUid, changeList); + await simulator.pushChangeListToDataLayer(orgUid, changeList); } else { - registryId = await dataLayer.pushChangeListToDataLayer(orgUid, changeList); + await dataLayer.pushChangeListToDataLayer(orgUid, changeList); } - await Meta.create({ registryId }); - return registryId; + + return storeId; }; const ensureRegistryTableStore = async (tableName) => { - const orgUid = await ensureOrganizationStore(); + const myOrganization = await Organization.findOne({ + where: { isHome: true }, + raw: true, + }); + + const orgUid = myOrganization.orgUid; const registryId = await ensureRegistryStore(orgUid); - const metaKey = `${tableName}StoreId`; + const metaKey = `${tableName}TableStoreId`; const storeId = await ensureDataLayerStore(metaKey); const changeList = [createChangeObject('insert', metaKey, storeId)]; - let registryTableStoreId; if (process.env.USE_SIMULATOR === 'true') { - registryTableStoreId = await simulator.pushChangeListToDataLayer( - registryId, - changeList, - ); + await simulator.pushChangeListToDataLayer(registryId, changeList); } else { - registryTableStoreId = await dataLayer.pushChangeListToDataLayer( - registryId, - changeList, - ); + await dataLayer.pushChangeListToDataLayer(registryId, changeList); } - await Meta.create({ [metaKey]: registryTableStoreId }); - - console.log('3############', registryTableStoreId); - return registryTableStoreId; -}; - -const ensureOrganizationStore = async () => { - const metaKey = 'organizationId'; - const storeMeta = await Meta.findOne({ - where: { metaKey }, - }); - - if (!storeMeta) { - let storeId; - - if (process.env.USE_SIMULATOR === 'true') { - storeId = await simulator.createDataLayerStore(); - } else { - storeId = await dataLayer.createDataLayerStore(); - } - - await Meta.upsert({ metaKey: 'organizationId', metaValue: storeId }); - return storeId; - } - - return storeMeta.metaValue; + return storeId; }; export const pushChangeListToRegistryTable = async (tableName, changeList) => { const storeId = await ensureRegistryTableStore(tableName); - console.log('########', storeId); - if (storeId) { if (process.env.USE_SIMULATOR === 'true') { return simulator.pushChangeListToDataLayer(storeId, changeList); @@ -116,18 +78,8 @@ export const pushChangeListToRegistryTable = async (tableName, changeList) => { throw new Error('Could not create datalayer store'); }; -export const getRegistryTableData = async (tableName) => { - const storeId = await ensureDataLayerStore(tableName); - - if (process.env.USE_SIMULATOR === 'true') { - return simulator.getStoreData(storeId); - } else { - return dataLayer.getStoreData(storeId); - } -}; - export const updateOrganization = async (orgName, orgIconUrl, orgWebSite) => { - const orgUid = await ensureOrganizationStore(); + const orgUid = await ensureDataLayerStore(); const changeList = []; const metaUpdateList = []; diff --git a/src/fullnode/index.js b/src/fullnode/index.js index 5b1d0cd6..e9e13781 100644 --- a/src/fullnode/index.js +++ b/src/fullnode/index.js @@ -1,2 +1,2 @@ -export * from './dispatcher'; -export * from './dataLayerService'; +export * from './dataLayerWriteService'; +export * from './dataLayerReadService'; diff --git a/src/fullnode/persistance.js b/src/fullnode/persistance.js index 9e52b6dd..25be8c6e 100644 --- a/src/fullnode/persistance.js +++ b/src/fullnode/persistance.js @@ -34,11 +34,11 @@ export const createDataLayerStore = async () => { throw new Error('Error creating new datalayer store'); }; -export const pushChangeListToDataLayer = async (storeId, changeList) => { +export const pushChangeListToDataLayer = async (storeId, changelist) => { const options = { url: `${rpcUrl}/update_data_store`, body: JSON.stringify({ - changelist: changeList, + changelist, id: storeId, }), }; @@ -52,6 +52,21 @@ export const pushChangeListToDataLayer = async (storeId, changeList) => { throw new Error('Error updating datalayer store'); }; +export const getRoot = async (storeId) => { + const options = { + url: `${rpcUrl}/get_root`, + body: JSON.stringify({ + id: storeId, + }), + }; + + const response = request(Object.assign({}, baseOptions, options)); + + if (response.body && response.body.keys_values) { + return response.body.keys_values; + } +}; + export const getStoreData = async (storeId) => { if (storeId) { const options = { diff --git a/src/fullnode/simulatorV2.js b/src/fullnode/simulatorV2.js index 9b1477a4..0c5642ea 100644 --- a/src/fullnode/simulatorV2.js +++ b/src/fullnode/simulatorV2.js @@ -1,25 +1,33 @@ import { uuid as uuidv4 } from 'uuidv4'; -import { Meta } from '../models'; +import { Simulator } from '../models'; import { Sequelize } from 'sequelize'; +import { RandomHash } from 'random-hash'; +import { randomBytes } from 'crypto'; const Op = Sequelize.Op; +const generateHash = new RandomHash({ + length: 55, + charset: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_', + rng: randomBytes, +}); + export const createDataLayerStore = async () => { return uuidv4(); }; -export const pushChangeListToDataLayer = (storeId, changeList) => { - console.log('!!!!!', storeId, changeList); - return Promise.all( +export const pushChangeListToDataLayer = async (storeId, changeList) => { + console.log(storeId, changeList); + await Promise.all( changeList.map(async (change) => { if (change.action === 'insert') { - await Meta.create({ - metaKey: `${storeId}_${change.key}`, - metaValue: change.value, + await Simulator.upsert({ + key: `simulator_${storeId}_${change.key}`, + value: change.value, }); } else if (change.action === 'delete') { - await Meta.destroy({ - where: { metaKey: `simulator_${storeId}_${change.key}` }, + await Simulator.destroy({ + where: { key: `simulator_${storeId}_${change.key}` }, }); } }), @@ -28,14 +36,15 @@ export const pushChangeListToDataLayer = (storeId, changeList) => { export const getStoreData = async (storeId) => { if (storeId) { - const results = await await Meta.findAll({ + const results = await await Simulator.findAll({ where: { - metaKey: { [Op.like]: `simulator_${storeId}%` }, + key: { [Op.like]: `${storeId}%` }, }, }); // return the store data in a form that mirrors that datalayer response return { + root: `0x${generateHash()}`, keys_values: results.map((result) => { const simulatedResult = result; simulatedResult.hash = result.metaValue.split('').reduce((a, b) => { @@ -52,3 +61,19 @@ export const getStoreData = async (storeId) => { return new Error('Error getting datalayer store data'); }; + +// eslint-disable-next-line +export const getRoot = (storeId) => { + return Promise.resolve({ + // fake hash + hash: `0x${generateHash()}`, + success: true, + }); +}; + +export const getRoots = (storeIds) => { + return Promise.resolve({ + hash: storeIds.map(() => `0x${generateHash()}`), + success: true, + }); +}; diff --git a/src/models/co-benefits/co-benefits.model.js b/src/models/co-benefits/co-benefits.model.js index 28003c25..d307f7b8 100644 --- a/src/models/co-benefits/co-benefits.model.js +++ b/src/models/co-benefits/co-benefits.model.js @@ -33,6 +33,17 @@ class CoBenefit extends Model { safeMirrorDbHandler(() => CoBenefitMirror.destroy(values)); return super.destroy(values); } + + static async generateChangeListFromStagedData( + // eslint-disable-next-line + action, + // eslint-disable-next-line + id, + // eslint-disable-next-line + stagedData, + ) { + return {}; + } } CoBenefit.init(ModelTypes, { diff --git a/src/models/index.js b/src/models/index.js index 19262ccf..db521fb3 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -27,3 +27,4 @@ export * from './vintages'; export * from './staging'; export * from './organizations'; export * from './meta'; +export * from './simulator'; diff --git a/src/models/locations/locations.model.js b/src/models/locations/locations.model.js index 2ae636fa..16e042ed 100644 --- a/src/models/locations/locations.model.js +++ b/src/models/locations/locations.model.js @@ -35,6 +35,17 @@ class ProjectLocation extends Model { safeMirrorDbHandler(() => ProjectLocationMirror.destroy(values)); return super.destroy(values); } + + static async generateChangeListFromStagedData( + // eslint-disable-next-line + action, + // eslint-disable-next-line + id, + // eslint-disable-next-line + stagedData, + ) { + return {}; + } } ProjectLocation.init(ModelTypes, { diff --git a/src/models/meta/meta.model.js b/src/models/meta/meta.model.js index fc478237..1b076ada 100644 --- a/src/models/meta/meta.model.js +++ b/src/models/meta/meta.model.js @@ -11,6 +11,7 @@ class Meta extends Model {} Meta.init(ModelTypes, { sequelize, modelName: 'meta', + freezeTableName: true, timestamps: false, createdAt: false, updatedAt: false, diff --git a/src/models/organizations/organizations.model.js b/src/models/organizations/organizations.model.js index 17cf72c7..dd7a1d2e 100644 --- a/src/models/organizations/organizations.model.js +++ b/src/models/organizations/organizations.model.js @@ -3,60 +3,126 @@ import Sequelize from 'sequelize'; const { Model } = Sequelize; import { sequelize } from '../database'; -import { Meta } from '../../models'; +import { createDataLayerStore, syncDataLayer } from '../../fullnode'; import ModelTypes from './organizations.modeltypes.cjs'; class Organization extends Model { static async getHomeOrg() { - const organizationRecord = await Meta.findOne({ - where: { metaKey: 'organizationId' }, - }); - const organizationNameRecord = await Meta.findOne({ - where: { metaKey: 'organizationName' }, - }); - const organizationIconRecord = await Meta.findOne({ - where: { metaKey: 'organizationIconUrl' }, + const myOrganization = await Organization.findOne({ + attributes: ['orgUid', 'name', 'icon'], + where: { isHome: true }, + raw: true, }); - if (organizationRecord) { - let homeOrg = { [organizationRecord.metaValue]: { writeAccess: true } }; + if (myOrganization) { + return myOrganization; + } - if (organizationNameRecord) { - homeOrg[organizationRecord.metaValue].name = - organizationNameRecord.metaValue; - } + return undefined; + } - if (organizationIconRecord) { - homeOrg[organizationRecord.metaValue].icon = - organizationIconRecord.metaValue; + static async getOrgsMap() { + const organizations = Organization.findOne({ + attributes: ['orgUid', 'name', 'icon', 'isHome', 'subscribed'], + raw: true, + }); + + return organizations.reduce((map, current) => { + if (map) { + map = {}; } - return homeOrg; + map[current.orgUid] = current; + }); + } + + static async createHomeOrganization(name, icon, dataVersion = 'v1') { + const myOrganization = await Organization.getHomeOrg(); + + if (myOrganization) { + return myOrganization.orgUid; } - return undefined; - } + const newOrganizationId = await createDataLayerStore(); + const newRegistryId = await createDataLayerStore(); + const registryVersionId = await createDataLayerStore(); - static async getOrgsMap() { - const homeOrg = await Organization.getHomeOrg(); - const stubbedOrganizations = { - ...homeOrg, - '35f92331-c8d7-4e9e-a8d2-cd0a86cbb2cf': { - name: 'chili', - icon: 'https://climate-warehouse.s3.us-west-2.amazonaws.com/public/orgs/chili.svg', - }, - 'fbffae6b-0203-4ac0-a08b-1551b730783b': { - name: 'belgium', - icon: 'https://climate-warehouse.s3.us-west-2.amazonaws.com/public/orgs/belgium.svg', - }, - '70150fde-57f6-44a6-9486-1fef49528475': { - name: 'bulgaria', - icon: 'https://climate-warehouse.s3.us-west-2.amazonaws.com/public/orgs/bulgaria.svg', - }, - }; - return stubbedOrganizations; + // sync the organization store + await syncDataLayer(newOrganizationId, { + registryId: newRegistryId, + name, + icon, + }); + + //sync the registry store + await syncDataLayer(newRegistryId, { + [dataVersion]: registryVersionId, + }); + + // Create the TableStores + const coBenefitsStoreId = await createDataLayerStore(); + const projectLocationStoreId = await createDataLayerStore(); + const projectsStoreId = await createDataLayerStore(); + const projectRatingStoreId = await createDataLayerStore(); + const relatedProjectsStoreId = await createDataLayerStore(); + const qualificationsStoreId = await createDataLayerStore(); + const unitsStoreId = await createDataLayerStore(); + const vintagesStoreId = await createDataLayerStore(); + const qualificationUnitJunctionStoreId = await createDataLayerStore(); + + await syncDataLayer(registryVersionId, { + coBenefitsStoreId, + projectLocationStoreId, + projectsStoreId, + projectRatingStoreId, + relatedProjectsStoreId, + qualificationsStoreId, + unitsStoreId, + vintagesStoreId, + qualificationUnitJunctionStoreId, + }); + + await Organization.create({ + orgUid: newOrganizationId, + registryId: registryVersionId, + coBenefitsStoreId, + projectLocationStoreId, + projectsStoreId, + projectRatingStoreId, + relatedProjectsStoreId, + qualificationsStoreId, + qualificationUnitJunctionStoreId, + unitsStoreId, + vintagesStoreId, + isHome: true, + subscribed: true, + name, + icon, + }); + + return newOrganizationId; } + + // eslint-disable-next-line + static appendNewRegistry = (dataVersion) => { + throw new Error('Not implemented yet'); + }; + + // eslint-disable-next-line + static importHomeOrganization = (orgUid) => { + throw new Error('Not implemented yet'); + }; + + // eslint-disable-next-line + static subscribeToOrganization = (orgUid) => { + throw new Error('Not implemented yet'); + }; + + // eslint-disable-next-line + static unsubscribeToOrganization = (orgUid) => { + throw new Error('Not implemented yet'); + }; } Organization.init(ModelTypes, { diff --git a/src/models/organizations/organizations.modeltypes.cjs b/src/models/organizations/organizations.modeltypes.cjs index 6d742c24..c7dae3ab 100644 --- a/src/models/organizations/organizations.modeltypes.cjs +++ b/src/models/organizations/organizations.modeltypes.cjs @@ -1,4 +1,3 @@ -const { uuid: uuidv4 } = require('uuidv4'); const Sequelize = require('sequelize'); module.exports = { @@ -13,6 +12,33 @@ module.exports = { }, name: Sequelize.STRING, icon: Sequelize.STRING, + registryId: Sequelize.STRING, + projectLocationStoreId: Sequelize.STRING, + projectLocationStoreHash: Sequelize.STRING, + projectRatingStoreId: Sequelize.STRING, + projectRatingStoreHash: Sequelize.STRING, + coBenefitsStoreId: Sequelize.STRING, + coBenefitsStoreHash: Sequelize.STRING, + projectsStoreId: Sequelize.STRING, + projectsStoreIdHash: Sequelize.STRING, + relatedProjectsStoreId: Sequelize.STRING, + relatedProjectsStoreHash: Sequelize.STRING, + vintagesStoreId: Sequelize.STRING, + vintagesStoreHash: Sequelize.STRING, + qualificationsStoreId: Sequelize.STRING, + qualificationsStoreHash: Sequelize.STRING, + qualificationUnitJunctionStoreId: Sequelize.STRING, + qualificationUnitJunctionStoreHash: Sequelize.STRING, + unitsStoreId: Sequelize.STRING, + unitsStoreHash: Sequelize.STRING, + subscribed: { + type: Sequelize.BOOLEAN, + defaultValue: false, + }, + isHome: { + type: Sequelize.BOOLEAN, + defaultValue: false, + }, createdAt: Sequelize.DATE, updatedAt: Sequelize.DATE, }; diff --git a/src/models/projects/projects.model.js b/src/models/projects/projects.model.js index 50bccf40..f8fcbfcb 100644 --- a/src/models/projects/projects.model.js +++ b/src/models/projects/projects.model.js @@ -1,10 +1,15 @@ 'use strict'; +import _ from 'lodash'; import Sequelize from 'sequelize'; import rxjs from 'rxjs'; const { Model } = Sequelize; -import {sequelize, safeMirrorDbHandler, sanitizeSqliteFtsQuery} from '../database'; +import { + sequelize, + safeMirrorDbHandler, + sanitizeSqliteFtsQuery, +} from '../database'; import { RelatedProject, @@ -12,8 +17,11 @@ import { Qualification, ProjectLocation, CoBenefit, + Rating, } from '../'; +import { changeListFactory } from '../../fullnode/data-layer-utils'; + import ModelTypes from './projects.modeltypes.cjs'; import { ProjectMirror } from './projects.model.mirror'; @@ -170,18 +178,19 @@ class Project extends Model { if (columns.length) { fields = columns.join(', '); } - + searchStr = sanitizeSqliteFtsQuery(searchStr); - - if (searchStr === '*') { // * isn't a valid matcher on its own. return empty set + + if (searchStr === '*') { + // * isn't a valid matcher on its own. return empty set return { count: 0, rows: [], - } + }; } - + if (searchStr.startsWith('+')) { - searchStr = searchStr.replace('+', '') // If query starts with +, replace it + searchStr = searchStr.replace('+', ''); // If query starts with +, replace it } let sql = `SELECT ${fields} FROM projects_fts WHERE projects_fts MATCH :search`; @@ -213,6 +222,68 @@ class Project extends Model { }), }; } + + static async generateChangeListFromStagedData( + action, + warehouseProjectId, + stagedData, + ) { + const foreignKeys = [ + 'projectLocations', + 'qualifications', + 'vintages', + 'coBenefits', + 'relatedProjects', + ]; + + return Promise.resolve( + changeListFactory( + action, + warehouseProjectId, + _.omit(stagedData, foreignKeys), + ), + ); + } + + static async generateFullProjectModelChangeListFromStagedRecord(data) { + const promises = [Project.generateChangeListFromStagedData(data)]; + + if (data.projectLocations) { + promises.push( + ProjectLocation.generateChangeListFromStagedData(data.projectLocations), + ); + } + + if (data.qualifications) { + promises.push( + Qualification.generateChangeListFromStagedData(data.qualifications), + ); + } + + if (data.relatedProjects) { + promises.push( + Rating.generateChangeListFromStagedData(data.relatedProjects), + ); + } + + if (data.coBenefits) { + promises.push( + CoBenefit.generateChangeListFromStagedData(data.coBenefits), + ); + } + + if (data.vintages) { + promises.push(Vintage.generateChangeListFromStagedData(data.vintages)); + } + + if (data.relatedProjects) { + promises.push( + RelatedProject.generateChangeListFromStagedData(data.relatedProjects), + ); + } + + return Promise.all(promises); + } } Project.init(ModelTypes, { diff --git a/src/models/qualifications/qualifications.model.js b/src/models/qualifications/qualifications.model.js index f10abd41..a8a70c09 100644 --- a/src/models/qualifications/qualifications.model.js +++ b/src/models/qualifications/qualifications.model.js @@ -47,6 +47,17 @@ class Qualification extends Model { safeMirrorDbHandler(() => QualificationMirror.destroy(values)); return super.destroy(values); } + + static async generateChangeListFromStagedData( + // eslint-disable-next-line + action, + // eslint-disable-next-line + id, + // eslint-disable-next-line + stagedData, + ) { + return {}; + } } Qualification.init(ModelTypes, { diff --git a/src/models/ratings/ratings.model.js b/src/models/ratings/ratings.model.js index 147f96f6..ce5efe11 100644 --- a/src/models/ratings/ratings.model.js +++ b/src/models/ratings/ratings.model.js @@ -33,6 +33,17 @@ class Rating extends Model { safeMirrorDbHandler(() => RatingMirror.destroy(values)); return super.destroy(values); } + + static async generateChangeListFromStagedData( + // eslint-disable-next-line + action, + // eslint-disable-next-line + id, + // eslint-disable-next-line + stagedData, + ) { + return {}; + } } Rating.init(ModelTypes, { diff --git a/src/models/related-projects/related-projects.model.js b/src/models/related-projects/related-projects.model.js index e14e745f..cbb5e1ab 100644 --- a/src/models/related-projects/related-projects.model.js +++ b/src/models/related-projects/related-projects.model.js @@ -34,6 +34,17 @@ class RelatedProject extends Model { safeMirrorDbHandler(() => RelatedProjectMirror.destroy(values)); return super.destroy(values); } + + static async generateChangeListFromStagedData( + // eslint-disable-next-line + action, + // eslint-disable-next-line + id, + // eslint-disable-next-line + stagedData, + ) { + return {}; + } } RelatedProject.init(ModelTypes, { diff --git a/src/models/simulator/index.js b/src/models/simulator/index.js new file mode 100644 index 00000000..fbbfc7b0 --- /dev/null +++ b/src/models/simulator/index.js @@ -0,0 +1,2 @@ +export * from './simulator.model.js'; +export * from './simulator.mock.js'; diff --git a/src/models/simulator/simulator.mock.js b/src/models/simulator/simulator.mock.js new file mode 100644 index 00000000..fe199fcb --- /dev/null +++ b/src/models/simulator/simulator.mock.js @@ -0,0 +1,8 @@ +import stub from './simulator.stub.json'; + +export const MetaMock = { + findAll: () => stub, + findOne: (id) => { + return stub.find((record) => record.id == id); + }, +}; diff --git a/src/models/simulator/simulator.model.js b/src/models/simulator/simulator.model.js new file mode 100644 index 00000000..04ce48f4 --- /dev/null +++ b/src/models/simulator/simulator.model.js @@ -0,0 +1,20 @@ +'use strict'; + +import Sequelize from 'sequelize'; +const { Model } = Sequelize; +import { sequelize } from '../database'; + +import ModelTypes from './simulator.modeltypes.cjs'; + +class Simulator extends Model {} + +Simulator.init(ModelTypes, { + sequelize, + modelName: 'simulator', + freezeTableName: true, + timestamps: false, + createdAt: false, + updatedAt: false, +}); + +export { Simulator }; diff --git a/src/models/simulator/simulator.modeltypes.cjs b/src/models/simulator/simulator.modeltypes.cjs new file mode 100644 index 00000000..3d05fdc7 --- /dev/null +++ b/src/models/simulator/simulator.modeltypes.cjs @@ -0,0 +1,14 @@ +const Sequelize = require('sequelize'); + +module.exports = { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + key: { + type: Sequelize.STRING, + unique: true, + }, + value: Sequelize.STRING, +}; diff --git a/src/models/simulator/simulator.stub.json b/src/models/simulator/simulator.stub.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/src/models/simulator/simulator.stub.json @@ -0,0 +1 @@ +[] diff --git a/src/models/staging/staging.model.js b/src/models/staging/staging.model.js index d2f2dff2..da39d540 100644 --- a/src/models/staging/staging.model.js +++ b/src/models/staging/staging.model.js @@ -1,6 +1,9 @@ 'use strict'; + import Sequelize from 'sequelize'; const { Model } = Sequelize; +import { pushDataLayerChangeList } from '../../fullnode'; +import { Project, Unit, Organization } from '../../models'; import rxjs from 'rxjs'; import { sequelize } from '../database'; @@ -19,6 +22,147 @@ class Staging extends Model { Staging.changes.next(['staging']); return super.destroy(values); } + + static async pushToDataLayer() { + const coBenefitsChangeList = []; + const projectLocationChangeList = []; + const projectsChangeList = []; + const projectRatingChangeList = []; + const relatedProjectsChangeList = []; + const qualificationsChangeList = []; + const unitsChangeList = []; + const vintagesChangeList = []; + + const stagedChangeList = await Staging.findAll(); + + await Promise.all( + stagedChangeList.map(async (stagingRecord) => { + const { + // eslint-disable-next-line + id: stagingRecordId, + uuid, + table, + action, + commited, + data: rawData, + } = stagingRecord; + let dataSet = JSON.parse(rawData); + + await Promise.all( + dataSet.map(async (data) => { + if (table === 'Projects' && !commited) { + const [ + thisProjectChangeList, + thisProjectLocationChangeList, + thisQualificationsChangeList, + thisProjectRatingChangeList, + thisCoBenefitsChangeList, + thisVintagesChangeList, + thisRelatedProjectsChangeList, + ] = await Project.generateFullProjectModelChangeListFromStagedRecord( + action, + uuid, + data, + ); + + if (thisProjectChangeList) { + projectsChangeList.push(thisProjectChangeList); + } + + if (thisProjectLocationChangeList) { + projectLocationChangeList.push(thisProjectLocationChangeList); + } + + if (thisQualificationsChangeList) { + qualificationsChangeList.push(thisQualificationsChangeList); + } + + if (thisProjectRatingChangeList) { + projectRatingChangeList.push(thisProjectRatingChangeList); + } + + if (thisCoBenefitsChangeList) { + coBenefitsChangeList.push(thisCoBenefitsChangeList); + } + + if (thisVintagesChangeList) { + vintagesChangeList.push(thisVintagesChangeList); + } + + if (thisRelatedProjectsChangeList) { + relatedProjectsChangeList.push(thisRelatedProjectsChangeList); + } + } else if (table === 'Units' && !commited) { + const [ + thisUnitsChangeList, + thisVintagesChangeList, + thisQualificationsChangeList, + ] = await Unit.generateUnitModelChangeListFromStagedRecord( + action, + uuid, + data, + ); + + if (thisUnitsChangeList) { + unitsChangeList.push(thisUnitsChangeList); + } + + if (thisVintagesChangeList) { + vintagesChangeList.push(thisVintagesChangeList); + } + + if (thisQualificationsChangeList) { + qualificationsChangeList.push(thisQualificationsChangeList); + } + } + }), + ); + }), + ); + + const { + projectLocationStoreId, + projectRatingStoreId, + coBenefitsStoreId, + projectsStoreId, + relatedProjectsStoreId, + vintagesStoreId, + qualificationsStoreId, + // qualificationUnitJunctionStoreId, + unitsStoreId, + } = await Organization.findOne({ + where: { isHome: true }, + raw: true, + }); + + await Promise.all([ + coBenefitsChangeList.length && + pushDataLayerChangeList(coBenefitsStoreId, coBenefitsChangeList), + projectLocationChangeList.length && + pushDataLayerChangeList( + projectLocationStoreId, + projectLocationChangeList, + ), + projectsChangeList.length && + pushDataLayerChangeList(projectsStoreId, projectsChangeList), + projectRatingChangeList.length && + pushDataLayerChangeList(projectRatingStoreId, projectRatingChangeList), + relatedProjectsChangeList.length && + pushDataLayerChangeList( + relatedProjectsStoreId, + relatedProjectsChangeList, + ), + qualificationsChangeList.length && + pushDataLayerChangeList( + qualificationsStoreId, + qualificationsChangeList, + ), + unitsChangeList.length && + pushDataLayerChangeList(unitsStoreId, unitsChangeList), + vintagesChangeList.length && + pushDataLayerChangeList(vintagesStoreId, vintagesChangeList), + ]); + } } Staging.init(ModelTypes, { diff --git a/src/models/units/units.model.js b/src/models/units/units.model.js index a517028f..f2a63bd7 100644 --- a/src/models/units/units.model.js +++ b/src/models/units/units.model.js @@ -1,11 +1,17 @@ 'use strict'; +import _ from 'lodash'; import Sequelize from 'sequelize'; -import {sequelize, safeMirrorDbHandler, sanitizeSqliteFtsQuery} from '../database'; +import rxjs from 'rxjs'; +import { + sequelize, + safeMirrorDbHandler, + sanitizeSqliteFtsQuery, +} from '../database'; import { Qualification, Vintage } from '../../models'; import { UnitMirror } from './units.model.mirror'; import ModelTypes from './units.modeltypes.cjs'; -import rxjs from 'rxjs'; +import { changeListFactory } from '../../fullnode/data-layer-utils'; const { Model } = Sequelize; @@ -215,20 +221,21 @@ class Unit extends Model { if (columns.length) { fields = columns.join(', '); } - + searchStr = sanitizeSqliteFtsQuery(searchStr); - - if (searchStr === '*') { // * isn't a valid matcher on its own. return empty set + + if (searchStr === '*') { + // * isn't a valid matcher on its own. return empty set return { count: 0, rows: [], - } + }; } - + if (searchStr.startsWith('+')) { - searchStr = searchStr.replace('+', '') // If query starts with +, replace it + searchStr = searchStr.replace('+', ''); // If query starts with +, replace it } - + let sql = `SELECT ${fields} FROM units_fts WHERE units_fts MATCH :search`; if (orgUid) { @@ -258,6 +265,49 @@ class Unit extends Model { }), }; } + + static async generateChangeListFromStagedData( + action, + warehouseUnitId, + stagedData, + ) { + const foreignKeys = ['qualifications', 'vintage']; + if (_.get(stagedData, 'vintage.id')) { + stagedData.vintageId = stagedData.vintage.id; + } + + return Promise.resolve( + changeListFactory( + action, + warehouseUnitId, + _.omit(stagedData, foreignKeys), + ), + ); + } + + static generateUnitModelChangeListFromStagedRecord(action, uuid, data) { + const promises = [ + Unit.generateChangeListFromStagedData(action, uuid, data), + ]; + + if (data.vintage) { + promises.push( + Vintage.generateChangeListFromStagedData(action, uuid, [data.vintage]), + ); + } + + if (data.qualifications) { + promises.push( + Qualification.generateChangeListFromStagedData( + action, + uuid, + data.qualifications, + ), + ); + } + + return Promise.all(promises); + } } Unit.init(Object.assign({}, ModelTypes, virtualFields), { diff --git a/src/models/vintages/vintages.model.js b/src/models/vintages/vintages.model.js index 9650f4db..87273407 100644 --- a/src/models/vintages/vintages.model.js +++ b/src/models/vintages/vintages.model.js @@ -40,6 +40,17 @@ class Vintage extends Model { safeMirrorDbHandler(() => VintageMirror.destroy(values)); return super.destroy(values); } + + static async generateChangeListFromStagedData( + // eslint-disable-next-line + action, + // eslint-disable-next-line + id, + // eslint-disable-next-line + stagedData, + ) { + return {}; + } } Vintage.init(ModelTypes, { diff --git a/src/models/vintages/vintages.stub.json b/src/models/vintages/vintages.stub.json index 5e418553..abea6a80 100644 --- a/src/models/vintages/vintages.stub.json +++ b/src/models/vintages/vintages.stub.json @@ -2,9 +2,9 @@ { "id": "a6745831-5d5e-45ed-b9fe-fd6aa129df25", "orgUid": "f1c54511-865e-4611-976c-7c3c1f704662", - "startDate": "2021-01-18", - "endDate": "2022-01-18", - "verificationDate": "2022-01-18 ", + "startDate": "2019-02-03 00:05:45.701 +00:00", + "endDate": "2029-03-12 00:05:45.701 +00:00", + "verificationDate": "2022-01-18 00:05:45.701 +00:00", "verificationBody": "This is verified", "warehouseProjectId": "81e05bfa-e93f-458f-b907-96bf170e52cd", "createdAt": "2022-01-18 00:05:45.701 +00:00", @@ -13,9 +13,9 @@ { "id": "3d5a8ed2-e5a7-4275-a36e-3456812e39b7", "orgUid": "f1c54511-865e-4611-976c-7c3c1f704662", - "startDate": "2021-01-18", - "endDate": "2022-01-18", - "verificationDate": "2022-01-18", + "startDate": "2004-03-21 00:05:45.701 +00:00", + "endDate": "2005-08-11 00:05:45.701 +00:00", + "verificationDate": "2022-01-18 00:05:45.701 +00:00", "verificationBody": "This is verified", "warehouseProjectId": "81e05bfa-e93f-458f-b907-96bf170e52cd", "createdAt": "2022-01-18 00:05:45.701 +00:00", @@ -24,9 +24,9 @@ { "id": "57c1859d-6aa4-4c57-9dfb-6438e0d4653e", "orgUid": "f1c54511-865e-4611-976c-7c3c1f704662", - "startDate": "2021-01-18", - "endDate": "2022-01-18", - "verificationDate": "2022-01-18", + "startDate": "2012-01-18 00:05:45.701 +00:00", + "endDate": "2013-06-22 00:05:45.701 +00:00", + "verificationDate": "2022-01-18 00:05:45.701 +00:00", "verificationBody": "This is verified", "warehouseProjectId": "81e05bfa-e93f-458f-b907-96bf170e52cd", "createdAt": "2022-01-18 00:05:45.701 +00:00", @@ -35,20 +35,20 @@ { "id": "74887b22-da3b-4c2b-b945-670319193cdd", "orgUid": "f1c54511-865e-4611-976c-7c3c1f704662", - "startDate": "2021-01-18", - "endDate": "2022-01-18", - "verificationDate": "2022-01-18", + "startDate": "2014-02-18 00:05:45.701 +00:00", + "endDate": "2016-04-18 00:05:45.701 +00:00", + "verificationDate": "2022-01-18 00:05:45.701 +00:00", "verificationBody": "This is verified", "warehouseProjectId": "81e05bfa-e93f-458f-b907-96bf170e52cd", - "createdAt": "2022-01-18 00:05:45.701 +00:00", - "updatedAt": "2022-01-18 00:05:45.701 +00:00" + "createdAt": "2022-02-18 00:05:45.701 +00:00", + "updatedAt": "2022-08-18 00:05:45.701 +00:00" }, { "id": "7f7f23a5-3e1a-43b4-82d8-b4156b158f88", "orgUid": "f1c54511-865e-4611-976c-7c3c1f704662", - "startDate": "2021-01-18", - "endDate": "2022-01-18", - "verificationDate": "2022-01-18", + "startDate": "2020-05-18 00:05:45.701 +00:00", + "endDate": "2021-03-22 00:05:45.701 +00:00", + "verificationDate": "2022-01-18 00:05:45.701 +00:00", "verificationBody": "This is verified", "warehouseProjectId": "81e05bfa-e93f-458f-b907-96bf170e52cd", "createdAt": "2022-01-18 00:05:45.701 +00:00", diff --git a/src/routes/v1/resources/organization.js b/src/routes/v1/resources/organization.js index 568112ca..3da03c91 100644 --- a/src/routes/v1/resources/organization.js +++ b/src/routes/v1/resources/organization.js @@ -1,17 +1,28 @@ 'use strict'; import express from 'express'; +import joiExpress from 'express-joi-validation'; import { OrganizationController } from '../../../controllers'; +import { newOrganizationSchema } from '../../../validations'; -const OrganizationRouter = express.Router({ passError: true }); +const validator = joiExpress.createValidator({ passError: true }); +const OrganizationRouter = express.Router(); OrganizationRouter.get('/', (req, res) => { return OrganizationController.findAll(req, res); }); -OrganizationRouter.post('/', (req, res) => { - return OrganizationController.create(req, res); +OrganizationRouter.post( + '/', + validator.body(newOrganizationSchema), + (req, res) => { + return OrganizationController.create(req, res); + }, +); + +OrganizationRouter.put('/', (req, res) => { + return OrganizationController.importOrg(req, res); }); export { OrganizationRouter }; diff --git a/src/routes/v1/resources/staging.js b/src/routes/v1/resources/staging.js index 154df971..29d04581 100644 --- a/src/routes/v1/resources/staging.js +++ b/src/routes/v1/resources/staging.js @@ -19,7 +19,7 @@ StagingRouter.delete( StagingController.destroy, ); -StagingRouter.post('/commit', StagingController.commitV2); +StagingRouter.post('/commit', StagingController.commit); // Empty entire stagin table StagingRouter.delete('/clean', StagingController.clean); diff --git a/src/server.js b/src/server.js index d098b1f6..4a515352 100644 --- a/src/server.js +++ b/src/server.js @@ -5,6 +5,7 @@ import http from 'http'; import { Server } from 'socket.io'; import Debug from 'debug'; import { connection } from './websocket'; +import { startDataLayerUpdatePolling } from './fullnode'; const debug = Debug('climate-warehouse:server'); @@ -50,4 +51,6 @@ function onListening() { debug('Listening on ' + bind); } +startDataLayerUpdatePolling(); + export default rootRouter; diff --git a/src/validations/index.js b/src/validations/index.js index fbaab0eb..6b4a86ae 100644 --- a/src/validations/index.js +++ b/src/validations/index.js @@ -1,3 +1,4 @@ export * from './units.validations'; export * from './staging.validations'; export * from './projects.validations'; +export * from './organizations.validations'; diff --git a/src/validations/organizations.validations.js b/src/validations/organizations.validations.js new file mode 100644 index 00000000..d1a1a236 --- /dev/null +++ b/src/validations/organizations.validations.js @@ -0,0 +1,10 @@ +import Joi from 'joi'; + +export const newOrganizationSchema = Joi.object({ + name: Joi.string().required(), + icon: Joi.string().required(), +}); + +export const importOrganizationSchema = Joi.object({ + orgUid: Joi.string().required(), +});