diff --git a/plugins/eginnovations/v1/.gitignore b/plugins/eginnovations/v1/.gitignore new file mode 100644 index 0000000..3685b93 --- /dev/null +++ b/plugins/eginnovations/v1/.gitignore @@ -0,0 +1,6 @@ +# Dependency directories +node_modules/ +jspm_packages/ + +# Test files +testConfig.json diff --git a/plugins/eginnovations/v1/cspell.json b/plugins/eginnovations/v1/cspell.json new file mode 100644 index 0000000..c88d4ae --- /dev/null +++ b/plugins/eginnovations/v1/cspell.json @@ -0,0 +1,6 @@ +{ + "version": "0.2", + "words": [ + "eGinnovations" + ] +} diff --git a/plugins/eginnovations/v1/custom_types.json b/plugins/eginnovations/v1/custom_types.json new file mode 100644 index 0000000..945c723 --- /dev/null +++ b/plugins/eginnovations/v1/custom_types.json @@ -0,0 +1,9 @@ +[ + { + "name": "eGComponents", + "type": "app", + "icon": "app", + "singular": "app", + "plural": "apps" + } +] \ No newline at end of file diff --git a/plugins/eginnovations/v1/data_streams.json b/plugins/eginnovations/v1/data_streams.json new file mode 100644 index 0000000..a2e0dc9 --- /dev/null +++ b/plugins/eginnovations/v1/data_streams.json @@ -0,0 +1,623 @@ +{ + "rowTypes": [ + { + "name": "Alarms", + "metadata": [ + { + "name": "healthState", + "displayName": "Health State", + "shape": "string", + "role": "label" + }, + { + "name": "count", + "displayName": "Count", + "shape": ["number",{"decimalPlaces": 0}], + "role": "value" + } + ] + }, + { + "name": "HistoricalData", + "metadata": [ + { + "name": "date", + "displayName": "Date", + "shape": "date", + "role": "label" + }, + { + "name": "componentType", + "displayName": "Component Type", + "shape": "string", + "role": "label" + }, + { + "name": "measure", + "displayName": "Measure", + "shape": "string", + "role": "label" + }, + { + "name": "test", + "displayName": "Test", + "shape": "string", + "role": "label" + }, + { + "name": "componentName", + "displayName": "Component Name", + "shape": "string", + "role": "label" + }, + { + "name": "descriptor", + "displayName": "Descriptor", + "shape": "string", + "role": "label" + }, + { + "name": "value", + "displayName": "Value", + "shape": "number", + "role": "value" + } + ] + }, + { + "name": "Alerts", + "metadata": [ + { + "name": "priority", + "displayName": "Priority", + "shape": "string", + "role": "label" + }, + { + "name": "startTime", + "displayName": "Start Time", + "shape": "string", + "role": "label" + }, + { + "name": "componentType", + "displayName": "Component Type", + "shape": "string", + "role": "label" + }, + { + "name": "componentName", + "displayName": "Component Name", + "shape": "string", + "role": "label" + }, + { + "name": "description", + "displayName": "Description", + "shape": "string", + "role": "label" + }, + { + "name": "layer", + "displayName": "Layer", + "shape": "string", + "role": "label" + }, + { + "name": "test", + "displayName": "Test", + "shape": "string", + "role": "label" + }, + { + "name": "measure", + "displayName": "Measure", + "shape": "string", + "role": "label" + }, + + { + "name": "service", + "displayName": "Service", + "shape": "string", + "role": "label" + }, + { + "name": "alarmID", + "displayName": "Alarm ID", + "shape": "string", + "role": "label" + }, + { + "name": "info", + "displayName": "Info", + "shape": "string", + "role": "label" + } + ] + }, + { + "name": "ComponentsState", + "metadata": [ + { + "name": "priority", + "displayName": "Priority", + "shape": "string", + "role": "label" + }, + { + "name": "componentType", + "displayName": "Component Type", + "shape": "string", + "role": "label" + }, + { + "name": "componentName", + "displayName": "Component Name", + "shape": "string", + "role": "label" + }, + { + "name": "state", + "displayName": "State", + "shape": "string", + "role": "label" + } + ] + }, + { + "name": "LiveMeasure", + "metadata": [ + { + "name": "priority", + "displayName": "Priority", + "shape": "string", + "role": "label" + }, + { + "name": "test", + "displayName": "Test", + "shape": "string", + "role": "label" + }, + { + "name": "measure", + "displayName": "Measure", + "shape": "string", + "role": "label" + }, + { + "name": "unit", + "displayName": "Unit", + "shape": "string", + "role": "label" + }, + { + "name": "descriptor", + "displayName": "Descriptor", + "shape": "string", + "role": "label" + }, + + { + "name": "value", + "displayName": "Value", + "shape": "string", + "role": "label" + }, + + { + "name": "lastMeasurementTime", + "displayName": "Last Measure Time", + "shape": "date", + "role": "label" + }, + { + "name": "componentName", + "displayName": "Component Name", + "shape": "string", + "role": "label" + }, + + { + "name": "componentType", + "displayName": "Component Type", + "shape": "string", + "role": "label" + } + ] + } + ], + "dataSources": [ + { + "name": "getHistoricalData", + "displayName": "Historical Data", + "description": "Historical Data For Components", + "supportedScope": "none" + }, + { + "name": "getLiveMeasure", + "displayName": "Live Measure", + "description": "Live Measure For Components", + "supportedScope": "none" + }, + { + "name": "getComponentsByState", + "displayName": "Components By State", + "description": "Reporting and Non-reporting Components", + "supportedScope": "none" + }, + { + "name": "getAlarmCount", + "displayName": "alarms", + "description": "Current Status", + "supportedScope": "none" + }, + { + "name": "getAlerts", + "displayName": "Alerts", + "description": "Infrastructure Health", + "supportedScope": "none" + }, + + { + "name": "getComponentsByType", + "displayName": "Component Type", + "description": "Component Type", + "supportedScope": "none" + }, + { + "name": "getUserComponentsForType", + "displayName": "Component Name", + "description": "Component Name", + "supportedScope": "none" + }, + { + "name": "getTestForType", + "displayName": "Test Name", + "description": "Test Name", + "supportedScope": "none" + }, + { + "name": "getMeasureForTest", + "displayName": "Measure Name", + "description": "Measure Name", + "supportedScope": "none" + } + ], + "matches": { + "__configId": { + "type": "equals", + "value": "{{configId}}" + } + }, + "dataStreams": [ + { + "displayName": "Live Measure", + "description": "Live Measure For Components", + "dataSourceName": "getLiveMeasure", + "definition": { + "timeframes": false, + "name": "Live Measure", + "dataSourceConfig": {}, + "rowPath": [], + "matches": "none", + "rowType": { + "name": "LiveMeasure" + } + }, + "template": [ + { + "name": "componentType", + "type": "autocomplete", + "label": "Component Type", + "title": "Component Type", + "help": "Select the Component Type", + "validation": { "required": true }, + "placeholder": "Select the Component Type", + "isMulti": false, + "data": { + "source": "dataStream", + "dataStreamName": "getComponentsListByType", + "dataSourceConfig": {} + } + }, + { + "name": "componentName", + "type": "autocomplete", + "label": "Component Name", + "title": "Component Name", + "help": "Select the Component Name", + "validation": { "required": true }, + "placeholder": "Select the Component Name", + "isMulti": false, + "data": { + "source": "dataStream", + "dataStreamName": "getUserComponentsListForType", + "dataSourceConfig": { "componentType": { "fieldName": "componentType", "required": true } } + } + }, + { + "name": "test", + "type": "autocomplete", + "label": "Test Name", + "title": "Test Name", + "help": "Select the Test Name", + "validation": { "required": true }, + "placeholder": "Select the Test Name", + "isMulti": false, + "data": { + "source": "dataStream", + "dataStreamName": "getTestListForType", + "dataSourceConfig": { "componentType": { "fieldName": "componentType", "required": true } } + } + }, + { + "name": "measure", + "type": "autocomplete", + "label": "Measure", + "title": "Measure", + "help": "Select the Measure Name", + "validation": { "required": true }, + "placeholder": "Select the measure Name", + "isMulti": false, + "data": { + "source": "dataStream", + "dataStreamName": "getMeasureListForTest", + "dataSourceConfig": { + "componentType": { "fieldName": "componentType", "required": true }, + "test": { "fieldName": "test", "required": true } + } + } + } + ] + }, + { + "displayName": "Components Type", + "description": "Select the components type", + "dataSourceName": "getComponentsByType", + "definition": { + "name": "getComponentsListByType", + "timeframes": false, + "provides": "templateData", + "options": { "noMatch": true }, + "dataSourceConfig": {}, + "rowPath": [], + "matches": "none", + "metadata": [ + { + "name": "componentType", + "role": "value", + "displayName": "Component Type", + "shape": "string" + } + ] + } + }, + { + "displayName": "Components Name", + "description": "Select the components Name", + "dataSourceName": "getUserComponentsForType", + "definition": { + "name": "getUserComponentsListForType", + "timeframes": false, + "provides": "templateData", + "options": { "noMatch": true }, + "dataSourceConfig": {}, + "rowPath": [], + "matches": "none", + "metadata": [ + { + "name": "componentName", + "role": "value", + "displayName": "Component Name", + "shape": "string" + } + ] + } + }, + { + "displayName": "Test Name", + "description": "Select the Test Name", + "dataSourceName": "getTestForType", + "definition": { + "name": "getTestListForType", + "timeframes": false, + "provides": "templateData", + "options": { "noMatch": true }, + "dataSourceConfig": {}, + "rowPath": [], + "matches": "none", + "metadata": [ + { + "name": "test", + "role": "value", + "displayName": "Test", + "shape": "string" + } + ] + } + }, + { + "displayName": "Measure Name", + "description": "Select the Measure Name", + "dataSourceName": "getMeasureForTest", + "definition": { + "name": "getMeasureListForTest", + "timeframes": false, + "provides": "templateData", + "options": { "noMatch": true }, + "dataSourceConfig": {}, + "rowPath": [], + "matches": "none", + "metadata": [ + { + "name": "measure", + "role": "value", + "displayName": "Measure", + "shape": "string" + } + ] + } + }, + { + "displayName": "Historical Data", + "description": "Historical Data For Components", + "dataSourceName": "getHistoricalData", + "definition": { + "timeframes": false, + "name": "Historical Data", + "dataSourceConfig": {}, + "rowPath": [], + "matches": "none", + "rowType": { + "name": "HistoricalData" + } + }, + "template": [ + { + "name": "componentType", + "type": "autocomplete", + "label": "Component Type", + "title": "Component Type", + "help": "Select the Component Type", + "validation": { "required": true }, + "placeholder": "Select the Component Type", + "isMulti": false, + "data": { + "source": "dataStream", + "dataStreamName": "getComponentsListByType", + "dataSourceConfig": {} + } + }, + { + "name": "componentName", + "type": "autocomplete", + "label": "Component Name", + "title": "Component Name", + "help": "Select the Component Name", + "validation": { "required": true }, + "placeholder": "Select the Component Name", + "isMulti": false, + "data": { + "source": "dataStream", + "dataStreamName": "getUserComponentsListForType", + "dataSourceConfig": { "componentType": { "fieldName": "componentType", "required": true } } + } + }, + { + "name": "test", + "type": "autocomplete", + "label": "Test Name", + "title": "Test Name", + "help": "Select the Test Name", + "validation": { "required": true }, + "placeholder": "Select the Test Name", + "isMulti": false, + "data": { + "source": "dataStream", + "dataStreamName": "getTestListForType", + "dataSourceConfig": { "componentType": { "fieldName": "componentType", "required": true } } + } + }, + { + "name": "measure", + "type": "autocomplete", + "label": "Measure", + "title": "Measure", + "help": "Select the Measure Name", + "validation": { "required": true }, + "placeholder": "Select the measure Name", + "isMulti": false, + "data": { + "source": "dataStream", + "dataStreamName": "getMeasureListForTest", + "dataSourceConfig": { + "componentType": { "fieldName": "componentType", "required": true }, + "test": { "fieldName": "test", "required": true } + } + } + }, + { + "name": "timeline", + "type": "autocomplete", + "label": "Timeline", + "title": "Timeline", + "help": "Select the Timeline", + "validation": { "required": true }, + "placeholder": "Select the Timeline", + "isMulti": false, + "allowCustomValues": false, + "data": { + "source": "fixed", + "values": [ + { "label": "1 hour", "value": "1 hour" }, + { "label": "2 hours", "value": "2 hours" }, + { "label": "4 hours", "value": "4 hours" }, + { "label": "6 hours", "value": "6 hours" }, + { "label": "8 hours", "value": "8 hours" }, + { "label": "10 hours", "value": "10 hours" }, + { "label": "12 hours", "value": "12 hours" }, + { "label": "1 day", "value": "1 day" }, + { "label": "2 days", "value": "2 days" }, + { "label": "4 days", "value": "4 days" }, + { "label": "6 days", "value": "6 days" }, + { "label": "1 week", "value": "1 week" }, + { "label": "2 weeks", "value": "2 weeks" }, + { "label": "4 weeks", "value": "4 weeks" } + ] + } + + } + ] + }, + { + "displayName": "Alarms", + "description": "Current Status", + "dataSourceName": "getAlarmCount", + "definition": { + "timeframes": false, + "name": "Alarms", + "dataSourceConfig": {}, + "rowPath": [], + "matches": "none", + "rowType": { + "name": "Alarms" + } + } + }, + { + "displayName": "Alerts", + "description": "Infrastructure Health", + "dataSourceName": "getAlerts", + "definition": { + "name": "Alerts", + "dataSourceConfig": {}, + "timeframes": false, + "rowPath": [], + "matches": "none", + "rowType": { + "name": "Alerts" + } + } + }, + + { + "displayName": "Components By State", + "description": "Reporting and Non-reporting Components", + "dataSourceName": "getComponentsByState", + + "definition": { + "name": "Components By State", + + "timeframes": false, + "rowPath": [], + "matches": "none", + "dataSourceConfig": {}, + "rowType": { + "name": "ComponentsState" + } + } + } + ] +} diff --git a/plugins/eginnovations/v1/handler.js b/plugins/eginnovations/v1/handler.js new file mode 100644 index 0000000..c29f6ad --- /dev/null +++ b/plugins/eginnovations/v1/handler.js @@ -0,0 +1,146 @@ +import { + testConfig as testConfigImpl, + importStages, + defaultApiLimits, + initialPagingContext, + reportImportProblem, + dataSourceFns +} from './handlerConfig.js'; + +// ============================================================================ +// +// testConfig +// +export async function testConfig(event, api) { + const { pluginConfig } = event; + const { log, report, patchConfig } = api; + + const context = { + pluginConfig, + log, + report, + patchConfig + }; + + return testConfigImpl(context); +} + +// ============================================================================ +// +// importObjects +// +export async function importObjects(event, api) { + const { pluginConfig, pagingContext } = event; + const { log, report, patchConfig } = api; + + const context = { + vertices: [], + edges: [], + + pluginConfig, + pagingContext, + + log, + report, + patchConfig, + + apiLimits: Object.assign({}, defaultApiLimits, pluginConfig.testSettings?.apiLimits ?? {}) + }; + const pageAPI = (context) => { + return { + get: (key) => context.pagingContext[key], + set: (key, value) => { + context.pagingContext[key] = value; + }, + clear: () => { + context.pagingContext = {}; + } + }; + }; + context.pageAPI = pageAPI(context); + context.reportImportProblem = reportImportProblem(context); + + if (!context.pageAPI.get('squaredUp_isInit')) { + // Set initial paging context values + context.pageAPI.set('squaredUp_stage', 0); + for (const [key, value] of Object.entries(initialPagingContext)) { + context.pageAPI.set(key, value); + } + context.pageAPI.set('squaredUp_isInit', true); + } + + // Run through the appropriate stages until we've been running for 10 minutes or we've created results larger than 2MB. + const maxElapsedTimeMSecs = pluginConfig.testSettings?.maxElapsedTimeMSecs ?? 10 * 60 * 1000; + const maxPayloadSize = pluginConfig.testSettings?.maxPayloadSize ?? 2 * 1024 * 1024; + let stage = context.pageAPI.get('squaredUp_stage'); + context.log.debug( + 'importObjects starts: ' + + `stage=${stage}, ` + + `apiLimits=${JSON.stringify(context.apiLimits)}, ` + + `maxElapsedTimeMSecs=${maxElapsedTimeMSecs}, ` + + `maxPayloadSize=${maxPayloadSize}` + ); + const start = Date.now(); + let elapsed; + let payloadSize; + do { + if (await importStages[stage](context)) { + // Stage reported it has finished... step to the next one + stage++; + context.pageAPI.set('squaredUp_stage', stage); + + if (stage >= importStages.length) { + // No more stages, so set pagingContext to an empty object to + // indicate import is complete + context.pageAPI.clear(); + break; + } + } + elapsed = Date.now() - start; + const pagingContextSize = JSON.stringify(context.pagingContext).length; + payloadSize = JSON.stringify({ + vertices: context.vertices, + edges: context.edges, + pagingContext: context.pagingContext + }).length; + context.log.debug( + `importObjects looping: elapsed = ${elapsed}, payloadSize=${payloadSize}, pagingContextSize=${pagingContextSize}` + ); + } while (elapsed < maxElapsedTimeMSecs && payloadSize < maxPayloadSize); + context.log.debug('importObjects loop ends'); + + // Return the results + const result = { + vertices: context.vertices, + edges: context.edges, + pagingContext: context.pagingContext + }; + return result; +} + +// ============================================================================ +// +// readDataSource +// +export async function readDataSource(event, api) { + const { pluginConfig, dataSource, dataSourceConfig, targetNodes, timeframe } = event; + const { log, report, patchConfig } = api; + + const context = { + pluginConfig, + dataSource, + dataSourceConfig, + targetNodes, + timeframe, + log, + report, + patchConfig + }; + + const dataSourceFn = dataSourceFns[dataSource.name]; + if (!dataSourceFn) { + throw new Error(`No data source function was found for data source ${dataSource.name}`); + } + + return dataSourceFn(context); +} diff --git a/plugins/eginnovations/v1/handlerConfig.js b/plugins/eginnovations/v1/handlerConfig.js new file mode 100644 index 0000000..d49b8e8 --- /dev/null +++ b/plugins/eginnovations/v1/handlerConfig.js @@ -0,0 +1,169 @@ +import https from 'https'; +import fetch from 'node-fetch'; +import { stageComponents } from './importObjects/components.js'; +import { getAlarmCount } from './readDataSource/alarmCount.js'; +import { getAlerts } from './readDataSource/alerts.js'; +import { getComponentsByState } from './readDataSource/componentsByState.js'; +import { getComponentsByType } from './readDataSource/getComponentsByType.js'; +import { getHistoricalData } from './readDataSource/getHistoricalData.js'; +import { getLiveMeasure } from './readDataSource/getLiveMeasure.js'; +import { getMeasureForTest } from './readDataSource/getMeasureForTest.js'; +import { getTestForType } from './readDataSource/getTestForType.js'; +import { getUserComponentsForType } from './readDataSource/getUserComponentsForType.js'; + +// ============================================================================ + +/** + * Test configuration for eG Innovations integration. + * Authenticates the provided credentials and prevents continuation on failure. + */ + +export async function testConfig(context) { + const { pluginConfig, log } = context; + const messages = []; + + const result = { + link: 'https://www.eginnovations.com/documentation/eG-Enterprise-User-Guides.htm', + messages + }; + + const newMessage = (message, status = 'error') => { + messages.push({ message, status }); + log.info(`[testConfig] ${status.toUpperCase()}: ${message}`); + }; + + try { + // Step 1: Validate configuration + if (!pluginConfig.serverUrl) { + newMessage('Server URL is required.'); + return result; + } + if (!pluginConfig.user || !pluginConfig.pwd || !pluginConfig.accessID) { + newMessage('Missing required configuration fields.'); + return result; + } + + // Validate URL + let url; + try { + url = new URL(pluginConfig.serverUrl); + } catch { + newMessage('Invalid server URL format.'); + return result; + } + + if (url.protocol !== 'https:') { + newMessage('Server URL must begin with https://'); + return result; + } + + // Step 2: Test Login API + const agent = new https.Agent({ rejectUnauthorized: false }); + const uname = pluginConfig.user; + const upass = Buffer.from(pluginConfig.pwd).toString('base64'); + const accessID = pluginConfig.accessID; + const serverUrl = pluginConfig.serverUrl; + + const loginUrl = `${serverUrl}/final/eGMobileService/getLoginSquaredup?uname=${encodeURIComponent(uname)}&user_from=squaredup&upass=${encodeURIComponent(upass)}&accessID=${encodeURIComponent(accessID)}`; + + // DO NOT log full URL containing credentials + log.info('Testing login API (URL hidden for security)'); + + let response; + try { + response = await fetch(loginUrl, { agent, method: 'GET' }); + } catch (error) { + newMessage('Network error contacting login API. Please check connectivity.'); + log.error(`Network error (details hidden): ${error.message}`); + return result; + } + + const status = response.status; + const contentType = response.headers.get('content-type') || ''; + let data = {}; + + if (contentType.includes('application/json')) { + try { + data = await response.json(); + } catch { + newMessage('Invalid JSON response from eG Enterprise server.'); + return result; + } + } else { + newMessage('Server did not return JSON.'); + return result; + } + + // Log without exposing credentials + log.info('Login API response received (content hidden)'); + + // Step 3: Authentication results + if (status === 200 && data.output?.toLowerCase() === 'success') { + newMessage('Authentication successful.', 'success'); + } else if (status === 400 || data.output?.includes('Invalid AccessID')) { + newMessage('Authentication failed: Invalid AccessID.'); + } else if (status === 401 || data.output?.includes('Invalid username or password')) { + newMessage('Authentication failed: Invalid username or password.'); + } else if (status === 404) { + newMessage('Authentication failed: API endpoint not found (404).'); + } else if (status === 405) { + newMessage('Authentication failed: Method not allowed (405).'); + } else { + newMessage(`Authentication failed with status ${status}.`); + } + + } catch (error) { + log.error(`TestConfig error (hidden details): ${error.message}`); + newMessage('Unexpected internal error occurred.', 'error'); + } + + // DO NOT log raw result (it may contain sensitive info) + log.info('TestConfig completed, result sanitized.'); + + return result; +} + + +// ============================================================================ +// +// importObjects +// +export const importStages = [stageComponents]; + +export const defaultApiLimits = { + apps: 50 +}; + +export const initialPagingContext = { + appIndex: 0, + buildingIndex: 0, + nextToken: undefined +}; + +export function reportImportProblem(context) { + return (err, stage) => { + if (['UnrecognizedClientException', 'InvalidSignatureException'].includes(err.name)) { + context.report.error('The configured access key details are invalid'); + } else if (['AccessDeniedException', 'AccessDenied', 'UnauthorizedOperation'].includes(err.name)) { + context.log.warn(`The configured access key has no permission to import ${stage} objects`); + } else { + context.log.warn(`${stage} objects failed to import: ${err.message}`); + } + }; +} + +// ============================================================================ +// +// readDataSource +// +export const dataSourceFns = { + getAlerts, + getComponentsByState, + getAlarmCount, + getLiveMeasure, + getHistoricalData, + getComponentsByType, + getUserComponentsForType, + getTestForType, + getMeasureForTest +}; diff --git a/plugins/eginnovations/v1/importObjects/components.js b/plugins/eginnovations/v1/importObjects/components.js new file mode 100644 index 0000000..bae2035 --- /dev/null +++ b/plugins/eginnovations/v1/importObjects/components.js @@ -0,0 +1,161 @@ +import fetch from 'node-fetch'; +import https from 'https'; + +export let totalNApps = 50; + +/** + * stageComponents + * Executes repeatedly by SquaredUp until `finished = true`. + * Pagination is handled here using context.pageAPI. + */ +export async function stageComponents(context) { + let finished = false; + + const pageSize = context.apiLimits.apps; // e.g. 50 + let appIndex = context.pageAPI.get('appIndex') || 0; + + context.log.debug(`Fetching page: offset=${appIndex}, limit=${pageSize}`); + + try { + // Load ALL objects from API (single call) + const response = await GetAppObjectsFromExternalApi(context); + + if (!response || !response.data || !response.data.apps) { + context.log.info('No valid data returned from API.'); + return true; + } + + const allApps = response.data.apps; + const totalLength = response.paging.totalLength; + + context.log.debug(`Total Apps from API: ${totalLength}`); + + // ----------------------------- + // Apply pagination (no duplicates!) + // ----------------------------- + const page = allApps.slice(appIndex, appIndex + pageSize); + + context.log.debug(`Returning ${page.length} paged apps`); + + // Add only this page's objects + for (const app of page) { + addVertexForApp(context, app); + } + + // Move the index forward + appIndex += page.length; + + if (appIndex < totalLength && page.length > 0) { + // Continue to next page + context.pageAPI.set('appIndex', appIndex); + context.log.debug(`Next page will start at appIndex=${appIndex}`); + finished = false; + } else { + // Finished all pages + context.log.debug('All pages imported. Import complete.'); + finished = true; + + // Reset for next full import cycle + context.pageAPI.set('appIndex', 0); + } + } catch (err) { + context.log.error(`Error in stageComponents: ${err.message}`); + context.reportImportProblem(err, 'Apps'); + finished = true; + } + + return finished; +} + +/** + * Creates a vertex for SquaredUp graph. + */ + function addVertexForApp(context, appObject) { + const vertex = { + sourceName: 'eG Enterprise', + name: appObject.appName, + type: 'app', + sourceType: 'eGComponents', + sourceId: appObject.appNum, + appType: appObject.appType + }; + + context.vertices.push(vertex); + return vertex; +} + +/** + * Calls API ONCE and flattens ALL objects. + * Pagination is *not* done here anymore. + */ +async function GetAppObjectsFromExternalApi(context) { + const url = `${context.pluginConfig.serverUrl}/api/eg/analytics/getComponentsDetails`; + + const agent = new https.Agent({ rejectUnauthorized: false }); + + const body = { from: 'squaredup' }; + + const headers = { + user: context.pluginConfig.user, + pwd: Buffer.from(context.pluginConfig.pwd).toString('base64'), + managerurl: context.pluginConfig.serverUrl, + accessID: context.pluginConfig.accessID + }; + + try { + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(body), + headers, + agent + }); + + if (!response.ok) { + throw new Error(`HTTP error! status ${response.status}`); + } + + const data = await response.json(); + + if (!data || !Array.isArray(data.details)) { + context.log.warn("API returned no components."); + return { + paging: { totalLength: 0 }, + data: { apps: [] } + }; + } + + const apps = []; + let total = 0; + + // Flatten full response (we paginate later) + for (const detail of data.details) { + for (const category of Object.keys(detail)) { + const arr = detail[category]; + if (!Array.isArray(arr)) continue; + + for (const comp of arr) { + if (!comp.componentID || !comp.componentName) continue; + + apps.push({ + appType: category, + appNum: comp.componentID, + appName: comp.componentName + }); + + total++; + } + } + } + + totalNApps = total; + + return { + paging: { totalLength: totalNApps }, + data: { apps } + }; + + } catch (err) { + context.log.error("GetAppObjectsFromExternalApi error: " + err.message); + throw err; + } +} diff --git a/plugins/eginnovations/v1/metadata.json b/plugins/eginnovations/v1/metadata.json new file mode 100644 index 0000000..b92abe3 --- /dev/null +++ b/plugins/eginnovations/v1/metadata.json @@ -0,0 +1,77 @@ +{ + "name": "eGInnovations", + "displayName": "eG Enterprise", + "version": "1.1.5", + "author": "eG Innovations Pvt Ltd", + "description": "Monitor key application metrics from your eG environment", + "category": "APM", + "type": "onprem", + "restrictedToPlatforms": [], + "supportsConfigValidation": true, + "links": [ + { + "url": "https://docs.squaredup.com/data-sources/eGinnovations-plugin", + "label": "eGinnovations Plugin Documentation" + } + ], + "keywords": ["eginnovations", "monitor", "APM"], + "objectTypes": ["Account", "Alerts information", "Performance metics", "Historical data"], + "actions": { + "__testConfig": { + "connector": "nodejs", + "version": "2.0.0", + "config": { "scriptPath": "handler.js", "entryPoint": "testConfig" } + }, + "import": { + "connector": "nodejs", + "version": "2.0.0", + "config": { "scriptPath": "handler.js", "entryPoint": "importObjects" }, + "isImportAction": true + }, + "getAlerts": { + "connector": "nodejs", + "version": "2.0.0", + "config": { "scriptPath": "handler.js", "entryPoint": "readDataSource" } + }, + "getComponentsByState": { + "connector": "nodejs", + "version": "2.0.0", + "config": { "scriptPath": "handler.js", "entryPoint": "readDataSource" } + }, + "getAlarmCount": { + "connector": "nodejs", + "version": "2.0.0", + "config": { "scriptPath": "handler.js", "entryPoint": "readDataSource" } + }, + "getLiveMeasure": { + "connector": "nodejs", + "version": "2.0.0", + "config": { "scriptPath": "handler.js", "entryPoint": "readDataSource" } + }, + "getHistoricalData": { + "connector": "nodejs", + "version": "2.0.0", + "config": { "scriptPath": "handler.js", "entryPoint": "readDataSource" } + }, + "getComponentsByType": { + "connector": "nodejs", + "version": "2.0.0", + "config": { "scriptPath": "handler.js", "entryPoint": "readDataSource" } + }, + "getUserComponentsForType": { + "connector": "nodejs", + "version": "2.0.0", + "config": { "scriptPath": "handler.js", "entryPoint": "readDataSource" } + }, + "getTestForType": { + "connector": "nodejs", + "version": "2.0.0", + "config": { "scriptPath": "handler.js", "entryPoint": "readDataSource" } + }, + "getMeasureForTest": { + "connector": "nodejs", + "version": "2.0.0", + "config": { "scriptPath": "handler.js", "entryPoint": "readDataSource" } + } + } +} diff --git a/plugins/eginnovations/v1/package.json b/plugins/eginnovations/v1/package.json new file mode 100644 index 0000000..21dd3be --- /dev/null +++ b/plugins/eginnovations/v1/package.json @@ -0,0 +1,17 @@ +{ + "name": "eginnovations-v1", + "version": "1.0.0", + "description": "Squaredup plugin for eGinnovations", + "type": "module", + "main": "handler.js", + "author": "eG innovations Pvt Ltd", + "license": "ISC", + "dependencies": { + "lodash": "^4.17.21", + "node-fetch": "^3.3.2" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "unitTest": "pnpm -w run test --ci --path=plugins/eginnovations/v1" + } +} diff --git a/plugins/eginnovations/v1/readDataSource/alarmCount.js b/plugins/eginnovations/v1/readDataSource/alarmCount.js new file mode 100644 index 0000000..63c0c4d --- /dev/null +++ b/plugins/eginnovations/v1/readDataSource/alarmCount.js @@ -0,0 +1,59 @@ +import fetch from 'node-fetch'; +import https from 'https'; + +export async function getAlarmCount(context) { + const serverUrl = context.pluginConfig.serverUrl; + const url = `${serverUrl}/api/eg/analytics/getAlarmCount`; + context.log.info(url); + + const agent = new https.Agent({ + rejectUnauthorized: false + }); + + const body = { + from: 'squaredup' + }; + + + const headers = { + 'Content-Type': 'application/json', + user: context.pluginConfig.user, + pwd: Buffer.from(context.pluginConfig.pwd).toString('base64'), + managerurl: `${serverUrl}`, + accessID: context.pluginConfig.accessID + }; + + try { + // Await the fetch request + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(body), + headers: headers, + agent: agent + }); + + // Check if the response is OK + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // Check if response is JSON + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + throw new Error('Response is not JSON'); + } + + let data = await response.json(); + + const result=[]; + result.push({healthState:'major',count:data.major}); + result.push({healthState:'minor',count:data.minor}); + result.push({healthState:'critical',count:data.critical}); + return result; + + } catch (error) { + // Catch and log any errors + context.log.error(`Error in getAlarmCount: ${error.message}`); + throw new Error(`HTTP error! status: ${error.message}`); + } +} diff --git a/plugins/eginnovations/v1/readDataSource/alerts.js b/plugins/eginnovations/v1/readDataSource/alerts.js new file mode 100644 index 0000000..c1b81ec --- /dev/null +++ b/plugins/eginnovations/v1/readDataSource/alerts.js @@ -0,0 +1,58 @@ +// import _ from 'lodash'; +import fetch from 'node-fetch'; +import https from 'https'; + +export async function getAlerts(context) { + const serverUrl = context.pluginConfig.serverUrl; + const url = `${serverUrl}/api/eg/analytics/getAlerts`; + context.log.info(url); + + const agent = new https.Agent({ + rejectUnauthorized: false + }); + + // Define the body of the request + const body = { + 'filterBy': 'ComponentType', + 'filterValues': 'All', + 'from': 'squaredup' + }; + + const headers = { + 'Content-Type': 'application/json', + user: context.pluginConfig.user, + pwd: Buffer.from(context.pluginConfig.pwd).toString('base64'), + managerurl: `${serverUrl}`, + accessID: context.pluginConfig.accessID + }; + + try { + // Await the fetch request + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(body), + headers: headers, + agent: agent + }); + + // Check if the response is OK + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // Check if response is JSON + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + throw new Error('Response is not JSON'); + } + + let data = await response.json(); + + return data.data; + + } catch (error) { + // Catch and log any errors + context.log.error(`Error in getAlerts: ${error.message}`); + throw new Error(`HTTP error! status: ${error.message}`); + } +} diff --git a/plugins/eginnovations/v1/readDataSource/componentsByState.js b/plugins/eginnovations/v1/readDataSource/componentsByState.js new file mode 100644 index 0000000..a2dd637 --- /dev/null +++ b/plugins/eginnovations/v1/readDataSource/componentsByState.js @@ -0,0 +1,53 @@ +// import _ from 'lodash'; +import https from 'https'; +import fetch from 'node-fetch'; + +export async function getComponentsByState(context) { + const serverUrl = context.pluginConfig.serverUrl; + const url = `${serverUrl}/api/eg/analytics/getComponentsByState`; + + const agent = new https.Agent({ + rejectUnauthorized: false + }); + + const body = { + from: 'squaredup' + }; + + const headers = { + 'Content-Type': 'application/json', + user: context.pluginConfig.user, + pwd: Buffer.from(context.pluginConfig.pwd).toString('base64'), + managerurl: serverUrl, + accessID: context.pluginConfig.accessID + }; + + try { + // Await the fetch request + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(body), + headers: headers, + agent: agent + }); + + // Check if the response is OK + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // Check if response is JSON + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + throw new Error('Response is not JSON'); + } + + let data = await response.json(); + + return data.totalComponents; + } catch (error) { + // Catch and log any errors + context.log.error(`Error in getComponentsByState: ${error.message}`); + throw new Error(`HTTP error! status: ${error.message}`); + } +} diff --git a/plugins/eginnovations/v1/readDataSource/getComponentsByType.js b/plugins/eginnovations/v1/readDataSource/getComponentsByType.js new file mode 100644 index 0000000..ac9206f --- /dev/null +++ b/plugins/eginnovations/v1/readDataSource/getComponentsByType.js @@ -0,0 +1,57 @@ +// import _ from 'lodash'; +import https from 'https'; +import fetch from 'node-fetch'; + +export async function getComponentsByType(context) { + const serverUrl = context.pluginConfig.serverUrl; + const url = `${serverUrl}/api/eg/analytics/getComponentsByState`; + const agent = new https.Agent({ + rejectUnauthorized: false + }); + + const body = { + from: 'squaredup' + }; + + const headers = { + 'Content-Type': 'application/json', + user: context.pluginConfig.user, + pwd: Buffer.from(context.pluginConfig.pwd).toString('base64'), + managerurl: serverUrl, + accessID: context.pluginConfig.accessID + }; + + try { + // Await the fetch request + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(body), + headers: headers, + agent: agent + }); + + // Check if the response is OK + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // Check if response is JSON + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + throw new Error('Response is not JSON'); + } + + let data = await response.json(); + + // Extract only the componentType values in a new array of objects + const componentTypes = data.totalComponents.map(component => ({ + componentType: component.componentType + })); + return componentTypes; + + } catch (error) { + // Catch and log any errors + context.log.error(`Error in getComponentsByType: ${error.message}`); + throw new Error(`HTTP error! status: ${error.message}`); + } +} diff --git a/plugins/eginnovations/v1/readDataSource/getHistoricalData.js b/plugins/eginnovations/v1/readDataSource/getHistoricalData.js new file mode 100644 index 0000000..5a25620 --- /dev/null +++ b/plugins/eginnovations/v1/readDataSource/getHistoricalData.js @@ -0,0 +1,63 @@ +// import _ from 'lodash'; +import https from 'https'; +import fetch from 'node-fetch'; + +export async function getHistoricalData(context) { + const serverUrl = context.pluginConfig.serverUrl; + const url = `${serverUrl}/api/eg/analytics/getHistoricalData`; + context.log.info(url); + context.log.info(JSON.stringify(context.dataSourceConfig)); + const agent = new https.Agent({ + rejectUnauthorized: false + }); + + + + // Define the body of the request + const body = { + timeline: context.dataSourceConfig.timeline,//'1 hour', + componentName:context.dataSourceConfig.componentName, //'172.16.8.112:7077', + componentType: context.dataSourceConfig.componentType, //'eG Manager', + test: context.dataSourceConfig.test, //'Network', + measure: context.dataSourceConfig.measure,//'Packet Loss', + from: 'squaredup' + }; + + const headers = { + 'Content-Type': 'application/json', + user: context.pluginConfig.user, + pwd: Buffer.from(context.pluginConfig.pwd).toString('base64'), + managerurl: `${serverUrl}`, + accessID: context.pluginConfig.accessID + }; + + try { + // Await the fetch request + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(body), + headers: headers, + agent: agent + }); + + // Check if the response is OK + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // Check if response is JSON + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + throw new Error('Response is not JSON'); + } + + let data = await response.json(); + const dynamicKey = Object.keys(data)[0]; + context.log.info(dynamicKey); + return data[dynamicKey]; + } catch (error) { + // Catch and log any errors + context.log.error(`Error in getHistoricalData: ${error.message}`); + throw new Error(`HTTP error! status: ${error.message}`); + } +} diff --git a/plugins/eginnovations/v1/readDataSource/getLiveMeasure.js b/plugins/eginnovations/v1/readDataSource/getLiveMeasure.js new file mode 100644 index 0000000..4e4d14a --- /dev/null +++ b/plugins/eginnovations/v1/readDataSource/getLiveMeasure.js @@ -0,0 +1,59 @@ +// import _ from 'lodash'; +import https from 'https'; +import fetch from 'node-fetch'; + +export async function getLiveMeasure(context) { + const serverUrl = context.pluginConfig.serverUrl; + const url = `${serverUrl}/api/eg/analytics/getLiveMeasure`; + context.log.info(url); + + const agent = new https.Agent({ + rejectUnauthorized: false + }); + + // Define the body of the request + const body = { + componentName: context.dataSourceConfig.componentName, //'172.16.8.112:7077', + componentType: context.dataSourceConfig.componentType, + test: context.dataSourceConfig.test, + measure: context.dataSourceConfig.measure, //'eG Manager' + from: 'squaredup' + }; + context.log.info(JSON.stringify(context.dataSourceConfig)); + const headers = { + 'Content-Type': 'application/json', + user: context.pluginConfig.user, + pwd: Buffer.from(context.pluginConfig.pwd).toString('base64'), + managerurl: `${serverUrl}`, + accessID: context.pluginConfig.accessID + }; + + try { + // Await the fetch request + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(body), + headers: headers, + agent: agent + }); + + // Check if the response is OK + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // Check if response is JSON + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + throw new Error('Response is not JSON'); + } + + let data = await response.json(); + + return data; + } catch (error) { + // Catch and log any errors + context.log.error(`Error in getLiveMeasure: ${error.message}`); + throw new Error(`HTTP error! status: ${error.message}`); + } +} diff --git a/plugins/eginnovations/v1/readDataSource/getMeasureForTest.js b/plugins/eginnovations/v1/readDataSource/getMeasureForTest.js new file mode 100644 index 0000000..0690704 --- /dev/null +++ b/plugins/eginnovations/v1/readDataSource/getMeasureForTest.js @@ -0,0 +1,55 @@ +// import _ from 'lodash'; +import https from 'https'; +import fetch from 'node-fetch'; + +export async function getMeasureForTest(context) { + const serverUrl = context.pluginConfig.serverUrl; + const url = `${serverUrl}/api/eg/analytics/getMeasureForTest`; + + const agent = new https.Agent({ + rejectUnauthorized: false + }); + + const body = { + componentType: context.dataSourceConfig.componentType, + test:context.dataSourceConfig.test, + from: 'squaredup' + }; + + const headers = { + 'Content-Type': 'application/json', + user: context.pluginConfig.user, + pwd: Buffer.from(context.pluginConfig.pwd).toString('base64'), + managerurl: serverUrl, + accessID: context.pluginConfig.accessID + }; + + try { + // Await the fetch request + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(body), + headers: headers, + agent: agent + }); + + // Check if the response is OK + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // Check if response is JSON + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + throw new Error('Response is not JSON'); + } + + let data = await response.json(); + + return data; + } catch (error) { + // Catch and log any errors + context.log.error(`Error in getMeasureForTest: ${error.message}`); + throw new Error(`HTTP error! status: ${error.message}`); + } +} diff --git a/plugins/eginnovations/v1/readDataSource/getTestForType.js b/plugins/eginnovations/v1/readDataSource/getTestForType.js new file mode 100644 index 0000000..bd0c77e --- /dev/null +++ b/plugins/eginnovations/v1/readDataSource/getTestForType.js @@ -0,0 +1,54 @@ +// import _ from 'lodash'; +import https from 'https'; +import fetch from 'node-fetch'; + +export async function getTestForType(context) { + const serverUrl = context.pluginConfig.serverUrl; + const url = `${serverUrl}/api/eg/analytics/getTestForType`; + + const agent = new https.Agent({ + rejectUnauthorized: false + }); + + const body = { + componentType: context.dataSourceConfig.componentType, + from: 'squaredup' + }; + + const headers = { + 'Content-Type': 'application/json', + user: context.pluginConfig.user, + pwd: Buffer.from(context.pluginConfig.pwd).toString('base64'), + managerurl: serverUrl, + accessID: context.pluginConfig.accessID + }; + + try { + // Await the fetch request + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(body), + headers: headers, + agent: agent + }); + + // Check if the response is OK + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // Check if response is JSON + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + throw new Error('Response is not JSON'); + } + + let data = await response.json(); + + return data; + } catch (error) { + // Catch and log any errors + context.log.error(`Error in getTestForType: ${error.message}`); + throw new Error(`HTTP error! status: ${error.message}`); + } +} diff --git a/plugins/eginnovations/v1/readDataSource/getUserComponentsForType.js b/plugins/eginnovations/v1/readDataSource/getUserComponentsForType.js new file mode 100644 index 0000000..364d8a9 --- /dev/null +++ b/plugins/eginnovations/v1/readDataSource/getUserComponentsForType.js @@ -0,0 +1,54 @@ +// import _ from 'lodash'; +import https from 'https'; +import fetch from 'node-fetch'; + +export async function getUserComponentsForType(context) { + const serverUrl = context.pluginConfig.serverUrl; + const url = `${serverUrl}/api/eg/analytics/getUserComponentsForType`; + + const agent = new https.Agent({ + rejectUnauthorized: false + }); + + const body = { + 'componentType': context.dataSourceConfig.componentType, //"eG Manager", + 'from': 'squaredup' + }; + context.log.info(JSON.stringify(context.dataSourceConfig.componentType)); + const headers = { + 'Content-Type': 'application/json', + user: context.pluginConfig.user, + pwd: Buffer.from(context.pluginConfig.pwd).toString('base64'), + managerurl: serverUrl, + accessID: context.pluginConfig.accessID + }; + + try { + // Await the fetch request + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(body), + headers: headers, + agent: agent + }); + + // Check if the response is OK + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + // Check if response is JSON + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + throw new Error('Response is not JSON'); + } + + let data = await response.json(); + + return data; + } catch (error) { + // Catch and log any errors + context.log.error(`Error in getUserComponentsForType: ${error.message}`); + throw new Error(`HTTP error! status: ${error.message}`); + } +} diff --git a/plugins/eginnovations/v1/ui.json b/plugins/eginnovations/v1/ui.json new file mode 100644 index 0000000..61e9e85 --- /dev/null +++ b/plugins/eginnovations/v1/ui.json @@ -0,0 +1,50 @@ +[ + { + "type": "text", + "name": "serverUrl", + "label": "EG Server URL", + "title": "EG Server URL", + "help": "The URL of your EG instance (e.g., https://your-instance.eginnovations.com)", + "validation": { + "required": true, + "pattern": { + "value": "^https://[^\\s]+$", + "message": "Please enter a valid URL starting with https://" + } + }, + "placeholder": "https://your-instance.eginnovations.com" + }, + { + "type": "text", + "name": "user", + "label": "Account UserName", + "title": "Account UserName", + "help": "The username for the account", + "validation": { + "required": true + }, + "placeholder": "Username" + }, + { + "type": "password", + "name": "pwd", + "label": "Account Password", + "title": "Account Password", + "help": "Provide your password", + "validation": { + "required": true + }, + "placeholder": "Password" + }, + { + "type": "password", + "name": "accessID", + "label": "Access ID", + "title": "Access ID", + "help": "The Access ID to use", + "validation": { + "required": true + }, + "placeholder": "Access ID" + } +] \ No newline at end of file diff --git a/plugins/eginnovations/v1/util.js b/plugins/eginnovations/v1/util.js new file mode 100644 index 0000000..50a1a82 --- /dev/null +++ b/plugins/eginnovations/v1/util.js @@ -0,0 +1,6 @@ +export function getScalar(obj, propertyName) { + if (Array.isArray(obj[propertyName])) { + return obj[propertyName][0]; + } + return obj[propertyName]; +} \ No newline at end of file diff --git a/plugins/eginnovations/v1/util/eGClient.js b/plugins/eginnovations/v1/util/eGClient.js new file mode 100644 index 0000000..1998288 --- /dev/null +++ b/plugins/eginnovations/v1/util/eGClient.js @@ -0,0 +1,47 @@ +import fetch from 'node-fetch'; +import https from 'https'; + +export async function eGClientRequest(context, endpoint) { + + const serverUrl = context.pluginConfig.serverUrl; + + const url = `${serverUrl}/api/eg/analytics/${endpoint}`; + context.log.info(url); + + const agent = new https.Agent({ + rejectUnauthorized: false + }); + + const headers = { + + user: context.pluginConfig.user, + pwd: Buffer.from(context.pluginConfig.pwd).toString('base64'), + managerurl: `${serverUrl}`, + accessID: context.pluginConfig.accessID + }; + try { + // Await the fetch request + const response = await fetch(url, { + method: 'POST', + headers: headers, + agent: agent + }); + + // Check if the response is OK + if (!response.ok) { + // throw new Error(`HTTP error! status: ${response.status}`); + } + + + let data = await response.json(); + context.log.info(JSON.stringify(data)); + return data; + + + } + catch (error) { + // Catch and log any errors + throw new Error('HTTP error! status:' + error); + } + +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 31a74ac..36acbc4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,6 +128,15 @@ importers: specifier: ^1.1.3 version: 1.1.3 + plugins/eginnovations/v1: + dependencies: + lodash: + specifier: ^4.17.21 + version: 4.17.21 + node-fetch: + specifier: ^3.3.2 + version: 3.3.2 + scripts/datastream-helper: dependencies: '@squaredup/timeframes': @@ -1743,6 +1752,10 @@ packages: resolution: {integrity: sha512-iux2F+85jDlBEJZgikfPT5SUZMwuFjNqEJiO1SO+xfQG+2MFV9CaHTsoRJIGNy3udMm1mw0GMY5UIVAodwlnhg==} engines: {node: '>=12.13.0'} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + date-fns@2.30.0: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} @@ -2032,6 +2045,10 @@ packages: picomatch: optional: true + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} @@ -2070,6 +2087,10 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -2735,6 +2756,15 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -3438,6 +3468,10 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -5193,6 +5227,8 @@ snapshots: fs-extra: 10.1.0 gensequence: 3.1.1 + data-uri-to-buffer@4.0.1: {} + date-fns@2.30.0: dependencies: '@babel/runtime': 7.28.3 @@ -5524,6 +5560,11 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + fflate@0.8.2: {} figures@3.2.0: @@ -5572,6 +5613,10 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + forwarded@0.2.0: {} fresh@2.0.0: {} @@ -6409,6 +6454,14 @@ snapshots: negotiator@1.0.0: {} + node-domexception@1.0.0: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-int64@0.4.0: {} node-releases@2.0.19: {} @@ -7113,6 +7166,8 @@ snapshots: dependencies: defaults: 1.0.4 + web-streams-polyfill@3.3.3: {} + which@2.0.2: dependencies: isexe: 2.0.0