'use strict'; const { parse, serialize } = require('../xml'); const axios = require('axios'); const { v4 } = require('uuid'); const { escape } = require('querystring'); class Ven { constructor( endpoint, clientCertPem, commonName, venId, venName, registrationId, ) { if (!endpoint.endsWith('/')) endpoint += '/'; this.endpoint = endpoint; this.clientCertHeader = escape(clientCertPem); this.venId = venId; this.registrationId = registrationId; this.venName = venName; this.commonName = commonName; } async queryRegistration() { const message = { _type: 'oadrQueryRegistration', requestId: v4(), }; const createdResponse = await this.makeRequest('EiRegisterParty', message); // track registrationId for subsequent requests this.registrationId = createdResponse.registrationId; return createdResponse; } async cancelRegistration() { const message = { _type: 'oadrCancelPartyRegistration', requestId: v4(), registrationId: this.registrationId, venId: this.venId, }; const cancelledResponse = await this.makeRequest( 'EiRegisterParty', message, ); // track registrationId for subsequent requests this.registrationId = undefined; return cancelledResponse; } async register() { const message = { _type: 'oadrCreatePartyRegistration', requestId: v4(), registrationId: this.registrationId, venId: this.venId, oadrProfileName: '2.0b', oadrTransportName: 'simpleHttp', oadrReportOnly: false, oadrXmlSignature: false, oadrVenName: this.venName, oadrHttpPullModel: true, }; const createdResponse = await this.makeRequest('EiRegisterParty', message); // track registrationId for subsequent requests this.registrationId = createdResponse.registrationId; return createdResponse; } async requestEvents() { const message = { _type: 'oadrRequestEvent', requestId: v4(), venId: this.venId, }; const response = await this.makeRequest('EiEvent', message); return response; } async poll() { const message = { _type: 'oadrPoll', venId: this.venId, }; const response = await this.makeRequest('OadrPoll', message); return response; } async opt(optType, eventId, modificationNumber) { const message = { _type: 'oadrCreatedEvent', responseCode: '200', responseDescription: 'OK', responseRequestId: '', venId: this.venId, eventResponses: [ { responseCode: '200', responseDescription: 'OK', responseRequestId: '', optType: optType, eventId: eventId, modificationNumber: modificationNumber, }, ], }; return await this.makeRequest('EiEvent', message); } async registerReports(oadrReports) { const message = { _type: 'oadrRegisterReport', requestId: v4(), venId: this.venId, reports: oadrReports, }; return await this.makeRequest('EiReport', message); } async notifyCreatedReports(createRequestId, reportRequestIds) { const message = { _type: 'oadrCreatedReport', responseCode: '200', responseDescription: 'OK', responseRequestId: createRequestId, venId: this.venId, pendingReports: reportRequestIds.map(x => ({ reportRequestId: x })), }; return await this.makeRequest('EiReport', message); } async sendReportData(reports) { const message = { _type: 'oadrUpdateReport', reports: reports, requestId: v4(), }; return await this.makeRequest('EiReport', message); } async makeRequest(service, message) { const xml = serialize(message); const config = { headers: { 'Content-Type': 'application/xml', // these next 2 headers are provided manually for now, simulating what nginx is doing. SSL_CLIENT_S_DN_CN: this.commonName || 'no_client_cert', SSL_CLIENT_CERTIFICATE: this.clientCertHeader, }, }; // axios will automatically reject HTTP response codes outside the 200-299 range const httpResponse = await axios.post( `${this.endpoint}OpenADR2/Simple/2.0b/${service}`, xml, config, ); const response = await parse(httpResponse.data); // but OpenADR provides its own response code in the XML envelope, we need to check that if (response.responseCode < 200 || response.responseCode >= 300) { throw new Error( `Error during ${service} call. ResponseCode=${response.responseCode}, ResponseDescription=${response.responseDescription}`, ); } return response; } } module.exports = { Ven, };