diff --git a/.env.copy b/.env.copy index 3e2111a3..04922193 100644 --- a/.env.copy +++ b/.env.copy @@ -9,4 +9,8 @@ USE_SIMULATOR=false PICKLIST_URL=https://climate-warehouse.s3.us-west-2.amazonaws.com/public/picklists.json DEFAULT_ORGANIZATIONS=https://climate-warehouse.s3.us-west-2.amazonaws.com/public/cw-organizations.json READ_ONLY=false +IS_GOVERNANCE_BODY=false API_KEY= +GOVERANCE_BODY_ID= +GOVERNANCE_BODY_IP= +GOVERNANCE_BODY_PORT= \ No newline at end of file diff --git a/src/controllers/governance.controller.js b/src/controllers/governance.controller.js index 25777135..3a463608 100644 --- a/src/controllers/governance.controller.js +++ b/src/controllers/governance.controller.js @@ -1,25 +1,98 @@ import { Governance } from '../models'; +import { + assertIsActiveGovernanceBody, + assertIfReadOnlyMode, + assertDataLayerAvailable, + assertWalletIsAvailable, + assertWalletIsSynced, + assertCanBeGovernanceBody, +} from '../utils/data-assertions'; + export const findAll = async (req, res) => { try { const results = await Governance.findAll(); return res.json(results); } catch (error) { res.status(400).json({ - message: 'Can not retreive issuances', + message: 'Can not retreive Governance Data', error: error.message, }); } }; // eslint-disable-next-line -export const createGoveranceBody = async (req, res) => {}; +export const createGoveranceBody = async (req, res) => { + try { + await assertIfReadOnlyMode(); + await assertDataLayerAvailable(); + await assertWalletIsAvailable(); + await assertWalletIsSynced(); + await assertCanBeGovernanceBody(); -// eslint-disable-next-line -export const setDefaultOrgList = async (req, res) => {}; + await Governance.createGoveranceBody(); + + return res.json({ + message: + 'Setting up new Governance Body on this node, this can tae a few mins', + }); + } catch (error) { + res.status(400).json({ + message: 'Cant update default orgs', + error: error.message, + }); + } +}; // eslint-disable-next-line -export const setPickList = async (req, res) => {}; +export const setDefaultOrgList = async (req, res) => { + try { + await assertIfReadOnlyMode(); + await assertDataLayerAvailable(); + await assertWalletIsAvailable(); + await assertWalletIsSynced(); + await assertIsActiveGovernanceBody(); + + const orgList = JSON.stringify(req.body); + + await Governance.updateGoveranceBodyData([ + { key: 'orgList', value: orgList }, + ]); + + return res.json({ + message: 'Committed this new organization list to the datalayer', + }); + } catch (error) { + console.trace(error); + res.status(400).json({ + message: 'Cant update default orgs', + error: error.message, + }); + } +}; // eslint-disable-next-line -export const subscribeToGovernanceBody = async (req, res) => {}; +export const setPickList = async (req, res) => { + try { + await assertIfReadOnlyMode(); + await assertDataLayerAvailable(); + await assertWalletIsAvailable(); + await assertWalletIsSynced(); + await assertIsActiveGovernanceBody(); + + const pickList = JSON.stringify(req.body); + + await Governance.updateGoveranceBodyData([ + { key: 'pickList', value: pickList }, + ]); + + return res.json({ + message: 'Committed this pick list to the datalayer', + }); + } catch (error) { + res.status(400).json({ + message: 'Cant update picklist', + error: error.message, + }); + } +}; diff --git a/src/controllers/index.js b/src/controllers/index.js index 30328c18..e127c112 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -5,3 +5,4 @@ export * as OrganizationController from './organization.controller'; export * as IssuanceController from './issuance.controller'; export * as LabelController from './label.controller'; export * as AuditController from './audit.controller'; +export * as GovernanceController from './governance.controller'; diff --git a/src/database/migrations/index.js b/src/database/migrations/index.js index 967ca03a..e93435f9 100644 --- a/src/database/migrations/index.js +++ b/src/database/migrations/index.js @@ -17,6 +17,7 @@ import CreateOrganizationTable from './20220122164836-create-organization-table' import CreateEstimationTable from './20220127190529-create-estimation-table'; import CreateAuditTable from './20220222204323-create-audit-table'; import CreateMetaTable from './20220119211024-create-meta-table'; +import CreateGoveranceTable from './20220315134151-create-governance-table'; export const migrations = [ { @@ -99,4 +100,8 @@ export const migrations = [ migration: AddOrgUidIndex, name: '20220115134849-add-orgUid-index.js', }, + { + migration: CreateGoveranceTable, + name: '20220315134151-create-governance-table', + }, ]; diff --git a/src/datalayer/persistance.js b/src/datalayer/persistance.js index dfcee5fd..c0baffd1 100644 --- a/src/datalayer/persistance.js +++ b/src/datalayer/persistance.js @@ -42,6 +42,8 @@ export const createDataLayerStore = async () => { const data = JSON.parse(response); + console.log(data); + if (data.success) { return data.id; } diff --git a/src/datalayer/writeService.js b/src/datalayer/writeService.js index a323b4cb..3bc4e71e 100644 --- a/src/datalayer/writeService.js +++ b/src/datalayer/writeService.js @@ -67,6 +67,7 @@ const pushChangesWhenStoreIsAvailable = async ( const storeExistAndIsConfirmed = await dataLayer.getRoot(storeId); if (!hasUnconfirmedTransactions && storeExistAndIsConfirmed) { + console.log('pushing to datalayer', { storeId, changeList }); const success = await dataLayer.pushChangeListToDataLayer( storeId, changeList, diff --git a/src/models/governance/governance.mock.js b/src/models/governance/governance.mock.js index 2bae4c24..d2d6439b 100644 --- a/src/models/governance/governance.mock.js +++ b/src/models/governance/governance.mock.js @@ -1,6 +1,6 @@ -import stub from './meta.stub.json'; +import stub from './governance.stub.json'; -export const MetaMock = { +export const GovernanceMock = { findAll: () => stub, findOne: (id) => { return stub.find((record) => record.id == id); diff --git a/src/models/governance/governance.model.js b/src/models/governance/governance.model.js index 88a820d7..3c0b9b38 100644 --- a/src/models/governance/governance.model.js +++ b/src/models/governance/governance.model.js @@ -21,15 +21,51 @@ class Governance extends Model { await Meta.upsert({ metaKey: 'goveranceBodyId', - goveranceBodyId, + metaValue: goveranceBodyId, }); return goveranceBodyId; } + static async sync() { + const { GOVERANCE_BODY_ID, GOVERNANCE_BODY_IP, GOVERNANCE_BODY_PORT } = + process.env; + + if (!GOVERANCE_BODY_ID || !GOVERNANCE_BODY_IP || !GOVERNANCE_BODY_PORT) { + throw new Error('Missing information in env to sync Governance data'); + } + + const governanceData = await datalayer.getSubscribedStoreData( + GOVERANCE_BODY_ID, + GOVERNANCE_BODY_IP, + GOVERNANCE_BODY_PORT, + ); + + const updates = []; + + if (governanceData.orgList) { + updates.push({ + metaKey: 'orgList', + metaValue: governanceData.orgList, + confirmed: true, + }); + } + + if (governanceData.pickList) { + updates.push({ + metaKey: 'pickList', + metaValue: governanceData.pickList, + confirmed: true, + }); + } + + await Promise.all(updates.map(async (update) => Governance.upsert(update))); + } + static async updateGoveranceBodyData(keyValueArray) { - const goveranceBodyId = await Meta.find({ + const goveranceBodyId = await Meta.findOne({ where: { metaKey: 'goveranceBodyId' }, + raw: true, }); if (!goveranceBodyId) { @@ -38,7 +74,7 @@ class Governance extends Model { ); } - const existingRecords = Governance.findAll({ raw: true }); + const existingRecords = await Governance.findAll({ raw: true }); const changeList = []; @@ -53,13 +89,20 @@ class Governance extends Model { metaValue: keyValue.value, confirmed: false, }); + changeList.push( - keyValueToChangeList(keyValue.key, keyValue.value, valueExists), + ...keyValueToChangeList(keyValue.key, keyValue.value, valueExists), ); }), ); const rollbackChangesIfFailed = async () => { + console.log('Reverting Goverance Records'); + await Governance.destroy({ + where: {}, + truncate: true, + }); + await Promise.all( existingRecords.map(async (record) => await Governance.upsert(record)), ); @@ -77,15 +120,22 @@ class Governance extends Model { ); }; - await datalayer.pushDataLayerChangeList(goveranceBodyId, changeList); + await datalayer.pushDataLayerChangeList( + goveranceBodyId.metaValue, + changeList, + ); - datalayer.getStoreData(goveranceBodyId, onConfirm, rollbackChangesIfFailed); + datalayer.getStoreData( + goveranceBodyId.metaValue, + onConfirm, + rollbackChangesIfFailed, + ); } } Governance.init(ModelTypes, { sequelize, - modelName: 'meta', + modelName: 'governance', freezeTableName: true, timestamps: false, createdAt: false, diff --git a/src/models/governance/governance.modeltypes.cjs b/src/models/governance/governance.modeltypes.cjs index 9d00efd3..541d1bb6 100644 --- a/src/models/governance/governance.modeltypes.cjs +++ b/src/models/governance/governance.modeltypes.cjs @@ -9,8 +9,12 @@ module.exports = { metaKey: { type: Sequelize.STRING, unique: true, + required: true, + }, + metaValue: { + type: Sequelize.STRING, + required: true, }, - metaValue: Sequelize.STRING, confirmed: { type: Sequelize.BOOLEAN, defaultValue: false, diff --git a/src/models/governance/governance.stub.json b/src/models/governance/governance.stub.json index fe51488c..7506ec41 100644 --- a/src/models/governance/governance.stub.json +++ b/src/models/governance/governance.stub.json @@ -1 +1,388 @@ -[] +{ + "registries": [ + "Verra", + "Gold Standard", + "Climate Action Reserve (CAR)", + "American Carbon Registry (ACR)", + "Cultivo", + "Switzerland National Registry", + "Sweden National Registry", + "Singapore National Registry", + "Japan National Registry", + "Costa Rica National Registry", + "Mexico National Registry", + "Chile National Registry", + "UNFCCC", + "Open Earth Foundation" + ], + "projectSector": [ + "Energy (renewable/non-renewable)", + "Energy distribution", + "Energy demand", + "Manufacturing industries", + "Chemical industry", + "Construction", + "Transport", + "Mining/Mineral production", + "Metal production", + "Fugitive emissions – from fuels (solid, oil and gas)", + "Fugitive emissions – from Industrial gases (halocarbons and sulphur hexafluoride)", + "Solvents use", + "Waste handling and disposal", + "Agriculture Forestry and Other Land Use (AFOLU)", + "Livestock and manure management" + ], + "projectType": [ + "Ozone Depleting Substances", + "Livestock", + "Soil Enrichment", + "Improved Forest Management", + "Landfill Gas Capture/Combustion", + "Forestry", + "Avoided Conversion", + "Coal Mine Methane", + "Conservation", + "Nitrogen Management", + "Organic Waste Composting", + "Organic Waste Digestion", + "Reforestation", + "Plastic Waste Collection", + "Plastic Waste Recycling", + "Energy demand", + "Afforestation", + "Reforestation and Revegetation", + "Reduced Emissions from Deforestation and Degradation" + ], + "coveredByNDC": ["Inside NDC", "Outside NDC", "Unknown"], + "projectStatusValues": ["Listed", "Registered", "Completed", "Transitioned"], + "unitMetric": ["tCO2e"], + "methodology": [ + "Decomposition of fluoroform (HFC-23) waste streams --- Version 6.0.0", + "Analysis of the least-cost fuel option for seasonally-operating biomass cogeneration plants --- Version 1.0", + "Recovery and utilization of gas from oil fields that would otherwise be flared or vented --- Version 7.0", + "Steam system efficiency improvements by replacing steam traps and returning condensate --- Version 2.0", + "Baseline methodology for steam optimization systems --- Version 4.0", + "Renewable energy projects replacing part of the electricity production of one single fossil fuel fired power plant that stands alone or supplies to a grid, excluding biomass projects --- Version 2.0", + "Baseline methodology for water pumping efficiency improvements --- Version 2.0", + "Baseline Methodology for decomposition of N2O from existing adipic acid production plants --- Version 3.0", + "Leak detection and repair in gas production, processing, transmission, storage and distribution systems and in refinery facilities --- Version 4.0.0", + "Methodology for zero-emissions grid-connected electricity generation from renewable sources in Chile or in countries with merit order based dispatch grid --- Version 3.0", + "Substitution of CO2 from fossil or mineral origin by CO2 from biogenic residual sources in the production of inorganic compounds --- Version 3.0", + "N2O destruction in the tail gas of Caprolactam production plants --- Version 6.0", + "PFC emission reductions from anode effect mitigation at primary aluminium smelting facilities --- Version 4.0.0", + "Bus rapid transit projects --- Version 8.0", + "SF6 emission reductions in electrical grids --- Version 2.0.0", + "Use of biomass in heat generation equipment --- Version 6.0", + "Flare (or vent) reduction and utilization of gas from oil wells as a feedstock --- Version 3.0", + "Methodology for improved electrical energy efficiency of an existing submerged electric arc furnace used for the production of silicon and ferro alloys --- Version 3.0.0", + "Leak reduction from a natural gas distribution grid by replacing old cast iron pipes or steel pipes without cathodic protection with polyethylene pipes --- Version 2.0", + "Energy efficiency improvement projects - boiler rehabilitation or replacement in industrial and district heating sectors --- Version 2.0.0", + "Grid connection of isolated electricity systems --- Version 3.0", + "Distribution of efficient light bulbs to households --- Version 2.0", + "New cogeneration project activities supplying electricity and heat to multiple customers --- Version 5.0", + "Methodology for gas based energy generation in an industrial facility --- Version 3.0", + "Feed switch in integrated Ammonia-urea manufacturing industry --- Version 3.0.0", + "Increased electricity generation from existing hydropower stations through Decision Support System optimization --- Version 3.0", + "Biogenic methane injection to a natural gas distribution grid --- Version 4.0.0", + "Recovery and utilization of waste gas in refinery or gas plant --- Version 2.1.0", + "Efficiency improvement by boiler replacement or rehabilitation and optional fuel switch in fossil fuel-fired steam boiler systems --- Version 1.0", + "Avoided emissions from biomass wastes through use as feed stock in pulp and paper, cardboard, fibreboard or bio-oil production --- Version 3.0.1", + "Introduction of a district heating system --- Version 5.0", + "Reduction in GHGs emission from primary aluminium smelters --- Version 2.0", + "Power saving through replacement by energy efficient chillers --- Version 2.0", + "Methodology for rehabilitation and/or energy efficiency improvement in existing power plants --- Version 2.1", + "Energy efficiency improvements of a power plant through retrofitting turbines --- Version 2.0", + "Recovery of CO2 from tail gas in industrial facilities to substitute the use of fossil fuels for production of CO2 --- Version 1.2.0", + "Capture and utilisation or destruction of mine methane (excluding coal mines) or non mine methane --- Version 3.0.0", + "Replacement of SF6 with alternate cover gas in the magnesium industry --- Version 2.1", + "GHG emission reductions through waste heat utilisation for pre-heating of raw materials in sponge iron manufacturing process --- Version 2.0", + "Methodology for installation of energy efficient transformers in a power distribution grid --- Version 2.0", + "Methodology for improved energy efficiency by modifying ferroalloy production facility --- Version 1.0", + "Biogenic methane use as feedstock and fuel for town gas production --- Version 2.0", + "Manufacturing of energy efficient domestic refrigerators --- Version 3.1.0", + "Manufacturing and servicing of domestic refrigeration appliances using a low GWP refrigerant --- Version 2.0", + "Fossil Fuel Displacement by Geothermal Resources for Space Heating --- Version 3.0", + "GHG emission reductions through multi-site manure collection and treatment in a central plant --- Version 1.0", + "Methodology for new grid connected power plants using permeate gas previously flared and/or vented --- Version 3.0.0", + "Methodology for collection, processing and supply of biogas to end-users for production of heat --- Version 1.0", + "Implementation of fossil fuel trigeneration systems in existing industrial facilities --- Version 2.0", + "Recovery of gas from oil wells that would otherwise be vented or flared and its delivery to specific end-users --- Version 1.0", + "Point of Use Abatement Device to Reduce SF6 emissions in LCD Manufacturing Operations --- Version 2.0.0", + "Recovery of SF6 from Gas insulated electrical equipment in testing facilities --- Version 2.0", + "Mitigation of greenhouse gases emissions with treatment of wastewater in aerobic wastewater treatment plants --- Version 1.0", + "Flare or vent reduction at coke plants through the conversion of their waste gas into dimethyl ether for use as a fuel --- Version 1.0", + "Use of charcoal from planted renewable biomass in a new iron ore reduction system --- Version 2.0", + "Avoidance of landfill gas emissions by in-situ aeration of landfills --- Version 1.0.1", + "Installation of cogeneration system supplying electricity and chilled water to new and existing consumers --- Version 3.0", + "Distribution of low greenhouse gas emitting water purification systems for safe drinking water --- Version 5.0", + "Air separation using cryogenic energy recovered from the vaporization of LNG --- Version 1.0", + "Production of diesel using a mixed feedstock of gasoil and vegetable oil --- Version 2.0", + "Modal shift in transportation of cargo from road transportation to water or rail transportation --- Version 1.1.0", + "Energy efficiency technologies and fuel switching in new and existing buildings --- Version 4.0", + "Substitution of PFC gases for cleaning Chemical Vapour Deposition (CVD) reactors in the semiconductor industry --- Version 2.0.0", + "Avoidance of landfill gas emissions by passive aeration of landfills --- Version 1.0.1", + "Distribution of biomass based stove and/or heater for household or institutional use --- Version 2.0.0", + "Waste gas based combined cycle power plant in a Greenfield iron and steel plant --- Version 1.0.0", + "CF4 emission reduction from installation of an abatement system in a semiconductor manufacturing facility --- Version 1.0.0", + "Installation of high voltage direct current power transmission line --- Version 1.0.0", + "Utilization of ammonia-plant off gas for steam generation --- Version 1.0.0", + "Installation of a new natural gas fired gas turbine to an existing CHP plant --- Version 1.0.0", + "Integrated Solar Combined Cycle (ISCC) projects --- Version 1.0.0", + "High speed passenger rail systems --- Version 2.0", + "Renewable energy power generation in isolated grids --- Version 4.0", + "Interconnection of electricity grids in countries with economic merit order dispatch --- Version 2.0.0", + "Energy efficiency in data centres through dynamic power management --- Version 1.0.0", + "Energy efficiency improvements of a lime production facility through installation of new kilns --- Version 2.0.0", + "New natural gas based cogeneration plant --- Version 4.0", + "Interconnection between electricity systems for energy exchange --- Version 1.0.0", + "Introduction of hot supply of Direct Reduced Iron in Electric Arc Furnaces --- Version 1.0.0", + "Modal shift in transportation of liquid fuels --- Version 2.0", + "Abatement of fluorinated greenhouse gases in semiconductor manufacturing --- Version 1.0.0", + "Less carbon intensive power generation through continuous reductive distillation of waste --- Version 1.0", + "Distribution of compact fluorescent lamps (CFL) and light-emitting diode (LED) lamps to households --- Version 1.0", + "Shift from electrolytic to catalytic process for recycling of chlorine from hydrogen chloride gas in isocyanate plants --- Version 1.0", + "Recovery and utilization of coke oven gas from coke plants for LNG production --- Version 1.0", + "Electric taxiing systems for airplanes --- Version 2.0", + "Introduction of a new district cooling system --- Version 2.0", + "Introduction of low resistivity power transmission line --- Version 2.0", + "SF6 emission reductions in gas insulated metal enclosed switchgear --- Version 1.0", + "Energy-efficient refrigerators and air-conditioners --- Version 1.0", + "Emission reduction from partial switching of raw materials and increasing the share of additives in the production of blended cement --- Version 1.0", + "Recovery of methane-rich vapours from hydrocarbon storage tanks --- Version 1.0", + "Plastic Waste Mechanical Recycling", + "Plastic Waste Collection Methodology", + "Grid-connected electricity generation from renewable sources --- Version 20.0", + "Electrification of communities through grid extension or construction of new mini-grids --- Version 2.0", + "Methodology for Sustainable Grassland Management --- Version 1.1" + ], + "validationBody": [ + "4K Earth Science Private Limited", + "AENOR International S.A.U.", + "Agri-Waste Technology, Inc.", + "Aster Global Environmental Solutions, Inc.", + "Carbon Check (India) Private Ltd.", + "China Building Material Test & Certification Group Co. LTD. (CTC)", + "China Classification Society Certification Co. Ltd. (CCSC)", + "China Environmental United Certification Center Co., Ltd. (CEC)", + "China Quality Certification Center (CQC)", + "Colombian Institute for Technical Standards and Certification (ICONTEC)", + "Earthood Services Private Limited", + "Enviro-Accès Inc.", + "EPIC Sustainability Services Pvt. Ltd.", + "ERM Certification and Verification Services Ltd.", + "First Environment, Inc.", + "GHD Services Inc.", + "KBS Certification Services Pvt. Ltd.", + "LGAI Technological Center, S.A. (Applus+)", + "Re Carbon Ltd.", + "RINA Services S.p.A", + "Ruby Canyon Environmental, Inc", + "S&A Carbon, LLC", + "SCS Global Services", + "Shenzhen CTI International Certification Co., Ltd (CTI)", + "TÜV Nord Cert GmbH", + "TÜV SÜD South Asia Private Limited" + ], + "countries": [ + "Afghanistan", + "Albania", + "Algeria", + "Andorra", + "Angola", + "Antigua and Barduba", + "Argentina", + "Australia", + "Austria", + "Azerbaijan", + "Bahamas", + "Bahrain", + "Bangladesh", + "Barbados", + "Belarus", + "Belgium", + "Belize", + "Benin", + "Bhutan", + "Bolivia", + "Bosnia and Herzegovina", + "Botswana", + "Brazil", + "Brunei Darussalam", + "Bulgaria", + "Burkina Faso", + "Burundi", + "Cabo Verde", + "Cambodia", + "Cameroon", + "Canada", + "Central African Republic", + "Chad", + "China", + "Colombia", + "Comoros", + "Congo", + "Costa Rica", + "Cote d'Ivoire", + "Croatia", + "Cuba", + "Cyprus", + "Czech Republic", + "Democratic People's Republic of Korea", + "Democratic Republic of Congo", + "Denmark", + "Djibouti", + "Dominica", + "Domincan Republic", + "Egypt", + "El Salvador", + "Equatorial New Guinea", + "Eritrea", + "Estonia", + "Ethiopia", + "European Union", + "Fiji", + "Finland", + "France", + "Gabon", + "Georgia", + "Germany", + "Ghana", + "Greece", + "Grenada", + "Guatemala", + "Guinea", + "Guinea Bissau", + "Guyana", + "Haiti", + "Honduras", + "Hungary", + "Iceland", + "India", + "Indonesia", + "Iran", + "Ireland", + "Israel", + "Italy", + "Jamaica", + "Japan", + "Jordan", + "Kenya", + "Kiribati", + "Kuwait", + "Lao People's Democratic Republic", + "Latvia", + "Lebanon", + "Lesotho", + "Liberia", + "Libya", + "Liechtenstein", + "Lithuania", + "Luxembourg", + "Madagascar", + "Malaysia", + "Maldives", + "Mali", + "Malta", + "Marshall Islands", + "Mauritius", + "Mauritania", + "Mexico", + "Micronesia", + "Monaco", + "Mongolia", + "Montenegro", + "Morocco", + "Mozambique", + "Myanmar", + "Namibia", + "Nauru", + "Nepal", + "Netherlands", + "New Zealand", + "Niger", + "Norway", + "Oman", + "Pakistan", + "Palau", + "Panama", + "Papua New Guinea", + "Paraguay", + "Peru", + "Philippines", + "Poland", + "Portugal", + "Qatar", + "Republic of Korea", + "Romania", + "Russian Federation", + "Rwanda", + "Saint Kitts and Nevis", + "Saint Lucia", + "Saint Vincent and the Grenadines", + "Samoa", + "San Marino", + "Sao Tome and Principe", + "Senegal", + "Serbia", + "Singapore", + "Slovakia", + "Slovenia", + "Solomon Islands", + "Somalia", + "South Africa", + "South Sudan", + "Spain", + "Sri Lanka", + "State of Palestine", + "Sudan", + "Suriname", + "Swaziland", + "Sweden", + "Switzerland", + "Tajikistan", + "Thailand", + "The Former Yugoslav Republic of Macedonia", + "Timor-Leste", + "Tonga", + "Trinidad and Tobago", + "Tunisia", + "Turkey", + "Tuvalu", + "Uganda", + "Ukraine", + "United Arab Emirates", + "United Kingdom", + "United Republic of Tanzania", + "United States of America", + "Uruguay", + "Vanuatu", + "Venezuela", + "Viet Nam", + "Zimbabwe" + ], + "ratingType": ["CDP", "CCQI"], + "unitType": [ + "Reduction - nature", + "Reduction - technical", + "Removal - nature", + "Removal - technical" + ], + "unitStatus": [ + "Held", + "Retired", + "Cancelled", + "Expired", + "Buffer", + "Transferred", + "For Sale", + "Pending Transfer", + "Purchased" + ], + "correspondingAdjustmentDeclaration": [ + "Committed", + "Not Required", + "Unknown" + ], + "correspondingAdjustmentStatus": ["Not Started", "Pending", "Completed"], + "labelType": ["Endorsement", "Letter of Qualification", "Certification"] +} diff --git a/src/models/index.js b/src/models/index.js index 4f8c9e44..892f19fc 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -34,7 +34,7 @@ export * from './simulator'; export * from './labelUnits'; export * from './estimations'; export * from './audit'; -export * from './Governance'; +export * from './governance'; export const ModelKeys = { unit: Unit, diff --git a/src/routes/v1/index.js b/src/routes/v1/index.js index 288613c7..ee0838c7 100644 --- a/src/routes/v1/index.js +++ b/src/routes/v1/index.js @@ -11,6 +11,7 @@ import { IssuanceRouter, LabelRouter, AuditRouter, + GovernanceRouter, } from './resources'; V1Router.use('/projects', ProjectRouter); @@ -20,5 +21,6 @@ V1Router.use('/organizations', OrganizationRouter); V1Router.use('/issuances', IssuanceRouter); V1Router.use('/labels', LabelRouter); V1Router.use('/audit', AuditRouter); +V1Router.use('/governance', GovernanceRouter); export { V1Router }; diff --git a/src/routes/v1/resources/governance.js b/src/routes/v1/resources/governance.js new file mode 100644 index 00000000..11868ce4 --- /dev/null +++ b/src/routes/v1/resources/governance.js @@ -0,0 +1,48 @@ +'use strict'; + +import express from 'express'; +import joiExpress from 'express-joi-validation'; + +import { GovernanceController } from '../../../controllers'; +import { + governanceSubscribeSchema, + setOrgListSchema, + governancePickListSchema, +} from '../../../validations'; + +const validator = joiExpress.createValidator({ passError: true }); +const GovernanceRouter = express.Router(); + +GovernanceRouter.get('/', (req, res) => { + return GovernanceController.findAll(req, res); +}); + +GovernanceRouter.post('/', (req, res) => { + return GovernanceController.createGoveranceBody(req, res); +}); + +GovernanceRouter.post( + '/meta/orgList', + validator.body(setOrgListSchema), + (req, res) => { + return GovernanceController.setDefaultOrgList(req, res); + }, +); + +GovernanceRouter.post( + '/meta/pickList', + validator.body(governancePickListSchema), + (req, res) => { + return GovernanceController.setPickList(req, res); + }, +); + +GovernanceRouter.post( + '/subscribe', + validator.body(governanceSubscribeSchema), + (req, res) => { + return GovernanceController.subscribeToGovernanceBody(req, res); + }, +); + +export { GovernanceRouter }; diff --git a/src/routes/v1/resources/index.js b/src/routes/v1/resources/index.js index ed480d88..4edf37c5 100644 --- a/src/routes/v1/resources/index.js +++ b/src/routes/v1/resources/index.js @@ -5,3 +5,4 @@ export * from './organization'; export * from './issuances'; export * from './labels'; export * from './audit'; +export * from './governance'; diff --git a/src/tasks/index.js b/src/tasks/index.js index 030be44f..061d4ceb 100644 --- a/src/tasks/index.js +++ b/src/tasks/index.js @@ -5,6 +5,7 @@ import syncDefaultOrganizations from './sync-default-organizations'; import syncPickLists from './sync-picklists'; import syncAudit from './sync-audit-table'; import syncOrganizationMeta from './sync-organization-meta'; +import syncGovernanceBody from './sync-governance-body'; const scheduler = new ToadScheduler(); @@ -18,6 +19,7 @@ const addJobToScheduler = (job) => { const start = () => { // add default jobs const defaultJobs = [ + syncGovernanceBody, syncDataLayer, syncDefaultOrganizations, syncPickLists, diff --git a/src/tasks/sync-default-organizations.js b/src/tasks/sync-default-organizations.js index 81b28d9c..45f72369 100644 --- a/src/tasks/sync-default-organizations.js +++ b/src/tasks/sync-default-organizations.js @@ -16,7 +16,7 @@ const task = new Task('sync-default-organizations', () => { }); const job = new SimpleIntervalJob( - { days: 1, runImmediately: true }, + { seconds: 30, runImmediately: true }, task, 'sync-default-organizations', ); diff --git a/src/tasks/sync-governance-body.js b/src/tasks/sync-governance-body.js new file mode 100644 index 00000000..3a6e95a4 --- /dev/null +++ b/src/tasks/sync-governance-body.js @@ -0,0 +1,28 @@ +import { SimpleIntervalJob, Task } from 'toad-scheduler'; +import { Governance } from '../models'; +import dotenv from 'dotenv'; +dotenv.config(); + +import Debug from 'debug'; +Debug.enable('climate-warehouse:task:governance'); + +const log = Debug('climate-warehouse:task:governance'); + +const task = new Task('sync-governance-meta', () => { + log('Syncing governance data'); + if ( + process.env.GOVERANCE_BODY_ID && + process.env.GOVERNANCE_BODY_IP && + process.env.GOVERNANCE_BODY_PORT + ) { + Governance.sync(); + } +}); + +const job = new SimpleIntervalJob( + { days: 1, runImmediately: true }, + task, + 'sync-governance-meta', +); + +export default job; diff --git a/src/tasks/sync-picklists.js b/src/tasks/sync-picklists.js index 3b9acd46..ce6a00f8 100644 --- a/src/tasks/sync-picklists.js +++ b/src/tasks/sync-picklists.js @@ -11,7 +11,7 @@ const task = new Task('sync-picklist', () => { }); const job = new SimpleIntervalJob( - { days: 1, runImmediately: true }, + { seconds: 30, runImmediately: true }, task, 'sync-picklist', ); diff --git a/src/utils/data-assertions.js b/src/utils/data-assertions.js index af0f7e20..5219383f 100644 --- a/src/utils/data-assertions.js +++ b/src/utils/data-assertions.js @@ -2,11 +2,31 @@ import _ from 'lodash'; -import { Organization, Unit, Project, Staging } from '../models'; +import { Organization, Unit, Project, Staging, Meta } from '../models'; import { transformSerialNumberBlock } from '../utils/helpers'; import datalayer from '../datalayer'; import { formatModelAssociationName } from './model-utils.js'; +export const assertCanBeGovernanceBody = async () => { + if (process.env.IS_GOVERNANCE_BODY !== 'true') { + throw new Error( + 'You are not an governance body and can not use this functionality', + ); + } +}; + +export const assertIsActiveGovernanceBody = async () => { + const governanceBodyIsSetUp = Meta.findAll({ + where: { metaKey: 'goveranceBodyId' }, + }); + + if (!governanceBodyIsSetUp) { + throw new Error( + 'You are not an governance body and can not use this functionality', + ); + } +}; + export const assertDataLayerAvailable = async () => { const isAvailable = await datalayer.dataLayerAvailable(); diff --git a/src/utils/data-loaders.js b/src/utils/data-loaders.js index e73d72e0..5c347f8c 100644 --- a/src/utils/data-loaders.js +++ b/src/utils/data-loaders.js @@ -1,26 +1,33 @@ import request from 'request-promise'; -import dotenv from 'dotenv'; -dotenv.config(); + +import { Governance } from '../models'; +import PickListStub from '../models/governance/governance.stub.json'; let downloadedPickList = {}; export const getPicklistValues = () => downloadedPickList; export const pullPickListValues = async () => { - const options = { - method: 'GET', - url: process.env.PICKLIST_URL, - }; + if (process.env.USE_SIMULATOR === 'true') { + downloadedPickList = PickListStub; + } else { + const goveranceData = await Governance.findOne({ + where: { metaKey: 'pickList' }, + raw: true, + }); - downloadedPickList = JSON.parse(await request(Object.assign({}, options))); + if (goveranceData.metaValue) { + downloadedPickList = JSON.parse(goveranceData.metaValue); + } + } }; export const getDefaultOrganizationList = async () => { - const options = { - method: 'GET', - url: process.env.DEFAULT_ORGANIZATIONS, - }; + const goveranceData = await Governance.findOne({ + where: { metaKey: 'orgList' }, + raw: true, + }); - return JSON.parse(await request(Object.assign({}, options))); + return JSON.parse(goveranceData.metaValue); }; export const serverAvailable = async (server, port) => { diff --git a/src/utils/datalayer-utils.js b/src/utils/datalayer-utils.js index caf6a419..6ff77ad7 100644 --- a/src/utils/datalayer-utils.js +++ b/src/utils/datalayer-utils.js @@ -7,7 +7,6 @@ export const decodeHex = (str) => { }; export const decodeDataLayerResponse = (data) => { - console.log(data); return data.keys_values.map((item) => ({ key: decodeHex(item.key), value: decodeHex(item.value), diff --git a/src/validations/governance.validations.js b/src/validations/governance.validations.js new file mode 100644 index 00000000..e4985300 --- /dev/null +++ b/src/validations/governance.validations.js @@ -0,0 +1,37 @@ +import Joi from 'joi'; + +export const governanceSubscribeSchema = Joi.object().keys({ + orgUid: Joi.string().required(), +}); + +export const setOrgListSchema = Joi.array().items( + Joi.object({ + orgUid: Joi.string().required(), + ip: Joi.string().required(), + port: Joi.number().integer().required(), + }), +); + +export const governancePickListSchema = Joi.object().keys({ + registries: Joi.array().items(Joi.string()).min(1).required(), + projectSector: Joi.array().items(Joi.string()).min(1).required(), + projectType: Joi.array().items(Joi.string()).min(1).required(), + coveredByNDC: Joi.array().items(Joi.string()).min(1).required(), + projectStatusValues: Joi.array().items(Joi.string()).min(1).required(), + unitMetric: Joi.array().items(Joi.string()).min(1).required(), + methodology: Joi.array().items(Joi.string()).min(1).required(), + validationBody: Joi.array().items(Joi.string()).min(1).required(), + countries: Joi.array().items(Joi.string()).min(1).required(), + ratingType: Joi.array().items(Joi.string()).min(1).required(), + unitType: Joi.array().items(Joi.string()).min(1).required(), + unitStatus: Joi.array().items(Joi.string()).min(1).required(), + correspondingAdjustmentDeclaration: Joi.array() + .items(Joi.string()) + .min(1) + .required(), + correspondingAdjustmentStatus: Joi.array() + .items(Joi.string()) + .min(1) + .required(), + labelType: Joi.array().items(Joi.string()).min(1).required(), +}); diff --git a/src/validations/index.js b/src/validations/index.js index 93898982..d9d751de 100644 --- a/src/validations/index.js +++ b/src/validations/index.js @@ -10,3 +10,5 @@ export * from './estimations.validations'; export * from './projects.validations'; export * from './units.validations'; export * from './audit.validations'; +export * from './estimations.validations'; +export * from './governance.validations'; diff --git a/src/websocket.js b/src/websocket.js index c55d4f1b..80c3ade5 100644 --- a/src/websocket.js +++ b/src/websocket.js @@ -8,7 +8,6 @@ const socketSubscriptions = {}; const authenticate = () => true; export const connection = (socket) => { - console.log(socketSubscriptions); socket.on('authentication', () => { if (!authenticate(socket)) { console.log('authentication failure');