Skip to content

Commit

Permalink
[Multiple Datasource][Version Decoupling] Add support of required bac…
Browse files Browse the repository at this point in the history
…kend plugins check on data sources (#7146)

* [Multiple Datasource][Version Decoupling] Add support of required backend plugins check on data sources

Signed-off-by: Zilong Xia <zilongx@amazon.com>

* Changeset file for PR #7146 created/updated

---------

Signed-off-by: Zilong Xia <zilongx@amazon.com>
Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
  • Loading branch information
ZilongX and opensearch-changeset-bot[bot] committed Jul 2, 2024
1 parent 5e19749 commit daccab7
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 10 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/7146.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fix:
- [MDS][Version Decoupling] Add support of required backend plugins check on data sources ([#7146](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7146))
17 changes: 16 additions & 1 deletion src/core/server/plugins/discovery/plugin_manifest_parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ describe('requiredEnginePlugins', () => {
requiredBundles: [],
server: true,
ui: false,
supportedOSDataSourceVersions: '',
requiredOSDataSourcePlugins: [],
});
});
});
Expand Down Expand Up @@ -416,7 +418,8 @@ test('set defaults for all missing optional fields', async () => {
requiredBundles: [],
server: true,
ui: false,
supportedOSDataSourceVersions: undefined,
supportedOSDataSourceVersions: '',
requiredOSDataSourcePlugins: [],
});
});

Expand All @@ -436,6 +439,10 @@ test('return all set optional fields as they are in manifest', async () => {
},
ui: true,
supportedOSDataSourceVersions: '>=1.0.0',
requiredOSDataSourcePlugins: [
'some-required-data-source-plugin-1',
'some-required-data-source-plugin-2',
],
})
)
);
Expand All @@ -455,6 +462,10 @@ test('return all set optional fields as they are in manifest', async () => {
server: false,
ui: true,
supportedOSDataSourceVersions: '>=1.0.0',
requiredOSDataSourcePlugins: [
'some-required-data-source-plugin-1',
'some-required-data-source-plugin-2',
],
});
});

Expand Down Expand Up @@ -484,6 +495,8 @@ test('return manifest when plugin expected OpenSearch Dashboards version matches
requiredBundles: [],
server: true,
ui: false,
supportedOSDataSourceVersions: '',
requiredOSDataSourcePlugins: [],
});
});

Expand Down Expand Up @@ -512,5 +525,7 @@ test('return manifest when plugin expected OpenSearch Dashboards version is `ope
requiredBundles: [],
server: true,
ui: true,
supportedOSDataSourceVersions: '',
requiredOSDataSourcePlugins: [],
});
});
9 changes: 8 additions & 1 deletion src/core/server/plugins/discovery/plugin_manifest_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const KNOWN_MANIFEST_FIELDS = (() => {
extraPublicDirs: true,
requiredBundles: true,
supportedOSDataSourceVersions: true,
requiredOSDataSourcePlugins: true,
};

return new Set(Object.keys(manifestFields));
Expand Down Expand Up @@ -246,7 +247,13 @@ export async function parseManifest(
ui: includesUiPlugin,
server: includesServerPlugin,
extraPublicDirs: manifest.extraPublicDirs,
supportedOSDataSourceVersions: manifest.supportedOSDataSourceVersions,
supportedOSDataSourceVersions:
manifest.supportedOSDataSourceVersions !== undefined
? manifest.supportedOSDataSourceVersions
: '',
requiredOSDataSourcePlugins: Array.isArray(manifest.requiredOSDataSourcePlugins)
? manifest.requiredOSDataSourcePlugins
: [],
};
}

Expand Down
2 changes: 2 additions & 0 deletions src/core/server/plugins/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ function createPluginManifest(manifestProps: Partial<PluginManifest> = {}): Plug
server: true,
ui: true,
supportedOSDataSourceVersions: '>=1.0.0',
requiredOSDataSourcePlugins: ['some-required-data-source-plugin'],
...manifestProps,
};
}
Expand Down Expand Up @@ -127,6 +128,7 @@ test('`constructor` correctly initializes plugin instance', () => {
expect(plugin.requiredPlugins).toEqual(['some-required-dep']);
expect(plugin.optionalPlugins).toEqual(['some-optional-dep']);
expect(plugin.supportedOSDataSourceVersions).toEqual('>=1.0.0');
expect(plugin.requiredOSDataSourcePlugins).toEqual(['some-required-data-source-plugin']);
});

