diff --git a/server/adaptors/integrations/__data__/repository/aws_elb/aws_elb-1.0.0.json b/server/adaptors/integrations/__data__/repository/aws_elb/aws_elb-1.0.0.json index e71831b52..de26372ac 100644 --- a/server/adaptors/integrations/__data__/repository/aws_elb/aws_elb-1.0.0.json +++ b/server/adaptors/integrations/__data__/repository/aws_elb/aws_elb-1.0.0.json @@ -16,10 +16,6 @@ { "annotation": "ELB Dashboard", "path": "dashboard1.png" - }, - { - "annotation": "ELB Logo", - "path": "logo.png" } ] }, diff --git a/server/adaptors/integrations/integrations_adaptor.ts b/server/adaptors/integrations/integrations_adaptor.ts index 14908bf1a..cf7f4853e 100644 --- a/server/adaptors/integrations/integrations_adaptor.ts +++ b/server/adaptors/integrations/integrations_adaptor.ts @@ -29,4 +29,6 @@ export interface IntegrationsAdaptor { ) => Promise<{ mappings: { [key: string]: unknown }; schemas: { [key: string]: unknown } }>; getAssets: (templateName: string) => Promise<{ savedObjects?: unknown }>; + + getSampleData: (templateName: string) => Promise<{ sampleData: object[] | null }>; } diff --git a/server/adaptors/integrations/integrations_kibana_backend.ts b/server/adaptors/integrations/integrations_kibana_backend.ts index c7da2c49f..1ac61880c 100644 --- a/server/adaptors/integrations/integrations_kibana_backend.ts +++ b/server/adaptors/integrations/integrations_kibana_backend.ts @@ -183,4 +183,15 @@ export class IntegrationsKibanaBackend implements IntegrationsAdaptor { } return Promise.resolve(integration.getAssets()); }; + + getSampleData = async (templateName: string): Promise<{ sampleData: object[] | null }> => { + const integration = await this.repository.getIntegration(templateName); + if (integration === null) { + return Promise.reject({ + message: `Template ${templateName} not found`, + statusCode: 404, + }); + } + return Promise.resolve(integration.getSampleData()); + }; } diff --git a/server/adaptors/integrations/repository/integration.ts b/server/adaptors/integrations/repository/integration.ts index e14d5c069..24388eff1 100644 --- a/server/adaptors/integrations/repository/integration.ts +++ b/server/adaptors/integrations/repository/integration.ts @@ -221,6 +221,50 @@ export class Integration { return result; } + /** + * Retrieve sample data associated with the integration. + * If the version is invalid, an error is thrown. + * If the sample data is invalid, null will be returned + * + * @param version The version of the integration to retrieve assets for. + * @returns An object containing a list of sample data with adjusted timestamps. + */ + async getSampleData( + version?: string + ): Promise<{ + sampleData: object[] | null; + }> { + const config = await this.getConfig(version); + if (config === null) { + return Promise.reject(new Error('Attempted to get assets of invalid config')); + } + const result: { sampleData: object[] | null } = { sampleData: null }; + if (config.sampleData) { + const sobjPath = path.join(this.directory, 'data', config.sampleData?.path); + try { + const jsonContent = await fs.readFile(sobjPath, { encoding: 'utf-8' }); + const parsed = JSON.parse(jsonContent) as object[]; + for (const value of parsed) { + if (!('@timestamp' in value)) { + continue; + } + // Randomly scatter timestamps across last 10 minutes + // Assume for now that the ordering of events isn't important, can change to a sequence if needed + // Also doesn't handle fields like `observedTimestamp` if present + Object.assign(value, { + '@timestamp': new Date( + Date.now() - Math.floor(Math.random() * 1000 * 60 * 10) + ).toISOString(), + }); + } + result.sampleData = parsed; + } catch (err: any) { + console.error("Failed to load saved object assets, proceeding as if it's absent", err); + } + } + return result; + } + /** * Retrieve schema data associated with the integration. * This method greedily retrieves all mappings and schemas. diff --git a/server/routes/integrations/integrations_router.ts b/server/routes/integrations/integrations_router.ts index 74b36678a..5a4813127 100644 --- a/server/routes/integrations/integrations_router.ts +++ b/server/routes/integrations/integrations_router.ts @@ -184,6 +184,23 @@ export function registerIntegrationsRoute(router: IRouter) { } ); + router.get( + { + path: `${INTEGRATIONS_BASE}/repository/{id}/data`, + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + async (context, request, response): Promise => { + const adaptor = getAdaptor(context, request); + return handleWithCallback(adaptor, response, async (a: IntegrationsAdaptor) => + a.getSampleData(request.params.id) + ); + } + ); + router.get( { path: `${INTEGRATIONS_BASE}/store`,