From d2e38dc615c5d82c025298636988937f04cb922b Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sat, 29 May 2021 20:42:25 -0400 Subject: [PATCH] :sparkles: Add UptimeRobot Node (#1830) * Add Monitor & account resources * Add alert contact resource * Add mwindows resource * Add 'public status page' resource * Clean up & lint fixes * Minor fixes * Apply code review suggestions * Minor fixes * Fix options name casing * :zap: Improvements * :zap: Improvements * :zap: Improvements on UptimeRobot Node * :zap: Activate continueOnFail support Co-authored-by: dali Co-authored-by: Jan Oberhauser --- .../credentials/UptimeRobotApi.credentials.ts | 19 + .../UptimeRobot/AlertContactDescription.ts | 300 ++++++++++ .../nodes/UptimeRobot/GenericFunctions.ts | 41 ++ .../MaintenanceWindowDescription.ts | 468 ++++++++++++++++ .../nodes/UptimeRobot/MonitorDescription.ts | 512 ++++++++++++++++++ .../PublicStatusPageDescription.ts | 338 ++++++++++++ .../nodes/UptimeRobot/UptimeRobot.node.json | 20 + .../nodes/UptimeRobot/UptimeRobot.node.ts | 447 +++++++++++++++ .../nodes/UptimeRobot/uptimerobot.svg | 6 + packages/nodes-base/package.json | 2 + 10 files changed, 2153 insertions(+) create mode 100644 packages/nodes-base/credentials/UptimeRobotApi.credentials.ts create mode 100644 packages/nodes-base/nodes/UptimeRobot/AlertContactDescription.ts create mode 100644 packages/nodes-base/nodes/UptimeRobot/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/UptimeRobot/MaintenanceWindowDescription.ts create mode 100644 packages/nodes-base/nodes/UptimeRobot/MonitorDescription.ts create mode 100644 packages/nodes-base/nodes/UptimeRobot/PublicStatusPageDescription.ts create mode 100644 packages/nodes-base/nodes/UptimeRobot/UptimeRobot.node.json create mode 100644 packages/nodes-base/nodes/UptimeRobot/UptimeRobot.node.ts create mode 100644 packages/nodes-base/nodes/UptimeRobot/uptimerobot.svg diff --git a/packages/nodes-base/credentials/UptimeRobotApi.credentials.ts b/packages/nodes-base/credentials/UptimeRobotApi.credentials.ts new file mode 100644 index 000000000..db928f3fb --- /dev/null +++ b/packages/nodes-base/credentials/UptimeRobotApi.credentials.ts @@ -0,0 +1,19 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + + +export class UptimeRobotApi implements ICredentialType { + name = 'uptimeRobotApi'; + displayName = 'Uptime Robot API'; + documentationUrl = 'uptimeRobot'; + properties = [ + { + displayName: 'API Key', + name: 'apiKey', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/UptimeRobot/AlertContactDescription.ts b/packages/nodes-base/nodes/UptimeRobot/AlertContactDescription.ts new file mode 100644 index 000000000..430dc2b1b --- /dev/null +++ b/packages/nodes-base/nodes/UptimeRobot/AlertContactDescription.ts @@ -0,0 +1,300 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const alertContactOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'alertContact', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an alert contact', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an alert contact', + }, + { + name: 'Get', + value: 'get', + description: 'Get an alert contact', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all alert contacts', + }, + { + name: 'Update', + value: 'update', + description: 'Update an alert contact', + }, + ], + default: 'getAll', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const alertContactFields = [ + /* -------------------------------------------------------------------------- */ + /* alertContact:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Friendly Name', + name: 'friendlyName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'alertContact', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The friendly name of the alert contact.', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + required: true, + default: '', + options: [ + { + name: 'Boxcar', + value: 4, + }, + { + name: 'E-mail', + value: 2, + }, + { + name: 'SMS', + value: 1, + }, + { + name: 'Twitter DM', + value: 3, + }, + { + name: 'Pushbullet', + value: 6, + }, + { + name: 'Pushover', + value: 9, + }, + { + name: 'Webhook', + value: 5, + }, + // the commented option are not supported yet + // { + // name:'HipChat', + // value:10, + // }, + // { + // name:'Slack', + // value:11 + // }, + // { + // name:'Zapier', + // value:7, + // }, + + ], + displayOptions: { + show: { + resource: [ + 'alertContact', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The type of the alert contact.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'alertContact', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The correspondent value for the alert contact type.', + }, + + /* -------------------------------------------------------------------------- */ + /* alertContact:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'ID', + name: 'id', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'alertContact', + ], + operation: [ + 'delete', + 'get', + ], + }, + }, + description: 'The ID of the alert contact.', + }, + + /* -------------------------------------------------------------------------- */ + /* alertContact:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'alertContact', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'alertContact', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'alertContact', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Alert Contact IDs', + name: 'alert_contacts', + type: 'string', + default: '', + description: 'Alert contact ids separated with dash, e.g. 236-1782-4790.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* alertContact:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'ID', + name: 'id', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'alertContact', + ], + operation: [ + 'update', + ], + }, + }, + description: 'The ID of the alert contact.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'alertContact', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Friendly Name', + name: 'friendly_name', + type: 'string', + default: '', + description: 'The friendly name of the alert contact.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'The correspondent value for the alert contact type (can only be used if it is a Webhook alert contact).', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/UptimeRobot/GenericFunctions.ts b/packages/nodes-base/nodes/UptimeRobot/GenericFunctions.ts new file mode 100644 index 000000000..39d181224 --- /dev/null +++ b/packages/nodes-base/nodes/UptimeRobot/GenericFunctions.ts @@ -0,0 +1,41 @@ +import { + OptionsWithUri +} from 'request'; + +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + NodeApiError, + NodeOperationError, +} from 'n8n-workflow'; + +export async function uptimeRobotApiRequest(this: IExecuteFunctions, method: string, resource: string, body: IDataObject = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}) { + const credentials = this.getCredentials('uptimeRobotApi'); + if (credentials === undefined) { + throw new NodeOperationError(this.getNode(), 'No credentials got returned!'); + } + + let options: OptionsWithUri = { + method, + qs, + form: { + api_key: credentials.apiKey, + ...body, + }, + uri: uri || `https://api.uptimerobot.com/v2${resource}`, + json: true, + }; + options = Object.assign({}, options, option); + try { + const responseData = await this.helpers.request(options); + if (responseData.stat !== 'ok') { + throw new NodeOperationError(this.getNode(), responseData); + } + return responseData; + } catch (error) { + throw new NodeApiError(this.getNode(), error); + } +} diff --git a/packages/nodes-base/nodes/UptimeRobot/MaintenanceWindowDescription.ts b/packages/nodes-base/nodes/UptimeRobot/MaintenanceWindowDescription.ts new file mode 100644 index 000000000..e99f47406 --- /dev/null +++ b/packages/nodes-base/nodes/UptimeRobot/MaintenanceWindowDescription.ts @@ -0,0 +1,468 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const maintenanceWindowOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a maintenance window', + + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a maintenance window', + }, + { + name: 'Get', + value: 'get', + description: 'Get a maintenance window', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all a maintenance windows', + }, + { + name: 'Update', + value: 'update', + description: 'Update a maintenance window', + }, + ], + default: 'getAll', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const maintenanceWindowFields = [ + /* -------------------------------------------------------------------------- */ + /* maintenanceWindow:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Duration (minutes)', + name: 'duration', + type: 'number', + required: true, + default: 1, + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The maintenance window activation period (minutes).', + }, + { + displayName: 'Friendly Name', + name: 'friendlyName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The friendly name of the maintenance window.', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + required: true, + default: '', + options: [ + { + name: 'Once', + value: 1, + }, + { + name: 'Daily', + value: 2, + }, + { + name: 'Weekly', + value: 3, + }, + { + name: 'Monthly', + value: 4, + }, + ], + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The type of the maintenance window.', + }, + { + displayName: 'Week Day', + name: 'weekDay', + type: 'options', + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'create', + ], + type: [ + 3, + ], + }, + }, + options: [ + { + name: 'Monday', + value: 1, + }, + { + name: 'Tuesday', + value: 2, + }, + { + name: 'Wednesday', + value: 3, + }, + { + name: 'Thursday', + value: 4, + }, + { + name: 'Friday', + value: 5, + }, + { + name: 'Saturday', + value: 6, + }, + { + name: 'Sunday', + value: 7, + }, + ], + default: '', + }, + { + displayName: 'Month Day', + name: 'monthDay', + type: 'number', + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'create', + ], + type: [ + 4, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 30, + }, + default: 1, + }, + { + displayName: 'Start Time', + name: 'start_time', + type: 'dateTime', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The maintenance window start datetime.', + }, + + /* -------------------------------------------------------------------------- */ + /* maintenanceWindow:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'ID', + name: 'id', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'delete', + 'get', + ], + }, + }, + description: 'The ID of the maintenance window.', + }, + + /* -------------------------------------------------------------------------- */ + /* maintenanceWindow:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'Whether all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Maintenance Window IDs', + name: 'mwindow', + type: 'string', + default: '', + description: 'Maintenance windows ids separated with dash, e.g. 236-1782-4790.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* maintenanceWindow:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'ID', + name: 'id', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'update', + ], + }, + }, + description: 'The ID of the maintenance window.', + }, + { + displayName: 'Duration (minutes)', + name: 'duration', + type: 'number', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'update', + ], + }, + }, + description: 'The maintenance window activation period (minutes).', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'maintenanceWindow', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Friendly Name', + name: 'friendly_name', + type: 'string', + default: '', + description: 'The friendly name of the maintenance window.', + }, + { + displayName: 'Start Time', + name: 'start_time', + type: 'dateTime', + default: '', + description: 'The maintenance window start datetime.', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + default: '', + options: [ + { + name: 'Once', + value: 1, + }, + { + name: 'Daily', + value: 2, + }, + { + name: 'Weekly', + value: 3, + }, + { + name: 'Monthly', + value: 4, + }, + ], + description: 'The type of the maintenance window.', + }, + { + displayName: 'Week Day', + name: 'weekDay', + type: 'options', + displayOptions: { + show: { + type: [ + 3, + ], + }, + }, + options: [ + { + name: 'Monday', + value: 1, + }, + { + name: 'Tuesday', + value: 2, + }, + { + name: 'Wednesday', + value: 3, + }, + { + name: 'Thursday', + value: 4, + }, + { + name: 'Friday', + value: 5, + }, + { + name: 'Saturday', + value: 6, + }, + { + name: 'Sunday', + value: 7, + }, + ], + default: '', + }, + { + displayName: 'Month Day', + name: 'monthDay', + type: 'number', + displayOptions: { + show: { + type: [ + 4, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 30, + }, + default: 1, + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/UptimeRobot/MonitorDescription.ts b/packages/nodes-base/nodes/UptimeRobot/MonitorDescription.ts new file mode 100644 index 000000000..fd71e7494 --- /dev/null +++ b/packages/nodes-base/nodes/UptimeRobot/MonitorDescription.ts @@ -0,0 +1,512 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const monitorOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'monitor', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a monitor', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a monitor', + }, + { + name: 'Get', + value: 'get', + description: 'Get a monitor', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all monitors', + }, + { + name: 'Reset', + value: 'reset', + description: 'Reset a monitor', + }, + { + name: 'Update', + value: 'update', + description: 'Update a monitor', + }, + ], + default: 'getAll', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const monitorFields = [ + /* -------------------------------------------------------------------------- */ + /* monitor:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Friendly Name', + name: 'friendlyName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'monitor', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The friendly name of the monitor.', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + required: true, + default: '', + options: [ + { + name: 'Heartbeat', + value: 5, + }, + { + name: 'HTTP(S)', + value: 1, + }, + { + name: 'Keyword', + value: 2, + }, + { + name: 'Ping', + value: 3, + }, + { + name: 'Port', + value: 4, + }, + ], + displayOptions: { + show: { + resource: [ + 'monitor', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The type of the monitor.', + }, + { + displayName: 'URL', + name: 'url', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'monitor', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The URL/IP of the monitor.', + }, + + /* -------------------------------------------------------------------------- */ + /* monitor:delete/reset */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'ID', + name: 'id', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'monitor', + ], + operation: [ + 'delete', + 'reset', + 'get', + ], + }, + }, + description: 'The ID of the monitor.', + }, + + /* -------------------------------------------------------------------------- */ + /* monitor:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'monitor', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'monitor', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'monitor', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Alert Contacts', + name: 'alert_contacts', + type: 'boolean', + default: false, + description: 'Whether the alert contacts set for the monitor to be returned.', + }, + { + displayName: 'Logs', + name: 'logs', + type: 'boolean', + default: false, + description: 'If the logs of each monitor will be returned.', + }, + { + displayName: 'Maintenance Window', + name: 'mwindow', + type: 'boolean', + default: false, + description: 'If the maintenance windows for the monitors to be returned.', + }, + { + displayName: 'Monitor IDs', + name: 'monitors', + type: 'string', + default: '', + description: 'Monitors IDs separated with dash, e.g. 15830-32696-83920.', + }, + { + displayName: 'Response Times', + name: 'response_times', + type: 'boolean', + default: false, + description: 'Whether the response time data of each monitor will be returned.', + }, + { + displayName: 'Search', + name: 'search', + type: 'string', + default: '', + description: 'A keyword to be matched against url and friendly name.', + }, + { + displayName: 'Statuses', + name: 'statuses', + type: 'multiOptions', + default: '', + options: [ + { + name: 'Paused', + value: 0, + }, + { + name: 'Not Checked Yet', + value: 1, + }, + { + name: 'Up', + value: 2, + }, + { + name: 'Seems Down', + value: 8, + }, + { + name: 'Down', + value: 9, + }, + ], + }, + { + displayName: 'Types', + name: 'types', + type: 'multiOptions', + default: '', + options: [ + { + name: 'Heartbeat', + value: 5, + }, + { + name: 'HTTP(S)', + value: 1, + }, + { + name: 'Keyword', + value: 2, + }, + { + name: 'Ping', + value: 3, + }, + { + name: 'Port', + value: 4, + }, + ], + description: 'Select monitor types.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* monitor:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'ID', + name: 'id', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'monitor', + ], + operation: [ + 'update', + ], + }, + }, + description: 'The ID of the monitor.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'monitor', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Friendly Name', + name: 'friendly_name', + type: 'string', + default: '', + description: 'The friendly name of the monitor.', + }, + { + displayName: 'HTTP Auth Type', + name: 'http_auth_type', + type: 'options', + default: '', + options: [ + { + name: 'HTTP Basic', + value: 1, + }, + { + name: 'Digest', + value: 2, + }, + ], + description: 'The authentication type for password-protected web pages.', + }, + { + displayName: 'HTTP Method', + name: 'http_method', + type: 'options', + default: '', + options: [ + { + name: 'DELETE', + value: 6, + }, + { + name: 'GET', + value: 2, + }, + { + name: 'HEAD', + value: 1, + }, + { + name: 'OPTIONS', + value: 7, + }, + { + name: 'PATCH', + value: 5, + }, + { + name: 'POST', + value: 3, + }, + { + name: 'PUT', + value: 4, + }, + ], + description: 'The HTTP method to be used.', + }, + { + displayName: 'HTTP Password', + name: 'http_password', + type: 'string', + default: '', + description: 'The password used for password-protected web pages.', + }, + { + displayName: 'HTTP Username', + name: 'http_username', + type: 'string', + default: '', + description: 'The username used for password-protected web pages.', + }, + { + displayName: 'Interval', + name: 'interval', + type: 'number', + default: '', + description: 'The interval for the monitoring check.', + }, + { + displayName: 'Port', + name: 'port', + type: 'number', + default: '', + description: 'The monitored port.', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + default: '', + options: [ + { + name: 'Pause', + value: 0, + }, + { + name: 'Resume', + value: 1, + }, + ], + description: 'Select monitor statuses.', + }, + { + displayName: 'Sub type', + name: 'sub_type', + type: 'options', + default: '', + options: [ + { + name: 'Custom Port', + value: 99, + }, + { + name: 'FTP (21)', + value: 3, + }, + { + name: 'HTTP (80)', + value: 1, + }, + { + name: 'HTTPS (443)', + value: 2, + }, + { + name: 'IMAP (143)', + value: 6, + }, + { + name: 'POP3 (110)', + value: 5, + }, + { + name: 'SMTP (25)', + value: 4, + }, + ], + description: 'Specify which pre-defined port/service or custom port is monitored.', + }, + { + displayName: 'URL', + name: 'url', + type: 'string', + default: '', + description: 'The URL/IP of the monitor.', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/UptimeRobot/PublicStatusPageDescription.ts b/packages/nodes-base/nodes/UptimeRobot/PublicStatusPageDescription.ts new file mode 100644 index 000000000..56b0a4fc6 --- /dev/null +++ b/packages/nodes-base/nodes/UptimeRobot/PublicStatusPageDescription.ts @@ -0,0 +1,338 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const publicStatusPageOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'publicStatusPage', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a public status page', + + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a public status page', + }, + { + name: 'Get', + value: 'get', + description: 'Get a public status page', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all a public status pages', + }, + // Got deactivated because it did not work reliably. Looks like it is on the UptimeRobot + // side but we deactivate for now just to be sure + // { + // name: 'Update', + // value: 'update', + // description: 'Update a public status page', + // }, + ], + default: 'getAll', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const publicStatusPageFields = [ + /* -------------------------------------------------------------------------- */ + /* publicStatusPage:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Friendly Name', + name: 'friendlyName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'publicStatusPage', + ], + operation: [ + 'create', + ], + }, + }, + description: 'The friendly name of the status page.', + }, + { + displayName: 'Monitor IDs', + name: 'monitors', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'publicStatusPage', + ], + operation: [ + 'create', + ], + }, + }, + description: 'Monitor ids to be displayed in status page (the values are separated with a dash (-) or 0 for all monitors).', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'publicStatusPage', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Custom Domain', + name: 'custom_domain', + type: 'string', + default: '', + description: 'The domain or subdomain that the status page will run on.', + }, + { + displayName: 'Password', + name: 'password', + type: 'string', + default: '', + description: 'The password for the status page.', + }, + { + displayName: 'Sort', + name: 'sort', + type: 'options', + default: '', + options: [ + { + name: 'Friendly Name (A-Z)', + value: 1, + }, + { + name: 'Friendly Name (Z-A)', + value: 2, + }, + { + name: 'Status (Up-Down-Paused)', + value: 3, + }, + { + name: 'Status (Down-Up-Paused)', + value: 4, + }, + ], + description: 'The sorting of the status page.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* publicStatusPage:delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'ID', + name: 'id', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'publicStatusPage', + ], + operation: [ + 'delete', + 'get', + ], + }, + }, + description: 'The ID of the public status page.', + }, + + /* -------------------------------------------------------------------------- */ + /* publicStatusPage:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'publicStatusPage', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'publicStatusPage', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'publicStatusPage', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Public Status Page IDs', + name: 'psps', + type: 'string', + default: '', + description: 'Public status pages ids separated with dash, e.g. 236-1782-4790.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* publicStatusPage:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'ID', + name: 'id', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'publicStatusPage', + ], + operation: [ + 'update', + ], + }, + }, + description: 'The ID of the public status page.', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'publicStatusPage', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Custom Domain', + name: 'custom_domain', + type: 'string', + default: '', + description: 'The domain or subdomain that the status page will run on.', + }, + { + displayName: 'Friendly Name', + name: 'friendly_name', + type: 'string', + default: '', + description: 'The friendly name of the status page.', + }, + { + displayName: 'Monitor IDs', + name: 'monitors', + type: 'string', + default: '', + description: 'Monitor IDs to be displayed in status page (the values are separated with a dash (-) or 0 for all monitors).', + }, + { + displayName: 'Password', + name: 'password', + type: 'string', + default: '', + description: 'The password for the status page.', + }, + { + displayName: 'Sort', + name: 'sort', + type: 'options', + default: '', + options: [ + { + name: 'Friendly Name (A-Z)', + value: 1, + }, + { + name: 'Friendly Name (Z-A)', + value: 2, + }, + { + name: 'Status (Up-Down-Paused)', + value: 3, + }, + { + name: 'Status (Down-Up-Paused)', + value: 4, + }, + ], + description: 'The sorting of the status page', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/UptimeRobot/UptimeRobot.node.json b/packages/nodes-base/nodes/UptimeRobot/UptimeRobot.node.json new file mode 100644 index 000000000..4171d3704 --- /dev/null +++ b/packages/nodes-base/nodes/UptimeRobot/UptimeRobot.node.json @@ -0,0 +1,20 @@ +{ + "node": "n8n-nodes-base.uptimeRobot", + "nodeVersion": "1.0", + "codexVersion": "1.0", + "categories": [ + "Monitoring" + ], + "resources": { + "credentialDocumentation": [ + { + "url": "https://docs.n8n.io/credentials/uptimeRobot" + } + ], + "primaryDocumentation": [ + { + "url": "https://docs.n8n.io/nodes/n8n-nodes-base.uptimeRobot/" + } + ] + } +} \ No newline at end of file diff --git a/packages/nodes-base/nodes/UptimeRobot/UptimeRobot.node.ts b/packages/nodes-base/nodes/UptimeRobot/UptimeRobot.node.ts new file mode 100644 index 000000000..56507f107 --- /dev/null +++ b/packages/nodes-base/nodes/UptimeRobot/UptimeRobot.node.ts @@ -0,0 +1,447 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeExecutionData, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + uptimeRobotApiRequest, +} from './GenericFunctions'; + +import { + monitorFields, + monitorOperations, +} from './MonitorDescription'; + +import { + alertContactFields, + alertContactOperations, +} from './AlertContactDescription'; + +import { + maintenanceWindowFields, + maintenanceWindowOperations, +} from './MaintenanceWindowDescription'; + +import { + publicStatusPageFields, + publicStatusPageOperations, +} from './PublicStatusPageDescription'; + +import * as moment from 'moment-timezone'; + +export class UptimeRobot implements INodeType { + description: INodeTypeDescription = { + displayName: 'UptimeRobot', + name: 'uptimeRobot', + icon: 'file:uptimerobot.svg', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume UptimeRobot API', + defaults: { + name: 'UptimeRobot', + color: '#3bd671', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'uptimeRobotApi', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Account', + value: 'account', + }, + { + name: 'Alert Contact', + value: 'alertContact', + }, + { + name: 'Maintenance Window', + value: 'maintenanceWindow', + }, + { + name: 'Monitor', + value: 'monitor', + }, + { + name: 'Public Status Page', + value: 'publicStatusPage', + }, + ], + default: 'account', + description: 'Resource to consume.', + }, + /* -------------------------------------------------------------------------- */ + /* account:getAccountDetails */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'account', + ], + }, + }, + options: [ + { + name: 'Get', + value: 'get', + description: 'Get account details', + }, + ], + default: 'get', + description: 'The operation to perform.', + }, + /* -------------------------------------------------------------------------- */ + /* Monitor */ + /* -------------------------------------------------------------------------- */ + ...monitorOperations, + ...monitorFields, + /* -------------------------------------------------------------------------- */ + /* Alert Contact */ + /* -------------------------------------------------------------------------- */ + ...alertContactOperations, + ...alertContactFields, + /* -------------------------------------------------------------------------- */ + /* Maintenance Window */ + /* -------------------------------------------------------------------------- */ + ...maintenanceWindowOperations, + ...maintenanceWindowFields, + /* -------------------------------------------------------------------------- */ + /* Public Status Page */ + /* -------------------------------------------------------------------------- */ + ...publicStatusPageOperations, + ...publicStatusPageFields, + ], + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData = []; + const length = items.length; + let responseData; + const timezone = this.getTimezone(); + for (let i = 0; i < length; i++) { + try { + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + let body: IDataObject = {}; + //https://uptimerobot.com/#methods + if (resource === 'account') { + if (operation === 'get') { + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/getAccountDetails'); + responseData = responseData.account; + } + } + + if (resource === 'monitor') { + if (operation === 'create') { + body = { + friendly_name: this.getNodeParameter('friendlyName', i) as string, + url: this.getNodeParameter('url', i) as string, + type: this.getNodeParameter('type', i) as number, + }; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/newMonitor', body); + responseData = responseData.monitor; + } + + if (operation === 'delete') { + + body = { + id: this.getNodeParameter('id', i) as string, + }; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/deleteMonitor', body); + responseData = responseData.monitor; + } + if (operation === 'get') { + const monitors = this.getNodeParameter('id', i) as string; + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/getMonitors', { monitors }); + responseData = responseData.monitors; + } + + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const filters = this.getNodeParameter('filters', i) as IDataObject; + + body = { + ...filters, + }; + + if (body.statuses) { + body.statuses = (body.statuses as string[]).join('-'); + } + + if (body.types) { + body.types = (body.types as string[]).join('-'); + } + + if (body.alert_contacts) { + body.alert_contacts = 1; + } + if (body.logs) { + body.logs = 1; + } + if (body.mwindow) { + body.mwindows = 1; + } + if (body.response_times) { + body.response_times = 1; + } + + if (!returnAll) { + body.limit = this.getNodeParameter('limit', i) as number; + } + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/getMonitors', body); + responseData = responseData.monitors; + } + + if (operation === 'reset') { + + body = { + id: this.getNodeParameter('id', i) as string, + }; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/resetMonitor', body); + responseData = responseData.monitor; + } + + if (operation === 'update') { + + body = { + id: this.getNodeParameter('id', i) as string, + ...this.getNodeParameter('updateFields', i) as IDataObject, + }; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/editMonitor', body); + responseData = responseData.monitor; + } + } + + if (resource === 'alertContact') { + if (operation === 'create') { + + body = { + friendly_name: this.getNodeParameter('friendlyName', i) as string, + value: this.getNodeParameter('value', i) as string, + type: this.getNodeParameter('type', i) as number, + }; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/newAlertContact', body); + responseData = responseData.alertcontact; + } + if (operation === 'delete') { + + body = { + id: this.getNodeParameter('id', i) as string, + }; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/deleteAlertContact', body); + responseData = responseData.alert_contact; + } + if (operation === 'get') { + const id = this.getNodeParameter('id', i) as string; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/getAlertContacts', { alert_contacts: id }); + responseData = responseData.alert_contacts; + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + body = { + ...this.getNodeParameter('filters', i) as IDataObject, + }; + + if (!returnAll) { + body.limit = this.getNodeParameter('limit', i) as number; + } + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/getAlertContacts', body); + responseData = responseData.alert_contacts; + } + if (operation === 'update') { + + body = { + id: this.getNodeParameter('id', i) as string, + ...this.getNodeParameter('updateFields', i) as IDataObject, + }; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/editAlertContact', body); + responseData = responseData.alert_contact; + } + } + if (resource === 'maintenanceWindow') { + if (operation === 'create') { + const startTime = this.getNodeParameter('start_time', i) as string; + const type = this.getNodeParameter('type', i) as number; + + const parsedStartTime = type === 1 + ? moment.tz(startTime, timezone).unix() + : moment.tz(startTime, timezone).format('HH:mm'); + + body = { + duration: this.getNodeParameter('duration', i) as number, + friendly_name: this.getNodeParameter('friendlyName', i) as string, + start_time: parsedStartTime, + type, + }; + + if (type === 3) { + body['value'] = this.getNodeParameter('weekDay', i) as number; + } + if (type === 4) { + body['value'] = this.getNodeParameter('monthDay', i) as number; + } + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/newMWindow', body); + responseData = responseData.mwindow; + + } + if (operation === 'delete') { + + body = { + id: this.getNodeParameter('id', i) as string, + }; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/deleteMWindow', body); + responseData = { status: responseData.message }; + + } + if (operation === 'get') { + const mwindows = this.getNodeParameter('id', i) as string; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/getMWindows', { mwindows }); + responseData = responseData.mwindows; + + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + body = { + ...this.getNodeParameter('filters', i) as IDataObject, + }; + + if (!returnAll) { + body.limit = this.getNodeParameter('limit', i) as number; + } + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/getMWindows', body); + responseData = responseData.mwindows; + + } + if (operation === 'update') { + + body = { + id: this.getNodeParameter('id', i) as string, + duration: this.getNodeParameter('duration', i) as string, + ...this.getNodeParameter('updateFields', i) as IDataObject, + }; + + if (body.type === 1 && body.start_time) { + body.start_time = moment.tz(body.start_time, timezone).unix(); + } else { + body.start_time = moment.tz(body.start_time, timezone).format('HH:mm'); + } + + if (body.type === 3) { + body['value'] = body.weekDay; + delete body.weekDay; + } + if (body.type === 4) { + body['value'] = body.monthDay; + delete body.monthDay; + } + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/editMWindow', body); + responseData = responseData.mwindow; + + } + } + if (resource === 'publicStatusPage') { + if (operation === 'create') { + + body = { + friendly_name: this.getNodeParameter('friendlyName', i) as string, + monitors: this.getNodeParameter('monitors', i) as string, + ...this.getNodeParameter('additionalFields', i) as IDataObject, + }; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/newPSP', body); + responseData = responseData.psp; + + } + if (operation === 'delete') { + + body = { + id: this.getNodeParameter('id', i) as string, + }; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/deletePSP', body); + responseData = responseData.psp; + + } + if (operation === 'get') { + const psps = this.getNodeParameter('id', i) as string; + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/getPSPs', { psps }); + responseData = responseData.psps; + + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + body = { + ...this.getNodeParameter('filters', i) as IDataObject, + }; + + if (!returnAll) { + body.limit = this.getNodeParameter('limit', i) as number; + } + + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/getPSPs', body); + responseData = responseData.psps; + + } + if (operation === 'update') { + + body = { + id: this.getNodeParameter('id', i) as string, + ...this.getNodeParameter('updateFields', i) as IDataObject, + }; + responseData = await uptimeRobotApiRequest.call(this, 'POST', '/editPSP', body); + responseData = responseData.psp; + } + } + Array.isArray(responseData) + ? returnData.push(...responseData) + : returnData.push(responseData); + } catch (error) { + if (this.continueOnFail()) { + returnData.push({ error: error.message }); + continue; + } + throw error; + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/UptimeRobot/uptimerobot.svg b/packages/nodes-base/nodes/UptimeRobot/uptimerobot.svg new file mode 100644 index 000000000..c9bbdf55a --- /dev/null +++ b/packages/nodes-base/nodes/UptimeRobot/uptimerobot.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 6a11aa1f5..f1cfd6859 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -259,6 +259,7 @@ "dist/credentials/UnleashedSoftwareApi.credentials.js", "dist/credentials/UpleadApi.credentials.js", "dist/credentials/UProcApi.credentials.js", + "dist/credentials/UptimeRobotApi.credentials.js", "dist/credentials/VeroApi.credentials.js", "dist/credentials/VonageApi.credentials.js", "dist/credentials/WebflowApi.credentials.js", @@ -546,6 +547,7 @@ "dist/nodes/UnleashedSoftware/UnleashedSoftware.node.js", "dist/nodes/Uplead/Uplead.node.js", "dist/nodes/UProc/UProc.node.js", + "dist/nodes/UptimeRobot/UptimeRobot.node.js", "dist/nodes/Vero/Vero.node.js", "dist/nodes/Vonage/Vonage.node.js", "dist/nodes/Webflow/Webflow.node.js",