test('`setup` fails if `plugin` initializer is not exported', async () => {
Expand Down
2 changes: 2 additions & 0 deletions src/core/server/plugins/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export class PluginWrapper<
public readonly includesServerPlugin: PluginManifest['server'];
public readonly includesUiPlugin: PluginManifest['ui'];
public readonly supportedOSDataSourceVersions: PluginManifest['supportedOSDataSourceVersions'];
public readonly requiredOSDataSourcePlugins: PluginManifest['requiredOSDataSourcePlugins'];

private readonly log: Logger;
private readonly initializerContext: PluginInitializerContext;
Expand Down Expand Up @@ -102,6 +103,7 @@ export class PluginWrapper<
this.includesServerPlugin = params.manifest.server;
this.includesUiPlugin = params.manifest.ui;
this.supportedOSDataSourceVersions = params.manifest.supportedOSDataSourceVersions;
this.requiredOSDataSourcePlugins = params.manifest.requiredOSDataSourcePlugins;
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/core/server/plugins/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,12 @@ export interface PluginManifest {
* Value will be following semver as a range for example ">= 1.3.0"
*/
readonly supportedOSDataSourceVersions?: string;

/**
* Specifies the required backend plugins that **must be** installed and enabled on the data source for this plugin to function properly
* when adding OpenSearch cluster as data sources.
*/
readonly requiredOSDataSourcePlugins?: readonly PluginName[];
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
*/

import React from 'react';
import { Header } from '../header';
import { render } from '@testing-library/react';
import { Header, useEffectOnce } from '../header';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';

jest.mock('../../../../../../../../../plugins/opensearch_dashboards_react/public', () => ({
Expand All @@ -15,6 +16,13 @@ jest.mock('../../../../../../../../../plugins/opensearch_dashboards_react/public
}),
}));

const mockGetDataSourcesCompatible = jest.fn(() =>
Promise.resolve([{ id: 1, attributes: { title: '213', dataSourceVersion: '2.13.0' } }])
);
const mockGetDataSourcesNotCompatible = jest.fn(() =>
Promise.resolve([{ id: 1, attributes: { title: '010', dataSourceVersion: '0.1.0' } }])
);

afterAll(() => jest.clearAllMocks());

describe('Header', () => {
Expand Down Expand Up @@ -118,4 +126,59 @@ describe('Header', () => {
.prop('isDisabled')
).toEqual(true);
});

it('should display compatible data source', () => {
const component = shallowWithIntl(
<Header
onDataSourceSelected={() => {}}
dataSourceRef={{ type: 'type', id: 'id', title: 'title' }!}
goToNextStep={() => {}}
isNextStepDisabled={true}
stepInfo={{ totalStepNumber: 0, currentStepNumber: 0 }}
hideLocalCluster={false}
getDataSources={mockGetDataSourcesCompatible}
/>
);

component
.find('[data-test-subj="createIndexPatternStepDataSourceUseDataSourceRadio"]')
.simulate('change', {
target: {
checked: true,
},
});

expect(
component
.find('[data-test-subj="createIndexPatternStepDataSourceSelectDataSource"]')
.first()
.exists()
).toBeTruthy();
});

it('should filter out incompatible data sources', () => {
const component = shallowWithIntl(
<Header
onDataSourceSelected={() => {}}
dataSourceRef={{ type: 'type', id: 'id', title: 'title' }!}
goToNextStep={() => {}}
isNextStepDisabled={true}
stepInfo={{ totalStepNumber: 0, currentStepNumber: 0 }}
hideLocalCluster={false}
getDataSources={mockGetDataSourcesNotCompatible}
/>
);

component
.find('[data-test-subj="createIndexPatternStepDataSourceUseDataSourceRadio"]')
.simulate('change', {
target: {
checked: true,
},
});

expect(
component.find('[data-test-subj="createIndexPatternStepDataSourceSelectDataSource"]').first()
).toEqual({});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,26 @@ export const Header: React.FC<HeaderProps> = (props: HeaderProps) => {
getDataSources(savedObjects.client)
.then((fetchedDataSources: DataSourceTableItem[]) => {
setIsLoading(false);

if (fetchedDataSources?.length) {
fetchedDataSources = fetchedDataSources.filter((dataSource) =>
semver.satisfies(
dataSource.datasourceversion,
pluginManifest.supportedOSDataSourceVersions
)
);
// filter out data sources which does NOT have the required backend plugins installed
if (pluginManifest.hasOwnProperty('requiredOSDataSourcePlugins')) {
fetchedDataSources = fetchedDataSources.filter((dataSource) =>
pluginManifest.requiredOSDataSourcePlugins.every((plugin) =>
dataSource.installedplugins.includes(plugin)
)
);
}

// filter out data sources which is NOT in the support range of plugin
if (pluginManifest.hasOwnProperty('supportedOSDataSourceVersions')) {
fetchedDataSources = fetchedDataSources.filter((dataSource) =>
semver.satisfies(
dataSource.datasourceversion,
pluginManifest.supportedOSDataSourceVersions
)
);
}
setDataSources(fetchedDataSources);
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,5 @@ export interface DataSourceTableItem {
checked?: 'on' | 'off';
label: string;
datasourceversion: string;
installedplugins: string[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export async function getDataSources(savedObjectsClient: SavedObjectsClientContr
savedObjectsClient
.find<DataSourceAttributes>({
type: 'data-source',
fields: ['title', 'type', 'dataSourceVersion'],
fields: ['title', 'type', 'dataSourceVersion', 'installedPlugins'],
perPage: 10000,
})
.then((response) =>
Expand All @@ -100,6 +100,7 @@ export async function getDataSources(savedObjectsClient: SavedObjectsClientContr
const type = dataSource.type;
const title = dataSource.get('title');
const datasourceversion = dataSource.get('dataSourceVersion');
const installedplugins = dataSource.get('installedPlugins');

return {
id,
Expand All @@ -108,6 +109,7 @@ export async function getDataSources(savedObjectsClient: SavedObjectsClientContr
label: title,
sort: `${title}`,
datasourceversion,
installedplugins,
};
})
.sort((a, b) => a.sort.localeCompare(b.sort))
Expand Down

0 comments on commit daccab7

Please sign in to comment.