diff --git a/utils/__tests__/build-validate-quickstart-artifact.test.js b/utils/__tests__/build-validate-quickstart-artifact.test.js index 4bb77a6b97..f6a809f757 100644 --- a/utils/__tests__/build-validate-quickstart-artifact.test.js +++ b/utils/__tests__/build-validate-quickstart-artifact.test.js @@ -1,20 +1,36 @@ -import * as fs from 'fs'; +import * as path from 'path'; import Quickstart from '../lib/Quickstart'; import DataSource from '../lib/DataSource'; import Alert from '../lib/Alert'; import Dashboard from '../lib/Dashboard'; - import { getArtifactComponents, - getDataSourceIds, validateArtifact, } from '../build-validate-quickstart-artifact'; -jest.mock('../lib/Quickstart'); -jest.mock('../lib/DataSource'); -jest.mock('../lib/Alert'); -jest.mock('../lib/Dashboard'); -jest.mock('fs'); +const MOCK_FILES_BASEPATH = path.resolve(__dirname, '..', 'mock_files'); + +const mockQuickstart1 = new Quickstart( + '/quickstarts/mock-quickstart-1/config.yml', + MOCK_FILES_BASEPATH +); + +const mockQuickstart2 = new Quickstart( + 'quickstarts/mock-quickstart-2/config.yml', + MOCK_FILES_BASEPATH +); + +const mockDataSource1 = new DataSource( + 'test-data-source', + MOCK_FILES_BASEPATH +) + +const mockAlert1 = new Alert( + 'mock-alert-policy-1', + MOCK_FILES_BASEPATH +); + +const mockDashboard1 = new Dashboard('mock-dashboard-1', MOCK_FILES_BASEPATH); describe('built-validate-quickstart-artifact', () => { beforeEach(() => { @@ -26,58 +42,27 @@ describe('built-validate-quickstart-artifact', () => { it('should find all of the components', () => { Quickstart.getAll = jest .fn() - .mockReturnValueOnce([ - { config: 'test-quickstart-1' }, - { config: 'test-quickstart-2' }, - ]); + .mockReturnValueOnce([mockQuickstart1, mockQuickstart2]); - DataSource.getAll = jest - .fn() - .mockReturnValueOnce([{ config: 'test-dataSource-1' }]); + DataSource.getAll = jest.fn().mockReturnValueOnce([mockDataSource1]); - Alert.getAll = jest - .fn() - .mockReturnValueOnce([{ config: 'test-alert-1' }]); - Dashboard.getAll = jest - .fn() - .mockReturnValueOnce([{ config: 'test-dashboard-1' }]); + Alert.getAll = jest.fn().mockReturnValueOnce([mockAlert1]); + Dashboard.getAll = jest.fn().mockReturnValueOnce([mockDashboard1]); const actual = getArtifactComponents(); expect(actual.quickstarts).toHaveLength(2); - expect(actual.quickstarts[0]).toEqual('test-quickstart-1'); - expect(actual.quickstarts[1]).toEqual('test-quickstart-2'); - expect(actual.dataSources).toHaveLength(1); - expect(actual.dataSources[0]).toEqual('test-dataSource-1'); - expect(actual.alerts).toHaveLength(1); - expect(actual.alerts[0]).toEqual('test-alert-1'); - expect(actual.dashboards).toHaveLength(1); - expect(actual.dashboards[0]).toEqual('test-dashboard-1'); - }); + expect(actual.quickstarts[0].dashboards).toEqual([]); + expect(actual.quickstarts[1].dashboards).toEqual(['mock-dashboard-1']); - it('should produce a complete list of dataSource IDs', () => { - Quickstart.getAll = jest.fn().mockReturnValueOnce([]); - Alert.getAll = jest.fn().mockReturnValueOnce([]); - Dashboard.getAll = jest.fn().mockReturnValueOnce([]); - DataSource.getAll = jest - .fn() - .mockReturnValueOnce([ - { config: { id: 'community-1' } }, - { config: { id: 'community-2' } }, - { config: { id: 'community-3' } }, - ]); - - const { dataSources } = getArtifactComponents(); - fs.readFileSync.mockReturnValueOnce(JSON.stringify(['core-1', 'core-2'])); + expect(actual.dataSources).toHaveLength(1); + expect(actual.dataSources[0].id).toEqual('test-data-source'); - const actual = getDataSourceIds('dummy-file.json', dataSources); + expect(Object.keys(actual.alerts)).toHaveLength(1); + expect(Object.keys(actual.alerts)).toContain('mock-alert-policy-1'); - expect(actual).toHaveLength(5); - expect(actual).toContain('community-1'); - expect(actual).toContain('community-2'); - expect(actual).toContain('community-3'); - expect(actual).toContain('core-1'); - expect(actual).toContain('core-2'); + expect(Object.keys(actual.dashboards)).toHaveLength(1); + expect(Object.keys(actual.dashboards)).toContain('mock-dashboard-1'); }); }); @@ -86,8 +71,8 @@ describe('built-validate-quickstart-artifact', () => { type: 'object', properties: { quickstarts: { type: 'array' }, - alerts: { type: 'array' }, - dashboards: { type: 'array' }, + alerts: { type: 'object' }, + dashboards: { type: 'object' }, dataSources: { type: 'array', items: { @@ -109,20 +94,12 @@ describe('built-validate-quickstart-artifact', () => { DataSource.getAll = jest .fn() .mockReturnValueOnce([ - { config: { id: 'community-1', title: 'DataSource 1' } }, - { config: { id: 'community-2', title: 'DataSource 2' } }, - { config: { id: 'community-3', title: 'DataSource 3' } }, + mockDataSource1 ]); const components = getArtifactComponents(); - fs.readFileSync.mockReturnValueOnce(JSON.stringify(['core-1', 'core-2'])); - const dataSourceIds = getDataSourceIds( - 'dummy-file.json', - components.dataSources - ); - - const artifact = { ...components, dataSourceIds }; + const artifact = { ...components, dataSourceIds: ['core-1', 'core-2'] }; const actual = validateArtifact(TEST_SCHEMA, artifact); @@ -133,23 +110,19 @@ describe('built-validate-quickstart-artifact', () => { Quickstart.getAll = jest.fn().mockReturnValueOnce([]); Alert.getAll = jest.fn().mockReturnValueOnce([]); Dashboard.getAll = jest.fn().mockReturnValueOnce([]); - DataSource.getAll = jest - .fn() - .mockReturnValueOnce([ - { config: { id: 'community-1', title: 'DataSource 1' } }, - { config: { id: false, title: 'DataSource 2' } }, - { config: { id: 'community-3', title: 3 } }, - ]); + DataSource.getAll = jest.fn().mockReturnValueOnce([]); const components = getArtifactComponents(); - fs.readFileSync.mockReturnValueOnce(JSON.stringify(['core-1', 'core-2'])); - const dataSourceIds = getDataSourceIds( - 'dummy-file.json', - components.dataSources - ); - - const artifact = { ...components, dataSourceIds }; + const artifact = { + ...components, + dataSources: [ + { id: 'community-1', title: 'DataSource 1' }, + { id: false, title: 'DataSource 2' }, + { id: 'community-3', title: 3 }, + ], + dataSourceIds: ['core-1', 'core-2'], + }; const actual = validateArtifact(TEST_SCHEMA, artifact); diff --git a/utils/build-validate-quickstart-artifact.ts b/utils/build-validate-quickstart-artifact.ts index 46b88070a2..7a6480c90d 100644 --- a/utils/build-validate-quickstart-artifact.ts +++ b/utils/build-validate-quickstart-artifact.ts @@ -5,11 +5,10 @@ import get from 'lodash/get'; import Quickstart from "./lib/Quickstart"; import DataSource from "./lib/DataSource"; import Alert from "./lib/Alert"; -import Dashboard, { DashboardConfig } from "./lib/Dashboard"; +import Dashboard from "./lib/Dashboard"; import Ajv, { type ErrorObject } from 'ajv'; -import { QuickstartConfig, QuickstartConfigAlert } from './types/QuickstartConfig'; -import { DataSourceConfig } from './types/DataSourceConfig'; import { passedProcessArguments } from './lib/helpers'; +import { ArtifactDataSourceConfig, ArtifactDashboardConfig, ArtifactQuickstartConfig, ArtifactAlertConfig } from './types/Artifact'; type ArtifactSchema = Record; @@ -20,10 +19,10 @@ type InvalidItem = { } type ArtifactComponents = { - quickstarts: QuickstartConfig[], - dataSources: DataSourceConfig[], - alerts: QuickstartConfigAlert[][], - dashboards: DashboardConfig[] + quickstarts: ArtifactQuickstartConfig[], + dataSources: ArtifactDataSourceConfig[], + alerts: ArtifactAlertConfig, + dashboards: ArtifactDashboardConfig } type Artifact = ArtifactComponents | { @@ -38,17 +37,33 @@ const getSchema = (filepath: string): ArtifactSchema => { // NOTE: we could run these in parallel to speed up the script export const getArtifactComponents = (): ArtifactComponents => { - const quickstarts = Quickstart.getAll().map((quickstart) => quickstart.config); + const quickstarts = Quickstart + .getAll() + .map((quickstart) => quickstart.transformForArtifact()); + console.log(`[*] Found ${quickstarts.length} quickstarts`); - const dataSources = DataSource.getAll().map((dataSource) => dataSource.config); + const dataSources = DataSource + .getAll() + .map((dataSource) => dataSource.transformForArtifact()); + console.log(`[*] Found ${dataSources.length} dataSources`); - const alerts = Alert.getAll().map((alert) => alert.config); - console.log(`[*] Found ${alerts.length} alerts`); + const alerts = Alert.getAll().reduce((acc, alert) => { + const conditions = alert.transformForArtifact() + + return { ...acc, ...conditions } + + }, {}); + + console.log(`[*] Found ${Object.keys(alerts).length} alerts`); + + const dashboards = Dashboard.getAll().reduce((acc, dash) => { + const dashboard = dash.transformForArtifact() + return { ...acc, ...dashboard } - const dashboards = Dashboard.getAll().map((dashboard) => dashboard.config); - console.log(`[*] Found ${dashboards.length} dashboards`); + }, {}); + console.log(`[*] Found ${Object.keys(dashboards).length} dashboards`); return { quickstarts, @@ -58,7 +73,7 @@ export const getArtifactComponents = (): ArtifactComponents => { } }; -export const getDataSourceIds = (filepath: string, communityDataSources: DataSourceConfig[]): string[] => { +export const getDataSourceIds = (filepath: string, communityDataSources: ArtifactComponents['dataSources']): string[] => { const coreDataSourceIds = yaml.load( fs.readFileSync(filepath).toString('utf8') ) as string[]; diff --git a/utils/lib/Alert.ts b/utils/lib/Alert.ts index 11c81c0524..c5dadbcdb6 100644 --- a/utils/lib/Alert.ts +++ b/utils/lib/Alert.ts @@ -10,6 +10,7 @@ import type { AlertType, QuickstartAlertInput, } from '../types/QuickstartMutationVariable'; +import type { ArtifactAlertConfig, ArtifactAlertType } from '../types/Artifact'; import type { QuickstartConfigAlert } from '../types/QuickstartConfig'; import type { NerdGraphResponseWithLocalErrors } from '../types/nerdgraph'; @@ -79,7 +80,11 @@ export type SubmitSetRequiredDataSourcesMutationResult = | NerdGraphResponseWithLocalErrors | { errors: ErrorOrNerdGraphError[] }; -class Alert extends Component { +class Alert extends Component< + QuickstartConfigAlert[], + QuickstartAlertInput[], + ArtifactAlertConfig +> { constructor(identifier: string, basePath?: string) { super(identifier, basePath); this.isValid = this.validate(); @@ -134,6 +139,38 @@ class Alert extends Component { } } + /** + * Method extracts criteria from the config and returns an object appropriately + * structured for the artifact. + */ + transformForArtifact() { + if (!this.isValid) { + console.error( + `Alert is invalid.\nPlease check if the path at ${this.identifier} exists.` + ); + return {}; + } + + const alertPolicy = this.config.map((condition) => { + const { description, name, type } = condition; + + return { + description: description && description.trim(), + displayName: name && name.trim(), + rawConfiguration: JSON.stringify(condition), + sourceUrl: Component.getAssetSourceUrl(this.configPath), + type: type && (type.trim().toLowerCase() as ArtifactAlertType), + }; + }); + + return { [this.identifier]: alertPolicy }; + } + + /** + * Method creates mutation variables for a given Alert. + * + * @deprecated This function should be removed once we have finished our new build publishing pipeline + */ getMutationVariables() { if (!this.isValid) { console.error( @@ -190,13 +227,17 @@ class Alert extends Component { return true; } catch (error) { - logger.error(`Alert Condition: Validaiton for ${displayName} failed with an error`); + logger.error( + `Alert Condition: Validaiton for ${displayName} failed with an error` + ); return false; } }); - logger.debug(`Alert condition: Finished validation for alert at ${this.identifier}`); + logger.debug( + `Alert condition: Finished validation for alert at ${this.identifier}` + ); return validations.every(Boolean); } @@ -299,8 +340,10 @@ class Alert extends Component { } static getAll() { - const alertPaths = glob.sync(path.join(__dirname, '..', '..', 'alert-policies', '**', '*.+(yml|yaml)')); - return alertPaths.map(alertPath => { + const alertPaths = glob.sync( + path.join(__dirname, '..', '..', 'alert-policies', '**', '*.+(yml|yaml)') + ); + return alertPaths.map((alertPath) => { // The identifier for alerts is the folder and the file name // e.g. `node-js/HighCpuUtilization.yml` const identifier = path.join(...alertPath.split('/').slice(-2, -1)); diff --git a/utils/lib/Component.ts b/utils/lib/Component.ts index 143ec16262..a6ed66002b 100644 --- a/utils/lib/Component.ts +++ b/utils/lib/Component.ts @@ -3,7 +3,7 @@ import * as fs from 'fs'; import * as yaml from 'js-yaml'; import { GITHUB_REPO_BASE_URL } from '../constants'; -abstract class Component { +abstract class Component { public identifier: string; // Local path to the component. Ex: python/flask public configPath: string; // Absolute path to the config file within the repository public config: ConfigType; @@ -23,6 +23,7 @@ abstract class Component { abstract getConfigFilePath(): string; abstract getConfigContent(): ConfigType; abstract getMutationVariables(): MutationVariablesType; + abstract transformForArtifact(): ArtifactType; get fullPath() { return path.join(this.basePath, this.configPath); diff --git a/utils/lib/Dashboard.ts b/utils/lib/Dashboard.ts index 6a846f1516..781b428d36 100644 --- a/utils/lib/Dashboard.ts +++ b/utils/lib/Dashboard.ts @@ -16,6 +16,7 @@ import { fetchNRGraphqlResults, ErrorOrNerdGraphError, } from './nr-graphql-helpers'; +import { ArtifactDashboardConfig } from '../types/Artifact'; export interface DashboardConfig { name: string; @@ -61,7 +62,11 @@ export type SubmitSetRequiredDataSourcesMutationResult = | NerdGraphResponseWithLocalErrors | { errors: ErrorOrNerdGraphError[] }; -class Dashboard extends Component { +class Dashboard extends Component< + DashboardConfig, + QuickstartDashboardInput, + ArtifactDashboardConfig +> { /** * @returns - filepath from top level directory. */ @@ -104,6 +109,34 @@ class Dashboard extends Component { } } + /** + * Method extracts criteria from the config and returns an object appropriately + * structured for the artifact. + */ + transformForArtifact() { + if (!this.isValid) { + console.error( + `Dashboard is invalid.\nPlease check the dashboard at ${this.identifier}\n` + ); + return {}; + } + + const { name, description } = this.config; + const screenshotPaths = this.getScreenshotPaths(); + + return { + [this.identifier]: { + description: description && description.trim(), + displayName: name && name.trim(), + rawConfiguration: JSON.stringify(this.config), + sourceUrl: Component.getAssetSourceUrl(this.configPath), + screenshots: + screenshotPaths && + screenshotPaths.map((s) => this.getScreenshotUrl(s)), + }, + }; + } + /** * Get mutation variables from dashboard config * @returns - mutation variables for dashboard. diff --git a/utils/lib/DataSource.ts b/utils/lib/DataSource.ts index 04ccce4b0b..e19cb65e47 100644 --- a/utils/lib/DataSource.ts +++ b/utils/lib/DataSource.ts @@ -17,6 +17,7 @@ import type { DataSourceInstallDirectiveInput, DataSourceMutationVariable, } from '../types/DataSourceMutationVariable'; +import { ArtifactDataSourceConfig, ArtifactInstall } from '../types/Artifact'; export interface DataSourceMutationResponse { dataSource: { @@ -24,7 +25,11 @@ export interface DataSourceMutationResponse { }; } -class DataSource extends Component { +class DataSource extends Component< + DataSourceConfig, + string, + ArtifactDataSourceConfig +> { /** * @returns Filepath for the configuration file (from top-level directory). */ @@ -73,9 +78,39 @@ class DataSource extends Component { return this._getYamlConfigContent(); } + /** + * Method extracts criteria from the config and returns an object appropriately + * structured for the artifact. + */ + public transformForArtifact() { + const { keywords, description, categoryTerms, icon, ...rest } = this.config; + + return { + ...rest, + iconUrl: this._getIconUrl(), + install: this._parseInstallsForArtifact(), + categoryTerms: categoryTerms ? categoryTerms.map((t) => t.trim()) : [], + keywords: keywords ? keywords.map((k) => k.trim()) : [], + description: description && description.trim(), + }; + } + + private _parseInstallsForArtifact() { + const { install } = this.config; + + return { + primary: this._parseInstallDirectiveForArtifact(install.primary), + fallback: + install.fallback && + this._parseInstallDirectiveForArtifact(install.fallback), + }; + } + /** * Get the variables for the **Quickstart** mutation. * + * @deprecated This function should be removed once we have finished our new build publishing pipeline + * * @returns The ID for this data source */ getMutationVariables(): DataSourceConfig['id'] { @@ -189,6 +224,33 @@ class DataSource extends Component { return directive; } + /** + * Helper method that returns the directive, based on its type. + */ + private _parseInstallDirectiveForArtifact( + directive: DataSourceConfigInstallDirective + ): ArtifactInstall { + if ('link' in directive) { + const { url } = directive.link; + + return { + url: url?.trim() ?? '', + }; + } + + if ('nerdlet' in directive) { + const { nerdletId, nerdletState, requiresAccount } = directive.nerdlet; + + return { + nerdletId: nerdletId?.trim() ?? '', + nerdletState: nerdletState, + requiresAccount: requiresAccount, + }; + } + + return directive; + } + static isDataSource(x: DataSource | undefined): x is DataSource { return x !== undefined; } diff --git a/utils/lib/Quickstart.ts b/utils/lib/Quickstart.ts index c8e04f8239..5bfd59f557 100644 --- a/utils/lib/Quickstart.ts +++ b/utils/lib/Quickstart.ts @@ -23,6 +23,7 @@ import type { QuickstartSupportLevel, } from '../types/QuickstartMutationVariable'; import type { QuickstartConfig } from '../types/QuickstartConfig'; +import { ArtifactQuickstartConfig, ArtifactQuickstartConfigSupportLevel } from '../types/Artifact'; export interface QuickstartMutationResponse { quickstart: { @@ -34,6 +35,9 @@ interface SupportLevelMap { [key: string]: QuickstartSupportLevel; } +// FIXME: We will want to clean this up and conform to the `Artifact` types +// when we go to cleanup the deprecated mutation functionality after we have +// finalized the new quickstart publishing pipeline. const SUPPORT_LEVEL_ENUMS: SupportLevelMap = { 'New Relic': 'NEW_RELIC', Community: 'COMMUNITY', @@ -134,7 +138,11 @@ class Quickstart { /** * Get mutation variables from quickstart config + * + * @deprecated This function should be removed once we have finished our new build publishing pipeline + * * @returns - Promised mutation variables for quickstart + * */ async getMutationVariables( dryRun: boolean @@ -198,6 +206,58 @@ class Quickstart { }; } + /** + * Method extracts criteria from the config and returns an object appropriately + * structured for the artifact. + */ + public transformForArtifact(): ArtifactQuickstartConfig { + const { + description, + title, + slug, + documentation, + icon, + keywords, + summary, + dataSourceIds, + id, + level, + authors = [], + dashboards = [], + alertPolicies = [], + } = this.config; + + const metadata = { + quickstartUuid: id, + description: description && description.trim(), + displayName: title && title.trim(), + slug: slug && slug.trim(), + documentation: + documentation && + documentation.map((doc) => ({ + displayName: doc.name, + url: doc.url, + description: doc.description, + })), + iconUrl: this._constructIconUrl(icon), + keywords: keywords ?? [], + sourceUrl: Component.getAssetSourceUrl( + Component.removeBasePath(path.dirname(this.configPath), this.basePath) + ), + summary: summary && summary.trim(), + supportLevel: SUPPORT_LEVEL_ENUMS[ + level + ]?.toLowerCase() as ArtifactQuickstartConfigSupportLevel, + dataSourceIds: dataSourceIds, + alertConditions: alertPolicies, + dashboards, + authors, + }; + + + return metadata; + } + public async submitMutation(dryRun = true) { logger.info(`Submitting mutation for ${this.identifier}`, { dryRun }); const { data, errors } = await fetchNRGraphqlResults< diff --git a/utils/schema/artifact.json b/utils/schema/artifact.json index 0114436fe4..fb9d5a9674 100644 --- a/utils/schema/artifact.json +++ b/utils/schema/artifact.json @@ -1,27 +1,40 @@ { "$schema": "http://json-schema.org/draft-07/schema", - "type": "object", "properties": { "quickstarts": { "type": "array", - "items": { "$ref": "#/definitions/quickstart" } + "items": { + "$ref": "#/definitions/quickstart" + } }, "dataSources": { "type": "array", - "items": { "$ref": "#/definitions/dataSource" } + "items": { + "$ref": "#/definitions/dataSource" + } }, "alerts": { - "type": "array", - "items": { "$ref": "#/definitions/alert" } + "type": "object", + "patternProperties": { + ".*": { + "$ref": "#/definitions/alert" + } + } }, "dashboards": { - "type": "array", - "items": { "$ref": "#/definitions/dashboard" } + "type": "object", + "patternProperties": { + ".*": { + "$ref": "#/definitions/dashboard" + } + } }, "dataSourceIds": { "type": "array", - "items": { "$ref": "#/definitions/dataSourceIds" } + "items": { + "$ref": "#/definitions/dataSourceIds" + } } }, "required": [ @@ -32,178 +45,576 @@ "dataSourceIds" ], "additionalProperties": false, - "definitions": { "quickstart": { "type": "object", "properties": { - "id": { "type": "string" }, - "description": { "type": "string" }, - "summary": { "type": "string" }, - "title": { "type": "string" }, + "quickstartUuid": { + "type": "string" + }, + "description": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "displayName": { + "type": "string" + }, "authors": { "type": "array", - "items": { "type": "string" } + "items": { + "type": "string" + } + }, + "sourceUrl": { + "type": "string" }, "documentation": { "type": "array", "items": { "type": "object", "properties": { - "name": { "type": "string" }, - "description": { "type": "string" }, - "url": { "type": "string" } + "displayName": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } }, - "required": ["name", "description", "url"], + "required": ["displayName", "description", "url"], "additionalProperties": false } }, - "level": { "enum": ["New Relic", "Community", "Verified"] }, - "icon": { "type": "string" }, + "supportLevel": { + "enum": ["new_relic", "community", "verified", "enterprise"] + }, + "iconUrl": { + "type": "string" + }, "keywords": { "type": "array", - "items": { "type": "string" } + "items": { + "type": "string" + } }, - "slug": { "type": "string" }, - "alertPolicies": { + "slug": { + "type": "string" + }, + "alertConditions": { "type": "array", - "items": { "type": "string" } + "items": { + "type": "string" + } }, "dashboards": { "type": "array", - "items": { "type": "string" } + "items": { + "type": "string" + } }, "dataSourceIds": { "type": "array", - "items": { "$ref": "#/definitions/dataSourceIds" } + "items": { + "$ref": "#/definitions/dataSourceIds" + } } }, "required": [ "description", "summary", - "title", + "displayName", "authors", - "icon" + "iconUrl" ], "additionalProperties": false }, - "dataSource": { "type": "object", "properties": { - "id": { "type": "string" }, - "displayName": { "type": "string" }, - "description": { "type": "string" }, + "id": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "description": { + "type": "string" + }, "install": { "type": "object", "properties": { - "primary": { "$ref": "#/definitions/installDirective" }, - "fallback": { "$ref": "#/definitions/installDirective" } + "primary": { + "$ref": "#/definitions/installDirective" + }, + "fallback": { + "$ref": "#/definitions/installDirective" + } }, "required": ["primary"], "additionalProperties": false }, "keywords": { - "type": "array", "items": { "type": "string" } + "type": "array", + "items": { + "type": "string" + } }, "categoryTerms": { - "type": "array", "items": { "type": "string" } + "type": "array", + "items": { + "type": "string" + } }, - "icon": { "type": "string" } + "iconUrl": { + "type": "string" + } }, "required": ["id", "displayName", "install"], "additionalProperties": false }, - "installDirective": { "type": "object", "oneOf": [ { "type": "object", "properties": { - "link": { - "type": "object", - "properties": { - "url": { "type": "string" } - }, - "required": ["url"], - "additionalProperties": false + "url": { + "type": "string" } }, - "required": ["link"], + "required": ["url"], "additionalProperties": false }, { "type": "object", "properties": { - "nerdlet": { - "type": "object", - "properties": { - "nerdletId": { "type": "string" }, - "nerdletState": { "type": "object" }, - "requiresAccount": { "type": "boolean" } - }, - "required": ["nerdletId", "requiresAccount"], - "additionalProperties": false + "nerdletId": { + "type": "string" + }, + "nerdletState": { + "type": "object" + }, + "requiresAccount": { + "type": "boolean" } }, - "required": ["nerdlet"], + "required": ["nerdletId", "requiresAccount"], "additionalProperties": false } ] }, - "alert": { "type": "array", "items": { "type": "object", "properties": { - "name": { "type": "string" }, - "description": { "type": "string", "nullable": true }, - "type": { "enum": ["BASELINE", "STATIC"], "type": "string", "nullable": true }, - "nrql": { - "type": "object", - "properties": { - "query": { "type": "string" } - }, - "required": ["query"], - "additionalProperties": true + "type": { + "enum": ["baseline", "static"], + "type": "string", + "nullable": true }, - "runbookUrl": { "type": "string", "nullable": true }, - "violationTimeLimitSeconds": { "type": "number" }, - "enabled": { "type": "boolean" }, - "terms": { + "displayName": { + "type": "string" + }, + "description": { + "type": ["string", "null"] + }, + "rawConfiguration": { + "type": "string" + }, + "sourceUrl": { + "type": "string" + }, + "screenshots": { "type": "array", - "minItems": 1, "items": { "type": "object", "properties": { - "duration": { "type": "number", "minimum": 5, "maximum": 120 }, - "priority": { "enum": ["CRITICAL", "WARNING"] }, - "operator": { "enum": ["ABOVE", "BELOW", "EQUALS"] }, - "threshold": { "type": "number", "minimum": 0 }, - "thresholdDuration": { "type": "number", "minimum": 0 }, - "thresholdOccurances": { "enum": ["ALL", "AT_LEAST_ONCE"] } + "url": { + "type": "string" + } }, - "additionalProperties": true + "required": ["url"] } } }, - "required": ["name", "description", "type"], - "additionalProperties": true + "required": ["displayName", "rawConfiguration"], + "additionalProperties": false } }, - "dashboard": { "type": "object", "properties": { - "name": { "type": "string" }, - "pages": { "type": "array", "minItems": 1 }, - "description": { "type": ["string", "null"] }, - "variables": { "type": "array" } + "displayName": { + "type": "string" + }, + "description": { + "type": ["string", "null"] + }, + "rawConfiguration": { + "type": "string" + }, + "sourceUrl": { + "type": "string" + }, + "screenshots": { + "type": "array", + "items": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] + } + } }, - "required": ["name", "pages"], + "required": ["displayName", "rawConfiguration"], "additionalProperties": false + }, + "dataSourceIds": { + "enum": [ + "active-directory", + "ads-web-gpt", + "ads-web-prebid", + "aerospike", + "akamai", + "alert-quality-management", + "amazon-cloudwatch-metric-streams", + "amazon-eks-on-aws-fargate", + "ansible-automation-controller", + "apache-druid", + "apache-flink", + "apache-hadoop", + "apache-mesos", + "apache-traffic-server", + "apache-zookeeper", + "apdex-optimizer", + "apigee-api", + "apm-signals", + "argocd", + "atlassian-jira", + "audit-events-analysis", + "aws-cloudfront-logs", + "azure-monitor", + "azure-virtual-machines", + "battlesnake", + "bitbucket", + "third-party-biztalk360", + "blameless", + "blazemeter", + "blazor-webassembly", + "boomi", + "browser-segment-investigation", + "browser-signals", + "cacti", + "camel", + "catchpoint-quickstart", + "circleci", + "cloud-spanner", + "cloudflare", + "contentsquare", + "conviva", + "cribl", + "customer-experience-bottom-funnel-analysis", + "customer-experience-quality-foundation", + "dapr", + "databricks", + "datazoom", + "dbmarlin", + "dbt-cloud", + "deeper-network", + "delphix", + "docker-otel", + "dojo", + "elasticsearch-query", + "elixir", + "email-notifications", + "envoy", + "event-api", + "fastly", + "fivetran", + "flutter-android", + "flutter-ios", + "flutter-web", + "full-story", + "gatsby-build-newrelic", + "gcp-pubsub", + "gh-gates", + "gigamon-appinsights", + "github-repo", + "gitlab-integration", + "glassbox", + "grafana-dashboard-migration", + "grafana-prometheus-integration", + "gridgain-data-source", + "hardware-sentry", + "hcp-consul-otel", + "hcp-consul", + "hcp-envoy", + "hcp-vault-logs", + "hcp-vault-metrics", + "hivemq-integration-docs", + "ibmmq-integration-docs", + "infrastructure-signals", + "jdbc-executebatch", + "otel-jenkins-integration", + "jfrog-platform-cloud", + "jfrog-platform", + "jira-errors-inbox", + "k6-prometheus", + "k6", + "kentik", + "lacework", + "lambdatest", + "langchain-vectordb", + "langchain", + "launchdarkly", + "lighthouse", + "lighttpd", + "linkerd", + "logs-api", + "logs-default", + "metric-api", + "mobile-getting-started", + "mobile-network-performance-install", + "mobile-signals", + "mongodb-prometheus-integration-docs", + "mule-esb", + "mux-video", + "netlify-builds", + "netlify-logs", + "netscaler", + "network-data-ingest-and-cardinality", + "network-routers-and-switches", + "network-syslog", + "newrelic-cli", + "nextcloud", + "nr-reports", + "nuxtjs", + "nvidia-dcgm", + "nvidia-gpu", + "nvidia-jetson", + "nvidia-triton", + "nvml", + "ocsf", + "okhttp", + "onepane", + "opencensus", + "openstack-controller", + "opslevel", + "pagerduty", + "perfmon", + "pihole", + "port-monitoring", + "postfix", + "postman", + "pulumi", + "quantum-metric", + "rafay", + "ray", + "redis-enterprise", + "redis-otel", + "redmine", + "redpanda", + "releaseiq", + "newrelic-java-rmi", + "roku-http-analytics", + "roku", + "salesforce-eventlog-for-logs", + "sendgrid-integration", + "sendmail", + "servicenow-notifications", + "shopify-hydrogen", + "shopify", + "signl4", + "singlestore", + "skykit", + "slack-notifications", + "snowflake", + "solutions-hub-dashboards", + "sonarqube", + "speedscale", + "split", + "squid-prometheus-integration-docs", + "stripe", + "sybase-integration", + "synthetics-private-locations", + "telemetry-data-platform", + "temporal-cloud", + "temporal", + "terraform", + "tidbcloud", + "trace-and-span-api", + "traceloop", + "trendmicro-cloudone-conformity", + "tvos-mobile", + "unix", + "vercel", + "vertica", + "newrelic-java-vertx", + "newrelic-java-vertx-extensions", + "video-android", + "video-chromecast", + "video-ios-tvos", + "video-web-akamai", + "video-web-html5", + "video-web-jwplayer", + "video-web-theplatform", + "video-web-videojs", + "webhook-notifications", + "windows-certs", + "winservices-integration-docs", + "xmatters", + "zebrium", + "zenduty", + "zipkin", + "activemq", + "amazon-cloudwatch", + "amazon-kinesis", + "amazon-linux", + "amazon-linux-logs", + "amazon-s3", + "amazon-sagemaker", + "amazon-web-services", + "android", + "angular", + "angular-js", + "ansible-install", + "apache", + "apm-logs", + "aporia", + "aws-firelens", + "aws-lambda", + "aws-migrate-metricStream", + "aws-security-hub", + "aws-vpc-flow-logs", + "azure-openai", + "backbone", + "bring-your-own-data", + "capacitor", + "cassandra", + "centos", + "centos-logs", + "change-tracking", + "chef-install", + "comet", + "confluent-cloud", + "consul", + "cordova", + "couchbase", + "dagshub", + "debian", + "debian-logs", + "dependabot", + "distributed-tracing", + "docker", + "dropwizard", + "elasticsearch", + "ember", + "f5", + "fluent-bit", + "fluentd", + "flutter", + "gatsby", + "gcp-vpc-flow-logs", + "golang", + "google-cloud-platform", + "google-cloud-platform-logs", + "govcloud", + "guided-install", + "haproxy", + "headerless-log-api", + "heroku", + "heroku-http", + "ios", + "java", + "java-logs", + "jmx", + "jquery", + "kafka", + "kamon", + "kubernetes", + "kubernetes-logs", + "linux", + "linux-infra", + "linux-logs", + "llm-application", + "logstash", + "macos", + "maui", + "memcached", + "micrometer", + "microsoft-azure", + "microsoft-azure-blob-storage", + "microsoft-azure-event-hub", + "microsoft-sql-server", + "microsoftnet", + "microsoftnet-logs", + "mona", + "mongodb", + "mysql", + "nagios", + "network-devices", + "network-flows", + "network-synthetics", + "new-relic-browser", + "new-relic-infrastructure-agent", + "new-relic-synthetics", + "next-js", + "nginx", + "node-js", + "nr-ai-monitoring", + "nvidia-nim", + "opentelemetry", + "oracle-database", + "php", + "pixie", + "postgresql", + "powerdns", + "prometheus", + "prometheus-agent-mode", + "puppet-install", + "python", + "rabbitmq", + "react", + "react-native", + "redis", + "rhel", + "rhel-logs", + "ruby", + "ruby-logs", + "security-api", + "sles", + "sles-logs", + "snmp", + "snyk", + "statsd", + "super-agent", + "syslog", + "trivy", + "truera", + "ubuntu", + "ubuntu-logs", + "unity", + "unreal", + "varnish", + "vmware-tanzu", + "vmware-vsphere", + "vue", + "windows", + "windows-infra", + "windows-logs", + "xamarin", + "xcframework", + "zepto" + ] } } } + diff --git a/utils/types/Artifact.ts b/utils/types/Artifact.ts new file mode 100644 index 0000000000..88fd0660d7 --- /dev/null +++ b/utils/types/Artifact.ts @@ -0,0 +1,100 @@ +/* -- Data source -- */ + +export interface ArtifactDataSourceConfig { + id: string; + displayName: string; + description?: string; + install: ArtifactInstallDirective; + keywords?: string[]; + categoryTerms?: string[]; + iconUrl?: string; +} + +export type ArtifactInstallDirective = { + primary: ArtifactInstall; + fallback?: ArtifactInstall; +}; + +export type ArtifactInstall = + | ArtifactDataSourceConfigNerdletDirective + | ArtifactDataSourceConfigLinkDirective; + +interface ArtifactDataSourceConfigNerdletDirective { + nerdletId: string; + nerdletState: Record; + requiresAccount: boolean; +} + +interface ArtifactDataSourceConfigLinkDirective { + url: string; +} + +interface ArtifactDataSourceConfigNerdletDirective { + nerdletId: string; + nerdletState: Record; + requiresAccount: boolean; +} + +/* -- Quickstart -- */ + +type QuickstartConfigDocumentation = { + displayName: string; + description: string; + url: string; +}; + +export type ArtifactQuickstartConfigSupportLevel = + | 'new_relic' + | 'community' + | 'verified' + // Enterprise is deprecated. However some quickstarts still have this support + // level within their config. + | 'enterprise'; + +export interface ArtifactQuickstartConfig { + quickstartUuid: string; + description: string; + displayName: string; + slug?: string; + documentation: QuickstartConfigDocumentation[]; + iconUrl: string; + keywords?: string[]; + summary: string; + supportLevel: ArtifactQuickstartConfigSupportLevel; + alertConditions?: string[]; + dashboards?: string[]; + dataSourceIds?: string[]; + authors: string[]; +}; + +/* -- Dashboard -- */ + +type DashboardScreenshot = { + url: string; +}; + +export interface ArtifactDashboardConfig { + [id: string]: { + description?: string; + displayName: string; + rawConfiguration: string; + sourceUrl?: string; + screenshots?: DashboardScreenshot[]; + }; +} + +/* --- Alert --- */ + +export type ArtifactAlertType = 'baseline' | 'static'; + +type ArtifactAlert = { + description?: string; + displayName: string; + rawConfiguration: string; + sourceUrl?: string; + type: ArtifactAlertType; +}; + +export interface ArtifactAlertConfig { + [id: string]: ArtifactAlert[]; +}