Skip to content

Commit

Permalink
[Fleet] Add custom integrations API (elastic#112481) (elastic#113136)
Browse files Browse the repository at this point in the history
Add a new plugin `custom_integrations`. This plugin allows for the registration of data-integrations tutorials. The Fleet-integrations app will display these alongside the existing Elastic Agent integrations.
# Conflicts:
#	packages/kbn-optimizer/limits.yml
  • Loading branch information
thomasneirynck authored Sep 27, 2021
1 parent 0de0307 commit ec7231d
Show file tree
Hide file tree
Showing 51 changed files with 1,100 additions and 171 deletions.
4 changes: 4 additions & 0 deletions docs/developer/plugin-list.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ NOTE:
|Console provides the user with tools for storing and executing requests against Elasticsearch.
|{kib-repo}blob/{branch}/src/plugins/custom_integrations/README.md[customIntegrations]
|Register add-data cards
|<<kibana-dashboard-plugin>>
|- Registers the dashboard application.
- Adds a dashboard embeddable that can be used in other applications.
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,4 @@ pageLoadAssetSize:
securitySolution: 232514
share: 100298
snapshotRestore: 63225
customIntegrations: 28810
9 changes: 9 additions & 0 deletions src/plugins/custom_integrations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# customIntegrations

Register add-data cards

---

## Development

See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment.
61 changes: 61 additions & 0 deletions src/plugins/custom_integrations/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export const PLUGIN_ID = 'customIntegrations';
export const PLUGIN_NAME = 'customIntegrations';

export interface CategoryCount {
count: number;
id: Category;
}

export const CATEGORY_DISPLAY = {
aws: 'AWS',
azure: 'Azure',
cloud: 'Cloud',
config_management: 'Config management',
containers: 'Containers',
crm: 'CRM',
custom: 'Custom',
datastore: 'Datastore',
elastic_stack: 'Elastic Stack',
google_cloud: 'Google cloud',
kubernetes: 'Kubernetes',
languages: 'Languages',
message_queue: 'Message queue',
monitoring: 'Monitoring',
network: 'Network',
notification: 'Notification',
os_system: 'OS & System',
productivity: 'Productivity',
security: 'Security',
sample_data: 'Sample data',
support: 'Support',
ticketing: 'Ticketing',
version_control: 'Version control',
web: 'Web',
upload_file: 'Upload a file',

updates_available: 'Updates available',
};

export type Category = keyof typeof CATEGORY_DISPLAY;

export interface CustomIntegration {
id: string;
title: string;
description: string;
type: 'ui_link';
uiInternalPath: string;
isBeta: boolean;
icons: Array<{ src: string; type: string }>;
categories: Category[];
shipper: string;
}

export const ROUTES_ADDABLECUSTOMINTEGRATIONS = `/api/${PLUGIN_ID}/appendCustomIntegrations`;
17 changes: 17 additions & 0 deletions src/plugins/custom_integrations/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/src/plugins/custom_integrations'],
testRunner: 'jasmine2',
coverageDirectory: '<rootDir>/target/kibana-coverage/jest/src/plugins/custom_integrations',
coverageReporters: ['text', 'html'],
collectCoverageFrom: ['<rootDir>/src/plugins/data/{common,public,server}/**/*.{ts,tsx}'],
};
16 changes: 16 additions & 0 deletions src/plugins/custom_integrations/kibana.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"id": "customIntegrations",
"version": "1.0.0",
"kibanaVersion": "kibana",
"owner": {
"name": "Fleet",
"githubTeam": "fleet"
},
"description": "Add custom data integrations so they can be displayed in the Fleet integrations app",
"ui": true,
"server": true,
"extraPublicDirs": [
"common"
],
"optionalPlugins": []
}
16 changes: 16 additions & 0 deletions src/plugins/custom_integrations/public/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { CustomIntegrationsPlugin } from './plugin';

// This exports static code and TypeScript types,
// as well as, Kibana Platform `plugin()` initializer.
export function plugin() {
return new CustomIntegrationsPlugin();
}
export { CustomIntegrationsSetup, CustomIntegrationsStart } from './types';
21 changes: 21 additions & 0 deletions src/plugins/custom_integrations/public/mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { CustomIntegrationsSetup } from './types';

function createCustomIntegrationsSetup(): jest.Mocked<CustomIntegrationsSetup> {
const mock = {
getAppendCustomIntegrations: jest.fn(),
};

return mock;
}

export const customIntegrationsMock = {
createSetup: createCustomIntegrationsSetup,
};
28 changes: 28 additions & 0 deletions src/plugins/custom_integrations/public/plugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { CustomIntegrationsPlugin } from './plugin';

import { coreMock } from '../../../core/public/mocks';

