| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- 'use strict';
- const logger = require('../logger');
- const nantum = require('../modules/nantum');
- const { vtnId } = require('../config');
- function calculateEventStatus(notificationTime, startTime, endTime) {
- const nowMillis = new Date().getTime();
- if (nowMillis < new Date(startTime).getTime()) {
- return 'far';
- }
- if (nowMillis > new Date(endTime).getTime()) {
- return 'completed';
- }
- return 'active';
- }
- function calculateDurationSeconds(startTime, endTime) {
- return Math.round(
- (new Date(endTime).getTime() - new Date(startTime).getTime()) / 1000,
- );
- }
- function calculateDuration(startTime, endTime) {
- return `PT${calculateDurationSeconds(startTime, endTime)}S`;
- }
- function calculateNotificationDuration(notificationTime, startTime) {
- if (!notificationTime) {
- return 'PT0S';
- }
- return calculateDuration(notificationTime, startTime);
- }
- function calculateEventIntervals(eventInfoValues, eventDurationSeconds) {
- //TODO: this is likely incorrect. Get more details on the event_info_value data model.
- let result = [];
- for (let i = 0; i < eventInfoValues.length; i++) {
- const eventInfoValue = eventInfoValues[i];
- const nextOffset =
- i === eventInfoValues.length - 1
- ? eventDurationSeconds - eventInfoValue.timeOffset
- : eventInfoValues[i + 1].timeOffset;
- result.push({
- signalPayloads: [eventInfoValue.value],
- duration: `PT${nextOffset}S`,
- uid: `${i + 1}`,
- });
- }
- return result;
- }
- function calculateEventSignals(eventInstances, eventDurationSeconds) {
- return eventInstances.map(eventInstance => {
- return {
- signalName: eventInstance.event_type_id,
- signalId: '112233445566',
- signalType: 'level',
- intervals: calculateEventIntervals(
- eventInstance.event_info_values,
- eventDurationSeconds,
- ),
- };
- });
- }
- function convertToOadrEvents(nantumEvent) {
- if (!nantumEvent.dr_event_data) {
- // no event
- return [];
- }
- const nowMillis = new Date().getTime();
- if (
- nowMillis < new Date(nantumEvent.dr_event_data.notification_time).getTime()
- ) {
- return []; // not in the notification period yet
- }
- return [
- {
- eventDescriptor: {
- eventId: nantumEvent.event_identifier,
- modificationNumber: nantumEvent.event_mod_number,
- marketContext: 'http://MarketContext1',
- createdDateTime: '2020-04-14T16:06:39.000Z',
- eventStatus: calculateEventStatus(
- nantumEvent.dr_event_data.notification_time,
- nantumEvent.dr_event_data.start_time,
- nantumEvent.dr_event_data.end_time,
- ),
- testEvent: nantumEvent.test_event,
- priority: 0,
- },
- activePeriod: {
- startDate: nantumEvent.dr_event_data.start_time,
- duration: calculateDuration(
- nantumEvent.dr_event_data.start_time,
- nantumEvent.dr_event_data.end_time,
- ),
- notificationDuration: calculateNotificationDuration(
- nantumEvent.dr_event_data.notification_time,
- nantumEvent.dr_event_data.start_time,
- ),
- },
- signals: {
- event: calculateEventSignals(
- nantumEvent.dr_event_data.event_instance,
- calculateDurationSeconds(
- nantumEvent.dr_event_data.start_time,
- nantumEvent.dr_event_data.end_time,
- ),
- ),
- },
- target: {
- venId: [nantumEvent.client_id],
- },
- responseRequired: 'always',
- },
- ];
- }
- async function retrieveEvents(
- oadrRequestEvent,
- clientCertificateCn,
- clientCertificateFingerprint,
- ) {
- logger.info(
- 'retrieveEvents',
- oadrRequestEvent,
- clientCertificateCn,
- clientCertificateFingerprint,
- );
- const requestVenId = oadrRequestEvent.venId;
- if (!requestVenId) {
- const error = new Error('No VenID in request');
- error.responseCode = 452;
- throw error;
- }
- if (requestVenId !== clientCertificateFingerprint) {
- // as per certification item #512, venId MUST be case-sensitive
- const error = new Error('VenID does not match certificate');
- error.responseCode = 452;
- throw error;
- }
- if (!clientCertificateCn) {
- const error = new Error('Could not determine CN from client certificate');
- error.responseCode = 452;
- throw error;
- }
- const event = await nantum.fetchEvent(requestVenId);
- return {
- responseCode: '200',
- responseDescription: 'OK',
- responseRequestId: oadrRequestEvent.requestId || '',
- requestId: oadrRequestEvent.requestId || '',
- vtnId: vtnId,
- events: convertToOadrEvents(event),
- };
- }
- /* qualifiedEvent is the combination of eventId & modificationNumber */
- function eventResponseMatchesValidEvent(eventResponse, oadrEvents) {
- return (
- oadrEvents.filter(oadrEvent => {
- return (
- oadrEvent.eventDescriptor.eventId === eventResponse.eventId &&
- oadrEvent.eventDescriptor.modificationNumber ===
- eventResponse.modificationNumber &&
- oadrEvent.eventDescriptor.status !== 'cancelled' &&
- oadrEvent.eventDescriptor.status !== 'completed'
- );
- }).length > 0
- );
- }
- async function validateEventResponses(venId, eventResponses) {
- const event = await nantum.fetchEvent(venId);
- const oadrEvents = convertToOadrEvents(event);
- const staleResponses = eventResponses.filter(
- eventResponse => !eventResponseMatchesValidEvent(eventResponse, oadrEvents),
- );
- if (staleResponses.length > 0) {
- const error = new Error('Event response references invalid event');
- error.responseCode = '454';
- throw error;
- }
- }
- async function updateOptType(
- oadrCreatedEvent,
- clientCertificateCn,
- clientCertificateFingerprint,
- ) {
- logger.info(
- 'updateOptType',
- oadrCreatedEvent,
- clientCertificateCn,
- clientCertificateFingerprint,
- );
- const requestVenId = oadrCreatedEvent.venId;
- validateVenId(requestVenId, clientCertificateFingerprint, true);
- let opted = await nantum.fetchOpted(requestVenId);
- try {
- await validateEventResponses(requestVenId, oadrCreatedEvent.eventResponses);
- for (const eventResponse of oadrCreatedEvent.eventResponses) {
- // remove existing opts for this eventId
- opted = [
- ...opted.filter(
- optedItem => optedItem.eventId !== eventResponse.eventId,
- ),
- ];
- opted.push({
- eventId: eventResponse.eventId,
- modificationNumber: eventResponse.modificationNumber,
- optType: eventResponse.optType,
- });
- }
- await nantum.updateOpted(requestVenId, opted);
- return {
- responseCode: '200',
- responseDescription: 'OK',
- venId: clientCertificateFingerprint,
- };
- } catch (e) {
- return {
- responseCode: e.responseCode || '454',
- responseDescription: e.message || 'Invalid event response received',
- venId: clientCertificateFingerprint,
- };
- }
- }
- async function filterOutAcknowledgedEvents(venId, events) {
- const opted = (await nantum.fetchOpted(venId)) || [];
- return events.filter(
- event =>
- opted.filter(
- optedItem =>
- optedItem.eventId === event.eventDescriptor.eventId &&
- optedItem.modificationNumber ===
- event.eventDescriptor.modificationNumber,
- ).length === 0,
- );
- }
- async function pollForEvents(
- oadrPoll,
- clientCertificateCn,
- clientCertificateFingerprint,
- ) {
- logger.info(
- 'pollForEvents',
- oadrPoll,
- clientCertificateCn,
- clientCertificateFingerprint,
- );
- const requestVenId = oadrPoll.venId;
- validateVenId(requestVenId, clientCertificateFingerprint, true);
- const event = await nantum.fetchEvent(requestVenId);
- const filteredEvents = await filterOutAcknowledgedEvents(
- requestVenId,
- convertToOadrEvents(event),
- );
- if (filteredEvents.length > 0) {
- return {
- responseCode: '200',
- responseDescription: 'OK',
- responseRequestId: '', // required field, but empty is allowed as per spec
- requestId: '',
- vtnId: vtnId,
- events: filteredEvents,
- };
- }
- return undefined;
- }
- function validateVenId(requestVenId, clientCertificateFingerprint, required) {
- if (requestVenId === clientCertificateFingerprint) {
- return;
- }
- if (!required && requestVenId == null) {
- return;
- }
- if (required && requestVenId == null) {
- const error = new Error('VenID is missing');
- error.responseCode = 452;
- throw error;
- }
- const error = new Error('VenID is invalid');
- error.responseCode = 452;
- throw error;
- }
- module.exports = {
- pollForEvents,
- retrieveEvents,
- updateOptType,
- };
|