diff --git a/plugins/ExamplePlugin/v1/cspell.json b/plugins/ExamplePlugin/v1/cspell.json deleted file mode 100644 index d4b2f96..0000000 --- a/plugins/ExamplePlugin/v1/cspell.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": "0.2", - "words": [ - "timeframe", - "timeframes", - "and", - "other", - "nonstandard", - "words", - "to", - "allow" - ] -} diff --git a/plugins/ExamplePlugin/v1/custom_types.json b/plugins/ExamplePlugin/v1/custom_types.json deleted file mode 100644 index 42bdab2..0000000 --- a/plugins/ExamplePlugin/v1/custom_types.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "name": "building", - "type": "Building", - "icon": "building", - "singular": "Building", - "plural": "Buildings" - } -] \ No newline at end of file diff --git a/plugins/ExamplePlugin/v1/data_streams.json b/plugins/ExamplePlugin/v1/data_streams.json deleted file mode 100644 index 2db64f1..0000000 --- a/plugins/ExamplePlugin/v1/data_streams.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "rowTypes": [ - { - "name": "stuff", - "metadata": [ - { "name": "enum", "displayName": "Enum", "shape": "string", "role": "label" }, - { "name": "start", "displayName": "Start", "shape": "date" }, - { "name": "unixStart", "displayName": "Unix Start", "shape": ["number", { "decimalPlaces": 0 }] }, - { "name": "end", "displayName": "End", "shape": "date" }, - { "name": "unixEnd", "displayName": "Unix End", "shape": ["number", { "decimalPlaces": 0 }] }, - { "name": "interval", "displayName": "Interval", "shape": "string" }, - { "name": "top", "displayName": "Top", "shape": "string" } - ] - } - ], - "dataSources":[ - { - "name": "dataSourceUnscoped", - "displayName": "Stuff (no scope)", - "description": "Return stuff", - "supportedScope": "none" - }, - { - "name": "appScopedProperties", - "displayName": "App-scoped Properties", - "description": "Get selected properties for scoped apps", - "supportedScope": "list", - "targetNodesProperties": ["id", "name", "sourceId", "appType", "appStatus" ] - } - ], - "matches":{ "__configId": { "type": "equals", "value": "{{configId}}" } }, - "dataStreams":[ - { - "displayName": "Stuff", - "description": "Returns Stuff", - "dataSourceName": "dataSourceUnscoped", - "definition": { - "name": "stuffUnscoped", - "dataSourceConfig": {}, - "rowPath": [], - "matches": "none", - "rowType": { "name": "stuff" } - }, - "template": [ - { - "name": "top", - "type": "text", - "label": "Optional, list top n", - "title": "Optional, list top n", - "help": "Pick how many rows of data to return", - "validation": { "required": false }, - "placeholder": "10" - } - ] - }, - { - "displayName": "App Health", - "description": "Returns Health for scoped Apps", - "dataSourceName": "appScopedProperties", - "provides": "health", - "definition": { - "name": "appHealth", - "timeframes": false, - "dataSourceConfig": { "properties": ["appStatus", "appType"] }, - "rowPath": [], - "matches": { - "sourceType": { "type": "equals", "value": "mySortOfApp" } - }, - "metadata": [ - { "name": "appStatus", "displayName": "Status", "shape": ["state", - { "map": { "success": ["OK"], "warning": ["Degraded", "Installing"], "error": ["Broken"] } } - ]}, - { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, - { "name": "id", "visible": false, "shape": "string", "role": "id" }, - { "name": "appType", "displayName": "App Type", "shape": "string" } - ] - } - } - ] -} diff --git a/plugins/ExamplePlugin/v1/handler.js b/plugins/ExamplePlugin/v1/handler.js deleted file mode 100644 index f8ac2d4..0000000 --- a/plugins/ExamplePlugin/v1/handler.js +++ /dev/null @@ -1,147 +0,0 @@ -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/ExamplePlugin/v1/handlerConfig.js b/plugins/ExamplePlugin/v1/handlerConfig.js deleted file mode 100644 index 15d7ae8..0000000 --- a/plugins/ExamplePlugin/v1/handlerConfig.js +++ /dev/null @@ -1,72 +0,0 @@ -import { stageApps } from './importObjects/apps.js'; -import { stageBuildings } from './importObjects/building.js'; -import { appScopedProperties } from './readDataSource/appScopedProperties.js'; -import { dataSourceUnscoped } from './readDataSource/dataSourceUnscoped.js'; - -// ============================================================================ -// -// testConfig -// -export async function testConfig(context) { - const messages = []; - - if (typeof context.pluginConfig.serverUrl === 'string' && context.pluginConfig.serverUrl.startsWith('https:')) { - messages.push({ - status: 'success', - message: 'Testing passed' - }); - } else { - messages.push({ - status: 'warning', - message: 'serverUrl is invalid' - }); - messages.push({ - status: 'error', - message: 'nothing works!' - }); - } - - const result = { - link: 'https://yourCompany.com/docs/plugin/pluginsetup-examplehybrid', - messages: messages - }; - return result; -} - -// ============================================================================ -// -// importObjects -// -export const importStages = [stageApps, stageBuildings]; - -export const defaultApiLimits = { - apps: 10, - buildings: 3 -}; - -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 = { - appScopedProperties, - dataSourceUnscoped -}; diff --git a/plugins/ExamplePlugin/v1/importObjects/apps.js b/plugins/ExamplePlugin/v1/importObjects/apps.js deleted file mode 100644 index 4447f65..0000000 --- a/plugins/ExamplePlugin/v1/importObjects/apps.js +++ /dev/null @@ -1,72 +0,0 @@ -export const totalNApps = 37; -export async function stageApps(context) { - let finished = false; - let apiLimit = context.apiLimits.apps; - - try { - let appIndex = context.pageAPI.get('appIndex'); - context.log.debug(`Getting page of ${apiLimit} apps from ${appIndex}`); - - const response = await GetAppObjectsFromExternalApi(context, appIndex, apiLimit); - for (const app of response.data.apps) { - addVertexForApp(context, app); - } - appIndex += apiLimit; - if (appIndex < response.paging.totalLength) { - context.pageAPI.set('appIndex', appIndex); - } else { - finished = true; - } - } catch (err) { - context.reportImportProblem(err, 'Apps'); - finished = true; - } - return finished; -} - -async function addVertexForApp(context, appObject) { - const vertex = { - sourceName: `myPluginName:${context.pluginConfig.serverUrl}`, - name: appObject.appName, - type: 'app', - sourceType: 'mySortOfApp', - sourceId: `app_${appObject.appNum}`, - appType: appObject.appType - }; - context.vertices.push(vertex); - - return vertex; -} - -async function GetAppObjectsFromExternalApi(context, appIndex, apiLimit) { - // This example code makes no use of the plugin configuration in the context object - // A real plugin would be making HTTP requests authenticated with information in - // the plugin configuration using fetch and creating vertices and edges using the - // information thus obtained. - let appNum = appIndex; - const apps = []; - while (appNum < totalNApps && apps.length < apiLimit) { - const appType = - (appNum & 7) === 0 - ? 'Hybrid' - : (appNum & 7) === 5 - ? 'ThinClient' - : (appNum & 7) === 3 - ? 'FatClient' - : 'Web'; - apps.push({ - appName: `Application #${appNum}`, - appNum, - appType - }); - appNum++; - } - return { - paging: { - totalLength: totalNApps - }, - data: { - apps - } - }; -} diff --git a/plugins/ExamplePlugin/v1/importObjects/building.js b/plugins/ExamplePlugin/v1/importObjects/building.js deleted file mode 100644 index a2a06b6..0000000 --- a/plugins/ExamplePlugin/v1/importObjects/building.js +++ /dev/null @@ -1,78 +0,0 @@ -const totalNBuildings = 12; -import { totalNApps } from './apps.js'; - -export async function stageBuildings(context) { - let finished = false; - let apiLimit = context.apiLimits.buildings; - - try { - let buildingIndex = context.pageAPI.get('buildingIndex'); - context.log.debug(`Getting page of ${apiLimit} buildings from ${buildingIndex}`); - - const response = await GetBuildingObjectsFromExternalApi(context, buildingIndex, apiLimit); - for (const building of response.data.buildings) { - addVertexForBuilding(context, building); - - const createEdgeFrom = (bn, an) => - context.edges.push({ - label: 'monitors', - outV: `app_${an}`, - inV: `building_${bn}` - }); - - // Create a monitoring edge from some random(ish) app to this building - createEdgeFrom(buildingIndex, (buildingIndex * 53) % totalNApps); - createEdgeFrom(buildingIndex, (buildingIndex * 59 - 7) % totalNApps); - createEdgeFrom(buildingIndex, (buildingIndex * 37 + 4) % totalNApps); - - buildingIndex++; - } - if (buildingIndex < response.paging.totalLength) { - context.pageAPI.set('buildingIndex', buildingIndex); - } else { - finished = true; - } - } catch (err) { - context.reportImportProblem(err, 'Buildings'); - finished = true; - } - return finished; -} - -async function addVertexForBuilding(context, buildingObject) { - const vertex = { - sourceName: `myPluginName:${context.pluginConfig.serverUrl}`, - name: buildingObject.buildingName, - type: 'building', - sourceType: 'myBuildingType', - sourceId: `building_${buildingObject.buildingNum}`, - buildingType: buildingObject.buildingType - }; - context.vertices.push(vertex); - - return vertex; -} - -async function GetBuildingObjectsFromExternalApi(context, buildingIndex, apiLimit) { - // This example code makes no use of the plugin configuration in the context object - // A real plugin would be making HTTP requests authenticated with information in - // the plugin configuration using fetch and creating vertices and edges using the - // information thus obtained. - let buildingNum = buildingIndex; - const buildings = []; - while (buildingNum < totalNBuildings && buildings.length < apiLimit) { - buildings.push({ - buildingName: `Building #${buildingNum}`, - buildingNum - }); - buildingNum++; - } - return { - paging: { - totalLength: totalNBuildings - }, - data: { - buildings - } - }; -} diff --git a/plugins/ExamplePlugin/v1/metadata.json b/plugins/ExamplePlugin/v1/metadata.json deleted file mode 100644 index fad963c..0000000 --- a/plugins/ExamplePlugin/v1/metadata.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "exampleHybrid", - "displayName": "Example Hybrid", - "version": "1.0.0", - "author": "Someone", - "description": "Demonstrates the structure of a hybrid plugin.", - "category": "Miscellaneous", - "type": "hybrid", - "restrictedToPlatforms": [], - "supportsConfigValidation": true, - "links": [ - { - "url": "https://yourCompany.com/docs/plugin/pluginsetup-examplehybrid", - "label": "Help adding this plugin" - } - ], - "keywords": [ - "additional", - "search", - "terms" - ], - "objectTypes": [ - "app", - "and", - "other", - "imported", - "object", - "types" - ], - "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 }, - "dataSourceUnscoped": { "connector": "nodejs", "version": "2.0.0", "config": { "scriptPath": "handler.js", "entryPoint": "readDataSource" } }, - "appScopedProperties": { "connector": "nodejs", "version": "2.0.0", "config": { "scriptPath": "handler.js", "entryPoint": "readDataSource" } } - } -} diff --git a/plugins/ExamplePlugin/v1/package-lock.json b/plugins/ExamplePlugin/v1/package-lock.json deleted file mode 100644 index 15b0864..0000000 --- a/plugins/ExamplePlugin/v1/package-lock.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "hybrid", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "hybrid", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "lodash": "^4.17.21" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - } - } -} diff --git a/plugins/ExamplePlugin/v1/package.json b/plugins/ExamplePlugin/v1/package.json deleted file mode 100644 index 56ec91a..0000000 --- a/plugins/ExamplePlugin/v1/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "hybrid", - "version": "1.0.0", - "type": "module", - "author": "someone", - "license": "ISC", - "dependencies": { - "lodash": "^4.17.21" - } -} diff --git a/plugins/ExamplePlugin/v1/readDataSource/appScopedProperties.js b/plugins/ExamplePlugin/v1/readDataSource/appScopedProperties.js deleted file mode 100644 index b6d853f..0000000 --- a/plugins/ExamplePlugin/v1/readDataSource/appScopedProperties.js +++ /dev/null @@ -1,24 +0,0 @@ -import _ from 'lodash'; -import { getScalar } from '../util.js'; -export async function appScopedProperties(context) { - const results = []; - for (const targetNode of context.targetNodes) { - const r = _.random(100); - const row = { - id: getScalar(targetNode, 'id'), - name: getScalar(targetNode, 'name'), - sourceId: getScalar(targetNode, 'sourceId') - }; - if (Array.isArray(context.dataSourceConfig.properties)) { - for (const property of context.dataSourceConfig.properties) { - if (property === 'appStatus') { - row[property] = r < 5 ? 'Broken' : r < 20 ? 'Degraded' : r < 30 ? 'Installing' : 'OK'; - } else { - row[property] = getScalar(targetNode, property); - } - } - } - results.push(row); - } - return results; -} diff --git a/plugins/ExamplePlugin/v1/readDataSource/dataSourceUnscoped.js b/plugins/ExamplePlugin/v1/readDataSource/dataSourceUnscoped.js deleted file mode 100644 index b6fa21b..0000000 --- a/plugins/ExamplePlugin/v1/readDataSource/dataSourceUnscoped.js +++ /dev/null @@ -1,11 +0,0 @@ -// eslint-disable-next-line no-unused-vars -import _ from 'lodash'; -export async function dataSourceUnscoped(context) { - const results = []; - // Simply echo the values of timeframe and the configurable "top" entry in dataSourceConfig - results.push({ - ...context.timeframe, - top: context.dataSourceConfig.top - }); - return results; -} diff --git a/plugins/ExamplePlugin/v1/ui.json b/plugins/ExamplePlugin/v1/ui.json deleted file mode 100644 index 1ddeeca..0000000 --- a/plugins/ExamplePlugin/v1/ui.json +++ /dev/null @@ -1,106 +0,0 @@ -[ - { - "type": "text", - "name": "serverUrl", - "label": "Server URL", - "title": "Server URL", - "help": "The URL to use", - "validation": { - "required": true - }, - "placeholder": "https://your.server.net/api/v1" - }, - { - "type": "password", - "name": "apiKey", - "label": "API Key", - "title": "API Key", - "help": "Provide your API Key", - "validation": { - "required": true - } - }, - { - "type": "autocomplete", - "name": "fruitChoice", - "label": "Fruit Endpoint", - "title": "Fruit Endpoint", - "help": "Select your required fruit endpoint", - "validation": { "required": true }, - "placeholder": "Fruit...", - "isMulti": false, - "data": { - "source": "fixed", - "values": [ - { "value":"https://api.apple.com", "label": "api.apple.com"}, - { "value":"https://api.banana.com", "label": "api.banana.com"} - ] - }, - "defaultValue": "" - }, - { - "name": "fruitCount", - "type": "number", - "label": "Fruit Count", - "title": "Fruit Count", - "help": "Pick X items of your fruit choice.", - "defaultValue": 100, - "validation": { "required": false, "min": 1, "max": 1000 } - }, - { - "type": "radio", - "name": "fruitType", - "label": "Fruit type", - "title": "Fruit type", - "help": "Select 'Apple' or 'Banana'", - "validation": { "required": true }, - "defaultValue": "apple", - "options": [ - { - "value": "apple", - "label": "Apple", - "description": "An apple!" - }, - { - "value":"banana", - "label": "Banana", - "description": "A banana!" - }] - }, - { - "type": "checkbox", - "name": "advancedLabels", - "label": "Advanced Options", - "title": "Advanced Options", - "help": "Configure custom object types", - "defaultValue": false, - "value": "show" - }, - { - "type": "fieldGroup", - "name": "advancedLabelsGroup", - "label": "Advanced Options", - "visible": { - "advancedLabels": "show" - }, - "fields": [ - { - "type": "key-value", - "name": "labelSetting", - "label": "Object Discovery Settings", - "title": "Object Discovery Settings", - "help": "Enter the label setting for your object creation", - "verb": "->", - "displayName": "object type", - "keyInput": { - "title": "Object Type", - "placeholder": "Server Cpu" - }, - "valueInput": { - "title": "Object Labels", - "placeholder": "servername, cpu" - } - } - ] - } -] \ No newline at end of file diff --git a/plugins/ExamplePlugin/v1/util.js b/plugins/ExamplePlugin/v1/util.js deleted file mode 100644 index 50a1a82..0000000 --- a/plugins/ExamplePlugin/v1/util.js +++ /dev/null @@ -1,6 +0,0 @@ -export function getScalar(obj, propertyName) { - if (Array.isArray(obj[propertyName])) { - return obj[propertyName][0]; - } - return obj[propertyName]; -} \ No newline at end of file