describe('CustomIntegrationsPlugin', () => {
beforeEach(() => {});

describe('setup', () => {
let mockCoreSetup: ReturnType<typeof coreMock.createSetup>;

beforeEach(() => {
mockCoreSetup = coreMock.createSetup();
});

test('wires up tutorials provider service and returns registerTutorial and addScopedTutorialContextFactory', () => {
const setup = new CustomIntegrationsPlugin().setup(mockCoreSetup);
expect(setup).toHaveProperty('getAppendCustomIntegrations');
});
});
});
30 changes: 30 additions & 0 deletions src/plugins/custom_integrations/public/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { CoreSetup, CoreStart, Plugin } from 'src/core/public';
import { CustomIntegrationsSetup, CustomIntegrationsStart } from './types';
import { CustomIntegration, ROUTES_ADDABLECUSTOMINTEGRATIONS } from '../common';

export class CustomIntegrationsPlugin
implements Plugin<CustomIntegrationsSetup, CustomIntegrationsStart>
{
public setup(core: CoreSetup): CustomIntegrationsSetup {
// Return methods that should be available to other plugins
return {
async getAppendCustomIntegrations(): Promise<CustomIntegration[]> {
return core.http.get(ROUTES_ADDABLECUSTOMINTEGRATIONS);
},
} as CustomIntegrationsSetup;
}

public start(core: CoreStart): CustomIntegrationsStart {
return {};
}

public stop() {}
}
18 changes: 18 additions & 0 deletions src/plugins/custom_integrations/public/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { CustomIntegration } from '../common';

export interface CustomIntegrationsSetup {
getAppendCustomIntegrations: () => Promise<CustomIntegration[]>;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface CustomIntegrationsStart {}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface AppPluginStartDependencies {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { CustomIntegrationRegistry } from './custom_integration_registry';
import { loggerMock, MockedLogger } from '@kbn/logging/mocks';
import { CustomIntegration } from '../common';

describe('CustomIntegrationsRegistry', () => {
let mockLogger: MockedLogger;

const integration: CustomIntegration = {
id: 'foo',
title: 'Foo',
description: 'test integration',
type: 'ui_link',
uiInternalPath: '/path/to/foo',
isBeta: false,
icons: [],
categories: ['upload_file'],
shipper: 'tests',
};

beforeEach(() => {
mockLogger = loggerMock.create();
});

describe('register', () => {
describe('should log to console on duplicate id', () => {
test('with an error in dev', () => {
const registry = new CustomIntegrationRegistry(mockLogger, true);
registry.registerCustomIntegration(integration);
registry.registerCustomIntegration(integration);
expect(mockLogger.error.mock.calls.length).toBe(1);
});
test('with a debug in prod', () => {
const registry = new CustomIntegrationRegistry(mockLogger, false);
registry.registerCustomIntegration(integration);
registry.registerCustomIntegration(integration);
expect(mockLogger.debug.mock.calls.length).toBe(1);
});
});
});

describe('getAppendCustomCategories', () => {
test('should return', () => {
const registry = new CustomIntegrationRegistry(mockLogger, true);
registry.registerCustomIntegration(integration);
registry.registerCustomIntegration({ ...integration, id: 'bar' });
expect(registry.getAppendCustomIntegrations()).toEqual([
{
categories: ['upload_file'],
description: 'test integration',
icons: [],
id: 'foo',
isBeta: false,
shipper: 'tests',
title: 'Foo',
type: 'ui_link',
uiInternalPath: '/path/to/foo',
},
{
categories: ['upload_file'],
description: 'test integration',
icons: [],
id: 'bar',
isBeta: false,
shipper: 'tests',
title: 'Foo',
type: 'ui_link',
uiInternalPath: '/path/to/foo',
},
]);
});
test('should ignore duplicate ids', () => {
const registry = new CustomIntegrationRegistry(mockLogger, true);
registry.registerCustomIntegration(integration);
registry.registerCustomIntegration(integration);
expect(registry.getAppendCustomIntegrations()).toEqual([
{
categories: ['upload_file'],
description: 'test integration',
icons: [],
id: 'foo',
isBeta: false,
shipper: 'tests',
title: 'Foo',
type: 'ui_link',
uiInternalPath: '/path/to/foo',
},
]);
});
test('should ignore integrations without category', () => {
const registry = new CustomIntegrationRegistry(mockLogger, true);
registry.registerCustomIntegration(integration);
registry.registerCustomIntegration({ ...integration, id: 'bar', categories: [] });

expect(registry.getAppendCustomIntegrations()).toEqual([
{
categories: ['upload_file'],
description: 'test integration',
icons: [],
id: 'foo',
isBeta: false,
shipper: 'tests',
title: 'Foo',
type: 'ui_link',
uiInternalPath: '/path/to/foo',
},
]);
});
});
});
Loading

0 comments on commit ec7231d

Please sign in to comment.