mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Migrate SO management routes to new plugin (#59734)
* unexpose SavedObjectsManagement from legacy server * migrate saved object management routes to new plugin * fix endpoint methods * adapt code due to rebase * extract types * improve findAll params * adapt existing api integration tests and migrate to TS * update generated doc * add API integration tests for /scroll/count * add unit tests for plugin and routes * add injectMetaAttributes tests * extract relation type * add find_relationships tests * add find_all tests * do not complete migrator$ to avoid unhandled promise rejection * fix data for search endpoint integration tests * remove falsy comment * rename plugin folder to match plugin id * address review comments * update CODEOWNERS
This commit is contained in:
parent
3fe48fe8b3
commit
a7508b8f20
48 changed files with 2217 additions and 915 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -131,6 +131,7 @@
|
|||
/src/legacy/server/saved_objects/ @elastic/kibana-platform
|
||||
/src/legacy/server/status/ @elastic/kibana-platform
|
||||
/src/plugins/status_page/ @elastic/kibana-platform
|
||||
/src/plugins/saved_objects_management/ @elastic/kibana-platform
|
||||
/src/dev/run_check_published_api_changes.ts @elastic/kibana-platform
|
||||
|
||||
# Security
|
||||
|
|
|
@ -21,8 +21,6 @@ export * from './service';
|
|||
|
||||
export { SavedObjectsSchema } from './schema';
|
||||
|
||||
export { SavedObjectsManagement } from './management';
|
||||
|
||||
export * from './import';
|
||||
|
||||
export {
|
||||
|
|
|
@ -36,6 +36,7 @@ export interface SavedObjectsLegacyService {
|
|||
getScopedSavedObjectsClient: SavedObjectsClientProvider['getClient'];
|
||||
SavedObjectsClient: typeof SavedObjectsClient;
|
||||
types: string[];
|
||||
importAndExportableTypes: string[];
|
||||
schema: SavedObjectsSchema;
|
||||
getSavedObjectsRepository(...rest: any[]): any;
|
||||
importExport: {
|
||||
|
|
|
@ -2028,6 +2028,8 @@ export interface SavedObjectsLegacyService {
|
|||
// (undocumented)
|
||||
getScopedSavedObjectsClient: SavedObjectsClientProvider['getClient'];
|
||||
// (undocumented)
|
||||
importAndExportableTypes: string[];
|
||||
// (undocumented)
|
||||
importExport: {
|
||||
objectLimit: number;
|
||||
importSavedObjects(options: SavedObjectsImportOptions): Promise<SavedObjectsImportResponse>;
|
||||
|
|
|
@ -24,7 +24,6 @@ import { promisify } from 'util';
|
|||
import { migrations } from './migrations';
|
||||
import { importApi } from './server/routes/api/import';
|
||||
import { exportApi } from './server/routes/api/export';
|
||||
import { managementApi } from './server/routes/api/management';
|
||||
import mappings from './mappings.json';
|
||||
import { getUiSettingDefaults } from './ui_setting_defaults';
|
||||
import { registerCspCollector } from './server/lib/csp_usage_collector';
|
||||
|
@ -259,7 +258,6 @@ export default function(kibana) {
|
|||
// routes
|
||||
importApi(server);
|
||||
exportApi(server);
|
||||
managementApi(server);
|
||||
registerCspCollector(usageCollection, server);
|
||||
server.injectUiAppVars('kibana', () => injectVars(server));
|
||||
},
|
||||
|
|
|
@ -20,12 +20,7 @@
|
|||
export function injectVars(server) {
|
||||
const serverConfig = server.config();
|
||||
|
||||
// Get types that are import and exportable, by default yes unless isImportableAndExportable is set to false
|
||||
const { types: allTypes } = server.savedObjects;
|
||||
const savedObjectsManagement = server.getSavedObjectsManagement();
|
||||
const importAndExportableTypes = allTypes.filter(type =>
|
||||
savedObjectsManagement.isImportAndExportable(type)
|
||||
);
|
||||
const { importAndExportableTypes } = server.savedObjects;
|
||||
|
||||
return {
|
||||
importAndExportableTypes,
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file wraps the saved object `_find` API and is designed specifically for the saved object
|
||||
* management UI. The main difference is this will inject a root `meta` attribute on each saved object
|
||||
* that the UI depends on. The meta fields come from functions within uiExports which can't be
|
||||
* injected into the front end when defined within uiExports. There are alternatives to this but have
|
||||
* decided to go with this approach at the time of development.
|
||||
*/
|
||||
|
||||
import Joi from 'joi';
|
||||
import { injectMetaAttributes } from '../../../../lib/management/saved_objects/inject_meta_attributes';
|
||||
|
||||
export function registerFind(server) {
|
||||
server.route({
|
||||
path: '/api/kibana/management/saved_objects/_find',
|
||||
method: 'GET',
|
||||
config: {
|
||||
validate: {
|
||||
query: Joi.object()
|
||||
.keys({
|
||||
perPage: Joi.number()
|
||||
.min(0)
|
||||
.default(20),
|
||||
page: Joi.number()
|
||||
.min(0)
|
||||
.default(1),
|
||||
type: Joi.array()
|
||||
.items(Joi.string())
|
||||
.single()
|
||||
.required(),
|
||||
search: Joi.string()
|
||||
.allow('')
|
||||
.optional(),
|
||||
defaultSearchOperator: Joi.string()
|
||||
.valid('OR', 'AND')
|
||||
.default('OR'),
|
||||
sortField: Joi.string(),
|
||||
hasReference: Joi.object()
|
||||
.keys({
|
||||
type: Joi.string().required(),
|
||||
id: Joi.string().required(),
|
||||
})
|
||||
.optional(),
|
||||
fields: Joi.array()
|
||||
.items(Joi.string())
|
||||
.single(),
|
||||
})
|
||||
.default(),
|
||||
},
|
||||
},
|
||||
async handler(request) {
|
||||
const searchFields = new Set();
|
||||
const searchTypes = request.query.type;
|
||||
const savedObjectsClient = request.getSavedObjectsClient();
|
||||
const savedObjectsManagement = server.getSavedObjectsManagement();
|
||||
const importAndExportableTypes = searchTypes.filter(type =>
|
||||
savedObjectsManagement.isImportAndExportable(type)
|
||||
);
|
||||
|
||||
// Accumulate "defaultSearchField" attributes from savedObjectsManagement. Unfortunately
|
||||
// search fields apply to all types of saved objects, the sum of these fields will
|
||||
// be searched on for each object.
|
||||
for (const type of importAndExportableTypes) {
|
||||
const searchField = savedObjectsManagement.getDefaultSearchField(type);
|
||||
if (searchField) {
|
||||
searchFields.add(searchField);
|
||||
}
|
||||
}
|
||||
|
||||
const findResponse = await savedObjectsClient.find({
|
||||
...request.query,
|
||||
fields: undefined,
|
||||
searchFields: [...searchFields],
|
||||
});
|
||||
return {
|
||||
...findResponse,
|
||||
saved_objects: findResponse.saved_objects
|
||||
.map(obj => injectMetaAttributes(obj, savedObjectsManagement))
|
||||
.map(obj => {
|
||||
const result = { ...obj, attributes: {} };
|
||||
for (const field of request.query.fields || []) {
|
||||
result.attributes[field] = obj.attributes[field];
|
||||
}
|
||||
return result;
|
||||
}),
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import Joi from 'joi';
|
||||
import { findRelationships } from '../../../../lib/management/saved_objects/relationships';
|
||||
|
||||
export function registerRelationships(server) {
|
||||
server.route({
|
||||
path: '/api/kibana/management/saved_objects/relationships/{type}/{id}',
|
||||
method: ['GET'],
|
||||
config: {
|
||||
validate: {
|
||||
params: Joi.object().keys({
|
||||
type: Joi.string(),
|
||||
id: Joi.string(),
|
||||
}),
|
||||
query: Joi.object().keys({
|
||||
size: Joi.number().default(10000),
|
||||
savedObjectTypes: Joi.array()
|
||||
.single()
|
||||
.items(Joi.string())
|
||||
.required(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
handler: async req => {
|
||||
const type = req.params.type;
|
||||
const id = req.params.id;
|
||||
const size = req.query.size;
|
||||
const savedObjectTypes = req.query.savedObjectTypes;
|
||||
const savedObjectsClient = req.getSavedObjectsClient();
|
||||
const savedObjectsManagement = req.server.getSavedObjectsManagement();
|
||||
|
||||
return await findRelationships(type, id, {
|
||||
size,
|
||||
savedObjectsClient,
|
||||
savedObjectsManagement,
|
||||
savedObjectTypes,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import Joi from 'joi';
|
||||
|
||||
async function findAll(savedObjectsClient, findOptions, page = 1, allObjects = []) {
|
||||
const objects = await savedObjectsClient.find({
|
||||
...findOptions,
|
||||
page,
|
||||
});
|
||||
|
||||
allObjects.push(...objects.saved_objects);
|
||||
if (allObjects.length < objects.total) {
|
||||
return findAll(savedObjectsClient, findOptions, page + 1, allObjects);
|
||||
}
|
||||
|
||||
return allObjects;
|
||||
}
|
||||
|
||||
export function registerScrollForExportRoute(server) {
|
||||
server.route({
|
||||
path: '/api/kibana/management/saved_objects/scroll/export',
|
||||
method: ['POST'],
|
||||
config: {
|
||||
validate: {
|
||||
payload: Joi.object()
|
||||
.keys({
|
||||
typesToInclude: Joi.array()
|
||||
.items(Joi.string())
|
||||
.required(),
|
||||
})
|
||||
.required(),
|
||||
},
|
||||
},
|
||||
|
||||
handler: async req => {
|
||||
const savedObjectsClient = req.getSavedObjectsClient();
|
||||
const objects = await findAll(savedObjectsClient, {
|
||||
perPage: 1000,
|
||||
type: req.payload.typesToInclude,
|
||||
});
|
||||
|
||||
return objects.map(hit => {
|
||||
return {
|
||||
_id: hit.id,
|
||||
_source: hit.attributes,
|
||||
_meta: {
|
||||
savedObjectVersion: 2,
|
||||
},
|
||||
_migrationVersion: hit.migrationVersion,
|
||||
_references: hit.references || [],
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function registerScrollForCountRoute(server) {
|
||||
server.route({
|
||||
path: '/api/kibana/management/saved_objects/scroll/counts',
|
||||
method: ['POST'],
|
||||
config: {
|
||||
validate: {
|
||||
payload: Joi.object()
|
||||
.keys({
|
||||
typesToInclude: Joi.array()
|
||||
.items(Joi.string())
|
||||
.required(),
|
||||
searchString: Joi.string(),
|
||||
})
|
||||
.required(),
|
||||
},
|
||||
},
|
||||
|
||||
handler: async req => {
|
||||
const savedObjectsClient = req.getSavedObjectsClient();
|
||||
const findOptions = {
|
||||
type: req.payload.typesToInclude,
|
||||
perPage: 1000,
|
||||
};
|
||||
|
||||
if (req.payload.searchString) {
|
||||
findOptions.search = `${req.payload.searchString}*`;
|
||||
findOptions.searchFields = ['title'];
|
||||
}
|
||||
|
||||
const objects = await findAll(savedObjectsClient, findOptions);
|
||||
const counts = objects.reduce((accum, result) => {
|
||||
const type = result.type;
|
||||
accum[type] = accum[type] || 0;
|
||||
accum[type]++;
|
||||
return accum;
|
||||
}, {});
|
||||
|
||||
for (const type of req.payload.typesToInclude) {
|
||||
if (!counts[type]) {
|
||||
counts[type] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return counts;
|
||||
},
|
||||
});
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import Hapi from 'hapi';
|
||||
import { registerScrollForExportRoute } from './scroll';
|
||||
|
||||
const createMockServer = () => {
|
||||
const mockServer = new Hapi.Server({
|
||||
debug: false,
|
||||
port: 8080,
|
||||
routes: {
|
||||
validate: {
|
||||
failAction: (r, h, err) => {
|
||||
throw err;
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return mockServer;
|
||||
};
|
||||
|
||||
describe(`POST /api/kibana/management/saved_objects/scroll/export`, () => {
|
||||
test('requires "typesToInclude"', async () => {
|
||||
const mockServer = createMockServer();
|
||||
registerScrollForExportRoute(mockServer);
|
||||
|
||||
const headers = {};
|
||||
const payload = {};
|
||||
|
||||
const request = {
|
||||
method: 'POST',
|
||||
url: `/api/kibana/management/saved_objects/scroll/export`,
|
||||
headers,
|
||||
payload,
|
||||
};
|
||||
|
||||
const { result, statusCode } = await mockServer.inject(request);
|
||||
expect(statusCode).toEqual(400);
|
||||
expect(result).toMatchObject({
|
||||
message: `child "typesToInclude" fails because ["typesToInclude" is required]`,
|
||||
});
|
||||
});
|
||||
|
||||
test(`uses "typesToInclude" when searching for objects to export`, async () => {
|
||||
const mockServer = createMockServer();
|
||||
const mockClient = {
|
||||
find: jest.fn(() => {
|
||||
return {
|
||||
saved_objects: [],
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
mockServer.decorate('request', 'getSavedObjectsClient', () => mockClient);
|
||||
|
||||
registerScrollForExportRoute(mockServer);
|
||||
|
||||
const headers = {};
|
||||
const payload = {
|
||||
typesToInclude: ['foo', 'bar'],
|
||||
};
|
||||
|
||||
const request = {
|
||||
method: 'POST',
|
||||
url: `/api/kibana/management/saved_objects/scroll/export`,
|
||||
headers,
|
||||
payload,
|
||||
};
|
||||
|
||||
const { result, statusCode } = await mockServer.inject(request);
|
||||
expect(statusCode).toEqual(200);
|
||||
expect(result).toEqual([]);
|
||||
|
||||
expect(mockClient.find).toHaveBeenCalledWith({
|
||||
page: 1,
|
||||
perPage: 1000,
|
||||
type: ['foo', 'bar'],
|
||||
});
|
||||
});
|
||||
});
|
4
src/legacy/server/kbn_server.d.ts
vendored
4
src/legacy/server/kbn_server.d.ts
vendored
|
@ -39,9 +39,6 @@ import {
|
|||
LegacyServiceDiscoverPlugins,
|
||||
} from '../../core/server';
|
||||
|
||||
// Disable lint errors for imports from src/core/server/saved_objects until SavedObjects migration is complete
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { SavedObjectsManagement } from '../../core/server/saved_objects/management';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { LegacyConfig, ILegacyService, ILegacyInternals } from '../../core/server/legacy';
|
||||
import { ApmOssPlugin } from '../core_plugins/apm_oss';
|
||||
|
@ -78,7 +75,6 @@ declare module 'hapi' {
|
|||
addScopedTutorialContextFactory: (
|
||||
scopedTutorialContextFactory: (...args: any[]) => any
|
||||
) => void;
|
||||
getSavedObjectsManagement(): SavedObjectsManagement;
|
||||
getInjectedUiAppVars: (pluginName: string) => { [key: string]: any };
|
||||
getUiNavLinks(): Array<{ _id: string }>;
|
||||
addMemoizedFactoryToRequest: (
|
||||
|
|
|
@ -29,7 +29,6 @@ import {
|
|||
} from '../../../core/server/saved_objects';
|
||||
import { getRootPropertiesObjects } from '../../../core/server/saved_objects/mappings';
|
||||
import { convertTypesToLegacySchema } from '../../../core/server/saved_objects/utils';
|
||||
import { SavedObjectsManagement } from '../../../core/server/saved_objects/management';
|
||||
|
||||
export function savedObjectsMixin(kbnServer, server) {
|
||||
const migrator = kbnServer.newPlatform.__internals.kibanaMigrator;
|
||||
|
@ -40,11 +39,6 @@ export function savedObjectsMixin(kbnServer, server) {
|
|||
const visibleTypes = allTypes.filter(type => !schema.isHiddenType(type));
|
||||
|
||||
server.decorate('server', 'kibanaMigrator', migrator);
|
||||
server.decorate(
|
||||
'server',
|
||||
'getSavedObjectsManagement',
|
||||
() => new SavedObjectsManagement(typeRegistry)
|
||||
);
|
||||
|
||||
const warn = message => server.log(['warning', 'saved-objects'], message);
|
||||
// we use kibana.index which is technically defined in the kibana plugin, so if
|
||||
|
@ -84,8 +78,13 @@ export function savedObjectsMixin(kbnServer, server) {
|
|||
|
||||
const provider = kbnServer.newPlatform.__internals.savedObjectsClientProvider;
|
||||
|
||||
const importAndExportableTypes = typeRegistry
|
||||
.getImportableAndExportableTypes()
|
||||
.map(type => type.name);
|
||||
|
||||
const service = {
|
||||
types: visibleTypes,
|
||||
importAndExportableTypes,
|
||||
SavedObjectsClient,
|
||||
SavedObjectsRepository,
|
||||
getSavedObjectsRepository: createRepository,
|
||||
|
|
|
@ -183,7 +183,7 @@ describe('Saved Objects Mixin', () => {
|
|||
'kibanaMigrator',
|
||||
expect.any(Object)
|
||||
);
|
||||
expect(mockServer.decorate).toHaveBeenCalledTimes(2);
|
||||
expect(mockServer.decorate).toHaveBeenCalledTimes(1);
|
||||
expect(mockServer.route).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
6
src/plugins/saved_objects_management/kibana.json
Normal file
6
src/plugins/saved_objects_management/kibana.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"id": "savedObjectsManagement",
|
||||
"version": "kibana",
|
||||
"server": true,
|
||||
"ui": false
|
||||
}
|
31
src/plugins/saved_objects_management/server/index.ts
Normal file
31
src/plugins/saved_objects_management/server/index.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { PluginInitializerContext } from 'src/core/server';
|
||||
import { SavedObjectsManagementPlugin } from './plugin';
|
||||
|
||||
export const plugin = (context: PluginInitializerContext) =>
|
||||
new SavedObjectsManagementPlugin(context);
|
||||
|
||||
export {
|
||||
SavedObjectsManagementPluginSetup,
|
||||
SavedObjectsManagementPluginStart,
|
||||
SavedObjectMetadata,
|
||||
SavedObjectWithMetadata,
|
||||
} from './types';
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { times } from 'lodash';
|
||||
import { SavedObjectsFindOptions, SavedObject } from 'src/core/server';
|
||||
import { savedObjectsClientMock } from '../../../../core/server/mocks';
|
||||
import { findAll } from './find_all';
|
||||
|
||||
describe('findAll', () => {
|
||||
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
|
||||
|
||||
const createObj = (id: number): SavedObject => ({
|
||||
type: 'type',
|
||||
id: `id-${id}`,
|
||||
attributes: {},
|
||||
references: [],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
savedObjectsClient = savedObjectsClientMock.create();
|
||||
});
|
||||
|
||||
it('calls the saved object client with the correct parameters', async () => {
|
||||
const query: SavedObjectsFindOptions = {
|
||||
type: ['some-type', 'another-type'],
|
||||
};
|
||||
|
||||
savedObjectsClient.find.mockResolvedValue({
|
||||
saved_objects: [createObj(1), createObj(2)],
|
||||
total: 1,
|
||||
per_page: 20,
|
||||
page: 1,
|
||||
});
|
||||
|
||||
const results = await findAll(savedObjectsClient, query);
|
||||
|
||||
expect(savedObjectsClient.find).toHaveBeenCalledTimes(1);
|
||||
expect(savedObjectsClient.find).toHaveBeenCalledWith({
|
||||
...query,
|
||||
page: 1,
|
||||
});
|
||||
|
||||
expect(results).toEqual([createObj(1), createObj(2)]);
|
||||
});
|
||||
|
||||
it('recursively call find until all objects are fetched', async () => {
|
||||
const query: SavedObjectsFindOptions = {
|
||||
type: ['some-type', 'another-type'],
|
||||
};
|
||||
const objPerPage = 2;
|
||||
|
||||
savedObjectsClient.find.mockImplementation(({ page }) => {
|
||||
const firstInPage = (page! - 1) * objPerPage + 1;
|
||||
return Promise.resolve({
|
||||
saved_objects: [createObj(firstInPage), createObj(firstInPage + 1)],
|
||||
total: objPerPage * 3,
|
||||
per_page: objPerPage,
|
||||
page: page!,
|
||||
});
|
||||
});
|
||||
|
||||
const results = await findAll(savedObjectsClient, query);
|
||||
expect(savedObjectsClient.find).toHaveBeenCalledTimes(3);
|
||||
expect(savedObjectsClient.find).toHaveBeenCalledWith({
|
||||
...query,
|
||||
page: 1,
|
||||
});
|
||||
expect(savedObjectsClient.find).toHaveBeenCalledWith({
|
||||
...query,
|
||||
page: 2,
|
||||
});
|
||||
expect(savedObjectsClient.find).toHaveBeenCalledWith({
|
||||
...query,
|
||||
page: 3,
|
||||
});
|
||||
|
||||
expect(results).toEqual(times(6, num => createObj(num + 1)));
|
||||
});
|
||||
});
|
46
src/plugins/saved_objects_management/server/lib/find_all.ts
Normal file
46
src/plugins/saved_objects_management/server/lib/find_all.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { SavedObjectsClientContract, SavedObject, SavedObjectsFindOptions } from 'src/core/server';
|
||||
|
||||
export const findAll = async (
|
||||
client: SavedObjectsClientContract,
|
||||
findOptions: SavedObjectsFindOptions
|
||||
): Promise<SavedObject[]> => {
|
||||
return recursiveFind(client, findOptions, 1, []);
|
||||
};
|
||||
|
||||
const recursiveFind = async (
|
||||
client: SavedObjectsClientContract,
|
||||
findOptions: SavedObjectsFindOptions,
|
||||
page: number,
|
||||
allObjects: SavedObject[]
|
||||
): Promise<SavedObject[]> => {
|
||||
const objects = await client.find({
|
||||
...findOptions,
|
||||
page,
|
||||
});
|
||||
|
||||
allObjects.push(...objects.saved_objects);
|
||||
if (allObjects.length < objects.total) {
|
||||
return recursiveFind(client, findOptions, page + 1, allObjects);
|
||||
}
|
||||
|
||||
return allObjects;
|
||||
};
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { findRelationships } from './find_relationships';
|
||||
import { managementMock } from '../services/management.mock';
|
||||
import { savedObjectsClientMock } from '../../../../core/server/mocks';
|
||||
|
||||
describe('findRelationships', () => {
|
||||
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
|
||||
let managementService: ReturnType<typeof managementMock.create>;
|
||||
|
||||
beforeEach(() => {
|
||||
savedObjectsClient = savedObjectsClientMock.create();
|
||||
managementService = managementMock.create();
|
||||
});
|
||||
|
||||
it('returns the child and parent references of the object', async () => {
|
||||
const type = 'dashboard';
|
||||
const id = 'some-id';
|
||||
const references = [
|
||||
{
|
||||
type: 'some-type',
|
||||
id: 'ref-1',
|
||||
name: 'ref 1',
|
||||
},
|
||||
{
|
||||
type: 'another-type',
|
||||
id: 'ref-2',
|
||||
name: 'ref 2',
|
||||
},
|
||||
];
|
||||
const referenceTypes = ['some-type', 'another-type'];
|
||||
|
||||
savedObjectsClient.get.mockResolvedValue({
|
||||
id,
|
||||
type,
|
||||
attributes: {},
|
||||
references,
|
||||
});
|
||||
|
||||
savedObjectsClient.bulkGet.mockResolvedValue({
|
||||
saved_objects: [
|
||||
{
|
||||
type: 'some-type',
|
||||
id: 'ref-1',
|
||||
attributes: {},
|
||||
references: [],
|
||||
},
|
||||
{
|
||||
type: 'another-type',
|
||||
id: 'ref-2',
|
||||
attributes: {},
|
||||
references: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
savedObjectsClient.find.mockResolvedValue({
|
||||
saved_objects: [
|
||||
{
|
||||
type: 'parent-type',
|
||||
id: 'parent-id',
|
||||
attributes: {},
|
||||
references: [],
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
per_page: 20,
|
||||
page: 1,
|
||||
});
|
||||
|
||||
const relationships = await findRelationships({
|
||||
type,
|
||||
id,
|
||||
size: 20,
|
||||
client: savedObjectsClient,
|
||||
referenceTypes,
|
||||
savedObjectsManagement: managementService,
|
||||
});
|
||||
|
||||
expect(savedObjectsClient.get).toHaveBeenCalledTimes(1);
|
||||
expect(savedObjectsClient.get).toHaveBeenCalledWith(type, id);
|
||||
|
||||
expect(savedObjectsClient.bulkGet).toHaveBeenCalledTimes(1);
|
||||
expect(savedObjectsClient.bulkGet).toHaveBeenCalledWith(
|
||||
references.map(ref => ({
|
||||
id: ref.id,
|
||||
type: ref.type,
|
||||
}))
|
||||
);
|
||||
|
||||
expect(savedObjectsClient.find).toHaveBeenCalledTimes(1);
|
||||
expect(savedObjectsClient.find).toHaveBeenCalledWith({
|
||||
hasReference: { type, id },
|
||||
perPage: 20,
|
||||
type: referenceTypes,
|
||||
});
|
||||
|
||||
expect(relationships).toEqual([
|
||||
{
|
||||
id: 'ref-1',
|
||||
relationship: 'child',
|
||||
type: 'some-type',
|
||||
meta: expect.any(Object),
|
||||
},
|
||||
{
|
||||
id: 'ref-2',
|
||||
relationship: 'child',
|
||||
type: 'another-type',
|
||||
meta: expect.any(Object),
|
||||
},
|
||||
{
|
||||
id: 'parent-id',
|
||||
relationship: 'parent',
|
||||
type: 'parent-type',
|
||||
meta: expect.any(Object),
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('uses the management service to consolidate the relationship objects', async () => {
|
||||
const type = 'dashboard';
|
||||
const id = 'some-id';
|
||||
const references = [
|
||||
{
|
||||
type: 'some-type',
|
||||
id: 'ref-1',
|
||||
name: 'ref 1',
|
||||
},
|
||||
];
|
||||
const referenceTypes = ['some-type', 'another-type'];
|
||||
|
||||
managementService.getIcon.mockReturnValue('icon');
|
||||
managementService.getTitle.mockReturnValue('title');
|
||||
managementService.getEditUrl.mockReturnValue('editUrl');
|
||||
managementService.getInAppUrl.mockReturnValue({
|
||||
path: 'path',
|
||||
uiCapabilitiesPath: 'uiCapabilitiesPath',
|
||||
});
|
||||
|
||||
savedObjectsClient.get.mockResolvedValue({
|
||||
id,
|
||||
type,
|
||||
attributes: {},
|
||||
references,
|
||||
});
|
||||
|
||||
savedObjectsClient.bulkGet.mockResolvedValue({
|
||||
saved_objects: [
|
||||
{
|
||||
type: 'some-type',
|
||||
id: 'ref-1',
|
||||
attributes: {},
|
||||
references: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
savedObjectsClient.find.mockResolvedValue({
|
||||
saved_objects: [],
|
||||
total: 0,
|
||||
per_page: 20,
|
||||
page: 1,
|
||||
});
|
||||
|
||||
const relationships = await findRelationships({
|
||||
type,
|
||||
id,
|
||||
size: 20,
|
||||
client: savedObjectsClient,
|
||||
referenceTypes,
|
||||
savedObjectsManagement: managementService,
|
||||
});
|
||||
|
||||
expect(managementService.getIcon).toHaveBeenCalledTimes(1);
|
||||
expect(managementService.getTitle).toHaveBeenCalledTimes(1);
|
||||
expect(managementService.getEditUrl).toHaveBeenCalledTimes(1);
|
||||
expect(managementService.getInAppUrl).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(relationships).toEqual([
|
||||
{
|
||||
id: 'ref-1',
|
||||
relationship: 'child',
|
||||
type: 'some-type',
|
||||
meta: {
|
||||
title: 'title',
|
||||
icon: 'icon',
|
||||
editUrl: 'editUrl',
|
||||
inAppUrl: {
|
||||
path: 'path',
|
||||
uiCapabilitiesPath: 'uiCapabilitiesPath',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { SavedObjectsClientContract } from 'src/core/server';
|
||||
import { injectMetaAttributes } from './inject_meta_attributes';
|
||||
import { ISavedObjectsManagement } from '../services';
|
||||
import { SavedObjectRelation, SavedObjectWithMetadata } from '../types';
|
||||
|
||||
export async function findRelationships({
|
||||
type,
|
||||
id,
|
||||
size,
|
||||
client,
|
||||
referenceTypes,
|
||||
savedObjectsManagement,
|
||||
}: {
|
||||
type: string;
|
||||
id: string;
|
||||
size: number;
|
||||
client: SavedObjectsClientContract;
|
||||
referenceTypes: string[];
|
||||
savedObjectsManagement: ISavedObjectsManagement;
|
||||
}): Promise<SavedObjectRelation[]> {
|
||||
const { references = [] } = await client.get(type, id);
|
||||
|
||||
// Use a map to avoid duplicates, it does happen but have a different "name" in the reference
|
||||
const referencedToBulkGetOpts = new Map(
|
||||
references.map(ref => [`${ref.type}:${ref.id}`, { id: ref.id, type: ref.type }])
|
||||
);
|
||||
|
||||
const [childReferencesResponse, parentReferencesResponse] = await Promise.all([
|
||||
referencedToBulkGetOpts.size > 0
|
||||
? client.bulkGet([...referencedToBulkGetOpts.values()])
|
||||
: Promise.resolve({ saved_objects: [] }),
|
||||
client.find({
|
||||
hasReference: { type, id },
|
||||
perPage: size,
|
||||
type: referenceTypes,
|
||||
}),
|
||||
]);
|
||||
|
||||
return childReferencesResponse.saved_objects
|
||||
.map(obj => injectMetaAttributes(obj, savedObjectsManagement))
|
||||
.map(extractCommonProperties)
|
||||
.map(
|
||||
obj =>
|
||||
({
|
||||
...obj,
|
||||
relationship: 'child',
|
||||
} as SavedObjectRelation)
|
||||
)
|
||||
.concat(
|
||||
parentReferencesResponse.saved_objects
|
||||
.map(obj => injectMetaAttributes(obj, savedObjectsManagement))
|
||||
.map(extractCommonProperties)
|
||||
.map(
|
||||
obj =>
|
||||
({
|
||||
...obj,
|
||||
relationship: 'parent',
|
||||
} as SavedObjectRelation)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function extractCommonProperties(savedObject: SavedObjectWithMetadata) {
|
||||
return {
|
||||
id: savedObject.id,
|
||||
type: savedObject.type,
|
||||
meta: savedObject.meta,
|
||||
};
|
||||
}
|
|
@ -17,9 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export default function({ loadTestFile }) {
|
||||
describe('saved_objects', () => {
|
||||
loadTestFile(require.resolve('./find'));
|
||||
loadTestFile(require.resolve('./relationships'));
|
||||
});
|
||||
}
|
||||
export { injectMetaAttributes } from './inject_meta_attributes';
|
||||
export { findAll } from './find_all';
|
||||
export { findRelationships } from './find_relationships';
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { SavedObject } from 'src/core/server';
|
||||
import { injectMetaAttributes } from './inject_meta_attributes';
|
||||
import { managementMock } from '../services/management.mock';
|
||||
|
||||
describe('injectMetaAttributes', () => {
|
||||
let managementService: ReturnType<typeof managementMock.create>;
|
||||
|
||||
beforeEach(() => {
|
||||
managementService = managementMock.create();
|
||||
|
||||
managementService.getIcon.mockReturnValue('icon');
|
||||
managementService.getTitle.mockReturnValue('title');
|
||||
managementService.getEditUrl.mockReturnValue('editUrl');
|
||||
managementService.getInAppUrl.mockReturnValue({
|
||||
path: 'path',
|
||||
uiCapabilitiesPath: 'uiCapabilitiesPath',
|
||||
});
|
||||
});
|
||||
|
||||
it('inject the metadata to the obj', () => {
|
||||
const obj: SavedObject<any> = {
|
||||
id: 'id',
|
||||
type: 'config',
|
||||
attributes: { some: 'value' },
|
||||
references: [],
|
||||
};
|
||||
|
||||
const objWithMeta = injectMetaAttributes(obj, managementService);
|
||||
expect(objWithMeta).toStrictEqual({
|
||||
id: 'id',
|
||||
type: 'config',
|
||||
attributes: { some: 'value' },
|
||||
references: [],
|
||||
meta: {
|
||||
icon: 'icon',
|
||||
title: 'title',
|
||||
editUrl: 'editUrl',
|
||||
inAppUrl: {
|
||||
path: 'path',
|
||||
uiCapabilitiesPath: 'uiCapabilitiesPath',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not alter the original object', () => {
|
||||
const obj: SavedObject<any> = {
|
||||
id: 'id',
|
||||
type: 'config',
|
||||
attributes: { some: 'value' },
|
||||
references: [],
|
||||
};
|
||||
|
||||
injectMetaAttributes(obj, managementService);
|
||||
|
||||
expect(obj).toStrictEqual({
|
||||
id: 'id',
|
||||
type: 'config',
|
||||
attributes: { some: 'value' },
|
||||
references: [],
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { SavedObject } from 'src/core/server';
|
||||
import { ISavedObjectsManagement } from '../services';
|
||||
import { SavedObjectWithMetadata } from '../types';
|
||||
|
||||
export function injectMetaAttributes<T = unknown>(
|
||||
savedObject: SavedObject<T> | SavedObjectWithMetadata<T>,
|
||||
savedObjectsManagement: ISavedObjectsManagement
|
||||
): SavedObjectWithMetadata<T> {
|
||||
const result = {
|
||||
...savedObject,
|
||||
meta: (savedObject as SavedObjectWithMetadata).meta || {},
|
||||
};
|
||||
|
||||
// Add extra meta information
|
||||
result.meta.icon = savedObjectsManagement.getIcon(savedObject.type);
|
||||
result.meta.title = savedObjectsManagement.getTitle(savedObject);
|
||||
result.meta.editUrl = savedObjectsManagement.getEditUrl(savedObject);
|
||||
result.meta.inAppUrl = savedObjectsManagement.getInAppUrl(savedObject);
|
||||
|
||||
return result;
|
||||
}
|
|
@ -17,8 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export default function({ loadTestFile }) {
|
||||
describe('management apis', () => {
|
||||
loadTestFile(require.resolve('./saved_objects'));
|
||||
});
|
||||
}
|
||||
export const registerRoutesMock = jest.fn();
|
||||
|
||||
jest.doMock('./routes', () => ({
|
||||
registerRoutes: registerRoutesMock,
|
||||
}));
|
45
src/plugins/saved_objects_management/server/plugin.test.ts
Normal file
45
src/plugins/saved_objects_management/server/plugin.test.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { registerRoutesMock } from './plugin.test.mocks';
|
||||
import { SavedObjectsManagementPlugin } from './plugin';
|
||||
import { coreMock } from '../../../core/server/mocks';
|
||||
|
||||
describe('SavedObjectsManagementPlugin', () => {
|
||||
let plugin: SavedObjectsManagementPlugin;
|
||||
|
||||
beforeEach(() => {
|
||||
plugin = new SavedObjectsManagementPlugin(coreMock.createPluginInitializerContext());
|
||||
});
|
||||
|
||||
describe('#setup', () => {
|
||||
it('registers the routes', async () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
|
||||
await plugin.setup(coreSetup);
|
||||
|
||||
expect(registerRoutesMock).toHaveBeenCalledTimes(1);
|
||||
expect(registerRoutesMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
http: coreSetup.http,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
53
src/plugins/saved_objects_management/server/plugin.ts
Normal file
53
src/plugins/saved_objects_management/server/plugin.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { Subject } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from 'src/core/server';
|
||||
import { SavedObjectsManagementPluginSetup, SavedObjectsManagementPluginStart } from './types';
|
||||
import { SavedObjectsManagement } from './services';
|
||||
import { registerRoutes } from './routes';
|
||||
|
||||
export class SavedObjectsManagementPlugin
|
||||
implements Plugin<SavedObjectsManagementPluginSetup, SavedObjectsManagementPluginStart, {}, {}> {
|
||||
private readonly logger: Logger;
|
||||
private managementService$ = new Subject<SavedObjectsManagement>();
|
||||
|
||||
constructor(private readonly context: PluginInitializerContext) {
|
||||
this.logger = this.context.logger.get();
|
||||
}
|
||||
|
||||
public async setup({ http }: CoreSetup) {
|
||||
this.logger.debug('Setting up SavedObjectsManagement plugin');
|
||||
registerRoutes({
|
||||
http,
|
||||
managementServicePromise: this.managementService$.pipe(first()).toPromise(),
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
public async start(core: CoreStart) {
|
||||
this.logger.debug('Starting up SavedObjectsManagement plugin');
|
||||
const managementService = new SavedObjectsManagement(core.savedObjects.getTypeRegistry());
|
||||
this.managementService$.next(managementService);
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
97
src/plugins/saved_objects_management/server/routes/find.ts
Normal file
97
src/plugins/saved_objects_management/server/routes/find.ts
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { IRouter } from 'src/core/server';
|
||||
import { injectMetaAttributes } from '../lib';
|
||||
import { ISavedObjectsManagement } from '../services';
|
||||
|
||||
export const registerFindRoute = (
|
||||
router: IRouter,
|
||||
managementServicePromise: Promise<ISavedObjectsManagement>
|
||||
) => {
|
||||
router.get(
|
||||
{
|
||||
path: '/api/kibana/management/saved_objects/_find',
|
||||
validate: {
|
||||
query: schema.object({
|
||||
perPage: schema.number({ min: 0, defaultValue: 20 }),
|
||||
page: schema.number({ min: 0, defaultValue: 1 }),
|
||||
type: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]),
|
||||
search: schema.maybe(schema.string()),
|
||||
defaultSearchOperator: schema.oneOf([schema.literal('OR'), schema.literal('AND')], {
|
||||
defaultValue: 'OR',
|
||||
}),
|
||||
sortField: schema.maybe(schema.string()),
|
||||
hasReference: schema.maybe(
|
||||
schema.object({
|
||||
type: schema.string(),
|
||||
id: schema.string(),
|
||||
})
|
||||
),
|
||||
fields: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], {
|
||||
defaultValue: [],
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
router.handleLegacyErrors(async (context, req, res) => {
|
||||
const managementService = await managementServicePromise;
|
||||
const { client } = context.core.savedObjects;
|
||||
const searchTypes = Array.isArray(req.query.type) ? req.query.type : [req.query.type];
|
||||
const includedFields = Array.isArray(req.query.fields)
|
||||
? req.query.fields
|
||||
: [req.query.fields];
|
||||
const importAndExportableTypes = searchTypes.filter(type =>
|
||||
managementService.isImportAndExportable(type)
|
||||
);
|
||||
|
||||
const searchFields = new Set<string>();
|
||||
importAndExportableTypes.forEach(type => {
|
||||
const searchField = managementService.getDefaultSearchField(type);
|
||||
if (searchField) {
|
||||
searchFields.add(searchField);
|
||||
}
|
||||
});
|
||||
|
||||
const findResponse = await client.find<any>({
|
||||
...req.query,
|
||||
fields: undefined,
|
||||
searchFields: [...searchFields],
|
||||
});
|
||||
|
||||
const enhancedSavedObjects = findResponse.saved_objects
|
||||
.map(so => injectMetaAttributes(so, managementService))
|
||||
.map(obj => {
|
||||
const result = { ...obj, attributes: {} as Record<string, any> };
|
||||
for (const field of includedFields) {
|
||||
result.attributes[field] = obj.attributes[field];
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
return res.ok({
|
||||
body: {
|
||||
...findResponse,
|
||||
saved_objects: enhancedSavedObjects,
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { registerRoutes } from './index';
|
||||
import { ISavedObjectsManagement } from '../services';
|
||||
import { coreMock, httpServiceMock } from '../../../../core/server/mocks';
|
||||
|
||||
describe('registerRoutes', () => {
|
||||
it('registers the management routes', () => {
|
||||
const router = httpServiceMock.createRouter();
|
||||
const httpSetup = coreMock.createSetup().http;
|
||||
httpSetup.createRouter.mockReturnValue(router);
|
||||
const managementPromise = Promise.resolve({} as ISavedObjectsManagement);
|
||||
|
||||
registerRoutes({
|
||||
http: httpSetup,
|
||||
managementServicePromise: managementPromise,
|
||||
});
|
||||
|
||||
expect(httpSetup.createRouter).toHaveBeenCalledTimes(1);
|
||||
expect(router.get).toHaveBeenCalledTimes(2);
|
||||
expect(router.post).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(router.get).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
path: '/api/kibana/management/saved_objects/_find',
|
||||
}),
|
||||
expect.any(Function)
|
||||
);
|
||||
expect(router.get).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
path: '/api/kibana/management/saved_objects/relationships/{type}/{id}',
|
||||
}),
|
||||
expect.any(Function)
|
||||
);
|
||||
expect(router.post).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
path: '/api/kibana/management/saved_objects/scroll/counts',
|
||||
}),
|
||||
expect.any(Function)
|
||||
);
|
||||
expect(router.post).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
path: '/api/kibana/management/saved_objects/scroll/export',
|
||||
}),
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
});
|
38
src/plugins/saved_objects_management/server/routes/index.ts
Normal file
38
src/plugins/saved_objects_management/server/routes/index.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { HttpServiceSetup } from 'src/core/server';
|
||||
import { ISavedObjectsManagement } from '../services';
|
||||
import { registerFindRoute } from './find';
|
||||
import { registerScrollForCountRoute } from './scroll_count';
|
||||
import { registerScrollForExportRoute } from './scroll_export';
|
||||
import { registerRelationshipsRoute } from './relationships';
|
||||
|
||||
interface RegisterRouteOptions {
|
||||
http: HttpServiceSetup;
|
||||
managementServicePromise: Promise<ISavedObjectsManagement>;
|
||||
}
|
||||
|
||||
export function registerRoutes({ http, managementServicePromise }: RegisterRouteOptions) {
|
||||
const router = http.createRouter();
|
||||
registerFindRoute(router, managementServicePromise);
|
||||
registerScrollForCountRoute(router);
|
||||
registerScrollForExportRoute(router);
|
||||
registerRelationshipsRoute(router, managementServicePromise);
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { IRouter } from 'src/core/server';
|
||||
import { findRelationships } from '../lib';
|
||||
import { ISavedObjectsManagement } from '../services';
|
||||
|
||||
export const registerRelationshipsRoute = (
|
||||
router: IRouter,
|
||||
managementServicePromise: Promise<ISavedObjectsManagement>
|
||||
) => {
|
||||
router.get(
|
||||
{
|
||||
path: '/api/kibana/management/saved_objects/relationships/{type}/{id}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
type: schema.string(),
|
||||
id: schema.string(),
|
||||
}),
|
||||
query: schema.object({
|
||||
size: schema.number({ defaultValue: 10000 }),
|
||||
savedObjectTypes: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]),
|
||||
}),
|
||||
},
|
||||
},
|
||||
router.handleLegacyErrors(async (context, req, res) => {
|
||||
const managementService = await managementServicePromise;
|
||||
const { client } = context.core.savedObjects;
|
||||
const { type, id } = req.params;
|
||||
const { size } = req.query;
|
||||
const savedObjectTypes = Array.isArray(req.query.savedObjectTypes)
|
||||
? req.query.savedObjectTypes
|
||||
: [req.query.savedObjectTypes];
|
||||
|
||||
const relations = await findRelationships({
|
||||
type,
|
||||
id,
|
||||
client,
|
||||
size,
|
||||
referenceTypes: savedObjectTypes,
|
||||
savedObjectsManagement: managementService,
|
||||
});
|
||||
|
||||
return res.ok({
|
||||
body: relations,
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { IRouter, SavedObjectsFindOptions } from 'src/core/server';
|
||||
import { findAll } from '../lib';
|
||||
|
||||
export const registerScrollForCountRoute = (router: IRouter) => {
|
||||
router.post(
|
||||
{
|
||||
path: '/api/kibana/management/saved_objects/scroll/counts',
|
||||
validate: {
|
||||
body: schema.object({
|
||||
typesToInclude: schema.arrayOf(schema.string()),
|
||||
searchString: schema.maybe(schema.string()),
|
||||
}),
|
||||
},
|
||||
},
|
||||
router.handleLegacyErrors(async (context, req, res) => {
|
||||
const { client } = context.core.savedObjects;
|
||||
|
||||
const findOptions: SavedObjectsFindOptions = {
|
||||
type: req.body.typesToInclude,
|
||||
perPage: 1000,
|
||||
};
|
||||
if (req.body.searchString) {
|
||||
findOptions.search = `${req.body.searchString}*`;
|
||||
findOptions.searchFields = ['title'];
|
||||
}
|
||||
|
||||
const objects = await findAll(client, findOptions);
|
||||
|
||||
const counts = objects.reduce((accum, result) => {
|
||||
const type = result.type;
|
||||
accum[type] = accum[type] || 0;
|
||||
accum[type]++;
|
||||
return accum;
|
||||
}, {} as Record<string, number>);
|
||||
|
||||
for (const type of req.body.typesToInclude) {
|
||||
if (!counts[type]) {
|
||||
counts[type] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return res.ok({
|
||||
body: counts,
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { IRouter } from 'src/core/server';
|
||||
import { findAll } from '../lib';
|
||||
|
||||
export const registerScrollForExportRoute = (router: IRouter) => {
|
||||
router.post(
|
||||
{
|
||||
path: '/api/kibana/management/saved_objects/scroll/export',
|
||||
validate: {
|
||||
body: schema.object({
|
||||
typesToInclude: schema.arrayOf(schema.string()),
|
||||
}),
|
||||
},
|
||||
},
|
||||
router.handleLegacyErrors(async (context, req, res) => {
|
||||
const { client } = context.core.savedObjects;
|
||||
const objects = await findAll(client, {
|
||||
perPage: 1000,
|
||||
type: req.body.typesToInclude,
|
||||
});
|
||||
|
||||
return res.ok({
|
||||
body: objects.map(hit => {
|
||||
return {
|
||||
_id: hit.id,
|
||||
_source: hit.attributes,
|
||||
_meta: {
|
||||
savedObjectVersion: 2,
|
||||
},
|
||||
_migrationVersion: hit.migrationVersion,
|
||||
_references: hit.references || [],
|
||||
};
|
||||
}),
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
|
@ -17,4 +17,4 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { SavedObjectsManagement } from './management';
|
||||
export { SavedObjectsManagement, ISavedObjectsManagement } from './management';
|
|
@ -24,7 +24,6 @@ const createManagementMock = () => {
|
|||
const mocked: jest.Mocked<Management> = {
|
||||
isImportAndExportable: jest.fn().mockReturnValue(true),
|
||||
getDefaultSearchField: jest.fn(),
|
||||
getImportableAndExportableTypes: jest.fn(),
|
||||
getIcon: jest.fn(),
|
||||
getTitle: jest.fn(),
|
||||
getEditUrl: jest.fn(),
|
|
@ -18,8 +18,7 @@
|
|||
*/
|
||||
|
||||
import { SavedObjectsManagement } from './management';
|
||||
import { SavedObjectsType } from '../types';
|
||||
import { SavedObjectTypeRegistry } from '../saved_objects_type_registry';
|
||||
import { SavedObjectsType, SavedObjectTypeRegistry } from '../../../../core/server';
|
||||
|
||||
describe('SavedObjectsManagement', () => {
|
||||
let registry: SavedObjectTypeRegistry;
|
|
@ -17,19 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { SavedObject } from '../types';
|
||||
import { ISavedObjectTypeRegistry } from '../saved_objects_type_registry';
|
||||
import { ISavedObjectTypeRegistry, SavedObject } from 'src/core/server';
|
||||
|
||||
export type ISavedObjectsManagement = PublicMethodsOf<SavedObjectsManagement>;
|
||||
|
||||
export class SavedObjectsManagement {
|
||||
constructor(private readonly registry: ISavedObjectTypeRegistry) {}
|
||||
|
||||
public getImportableAndExportableTypes() {
|
||||
return this.registry
|
||||
.getAllTypes()
|
||||
.map(type => type.name)
|
||||
.filter(type => this.isImportAndExportable(type));
|
||||
}
|
||||
|
||||
public isImportAndExportable(type: string) {
|
||||
return this.registry.isImportableAndExportable(type);
|
||||
}
|
54
src/plugins/saved_objects_management/server/types.ts
Normal file
54
src/plugins/saved_objects_management/server/types.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { SavedObject } from 'src/core/server';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface SavedObjectsManagementPluginSetup {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface SavedObjectsManagementPluginStart {}
|
||||
|
||||
/**
|
||||
* The metadata injected into a {@link SavedObject | saved object} when returning
|
||||
* {@link SavedObjectWithMetadata | enhanced objects} from the plugin API endpoints.
|
||||
*/
|
||||
export interface SavedObjectMetadata {
|
||||
icon?: string;
|
||||
title?: string;
|
||||
editUrl?: string;
|
||||
inAppUrl?: { path: string; uiCapabilitiesPath: string };
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link SavedObject | saved object} enhanced with meta properties used by the client-side plugin.
|
||||
*/
|
||||
export type SavedObjectWithMetadata<T = unknown> = SavedObject<T> & {
|
||||
meta: SavedObjectMetadata;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a relation between two {@link SavedObject | saved object}
|
||||
*/
|
||||
export interface SavedObjectRelation {
|
||||
id: string;
|
||||
type: string;
|
||||
relationship: 'child' | 'parent';
|
||||
meta: SavedObjectMetadata;
|
||||
}
|
|
@ -25,7 +25,7 @@ export default function({ loadTestFile }) {
|
|||
loadTestFile(require.resolve('./home'));
|
||||
loadTestFile(require.resolve('./index_patterns'));
|
||||
loadTestFile(require.resolve('./kql_telemetry'));
|
||||
loadTestFile(require.resolve('./management'));
|
||||
loadTestFile(require.resolve('./saved_objects_management'));
|
||||
loadTestFile(require.resolve('./saved_objects'));
|
||||
loadTestFile(require.resolve('./scripts'));
|
||||
loadTestFile(require.resolve('./shorten'));
|
||||
|
|
|
@ -1,449 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
const Joi = require('joi');
|
||||
|
||||
export default function({ getService }) {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
const GENERIC_RESPONSE_SCHEMA = Joi.array().items(
|
||||
Joi.object().keys({
|
||||
id: Joi.string()
|
||||
.uuid()
|
||||
.required(),
|
||||
type: Joi.string().required(),
|
||||
relationship: Joi.string()
|
||||
.valid('parent', 'child')
|
||||
.required(),
|
||||
meta: Joi.object()
|
||||
.keys({
|
||||
title: Joi.string().required(),
|
||||
icon: Joi.string().required(),
|
||||
editUrl: Joi.string().required(),
|
||||
inAppUrl: Joi.object()
|
||||
.keys({
|
||||
path: Joi.string().required(),
|
||||
uiCapabilitiesPath: Joi.string().required(),
|
||||
})
|
||||
.required(),
|
||||
})
|
||||
.required(),
|
||||
})
|
||||
);
|
||||
|
||||
describe('relationships', () => {
|
||||
before(() => esArchiver.load('management/saved_objects'));
|
||||
after(() => esArchiver.unload('management/saved_objects'));
|
||||
|
||||
const baseApiUrl = `/api/kibana/management/saved_objects/relationships`;
|
||||
const coerceToArray = itemOrItems => [].concat(itemOrItems);
|
||||
const getSavedObjectTypesQuery = types =>
|
||||
coerceToArray(types)
|
||||
.map(type => `savedObjectTypes=${type}`)
|
||||
.join('&');
|
||||
const defaultQuery = getSavedObjectTypesQuery([
|
||||
'visualization',
|
||||
'index-pattern',
|
||||
'search',
|
||||
'dashboard',
|
||||
]);
|
||||
|
||||
describe('searches', () => {
|
||||
it('should validate search response schema', async () => {
|
||||
await supertest
|
||||
.get(`${baseApiUrl}/search/960372e0-3224-11e8-a572-ffca06da1357?${defaultQuery}`)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
const validationResult = Joi.validate(resp.body, GENERIC_RESPONSE_SCHEMA);
|
||||
expect(validationResult.error).to.be(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for searches', async () => {
|
||||
await supertest
|
||||
.get(`${baseApiUrl}/search/960372e0-3224-11e8-a572-ffca06da1357?${defaultQuery}`)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: '8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
type: 'index-pattern',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
title: 'saved_objects*',
|
||||
icon: 'indexPatternApp',
|
||||
editUrl: '/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path:
|
||||
'/app/kibana#/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'management.kibana.index_patterns',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
title: 'VisualizationFromSavedSearch',
|
||||
icon: 'visualizeApp',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter based on savedObjectTypes', async () => {
|
||||
await supertest
|
||||
.get(
|
||||
`${baseApiUrl}/search/960372e0-3224-11e8-a572-ffca06da1357?${getSavedObjectTypesQuery(
|
||||
'visualization'
|
||||
)}`
|
||||
)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: '8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
type: 'index-pattern',
|
||||
meta: {
|
||||
icon: 'indexPatternApp',
|
||||
title: 'saved_objects*',
|
||||
editUrl: '/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path:
|
||||
'/app/kibana#/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'management.kibana.index_patterns',
|
||||
},
|
||||
},
|
||||
relationship: 'child',
|
||||
},
|
||||
{
|
||||
id: 'a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
meta: {
|
||||
icon: 'visualizeApp',
|
||||
title: 'VisualizationFromSavedSearch',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
relationship: 'parent',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
//TODO: https://github.com/elastic/kibana/issues/19713 causes this test to fail.
|
||||
it.skip('should return 404 if search finds no results', async () => {
|
||||
await supertest
|
||||
.get(`${baseApiUrl}/search/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx${defaultQuery}`)
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dashboards', () => {
|
||||
it('should validate dashboard response schema', async () => {
|
||||
await supertest
|
||||
.get(`${baseApiUrl}/dashboard/b70c7ae0-3224-11e8-a572-ffca06da1357?${defaultQuery}`)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
const validationResult = Joi.validate(resp.body, GENERIC_RESPONSE_SCHEMA);
|
||||
expect(validationResult.error).to.be(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for dashboards', async () => {
|
||||
await supertest
|
||||
.get(`${baseApiUrl}/dashboard/b70c7ae0-3224-11e8-a572-ffca06da1357?${defaultQuery}`)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: 'add810b0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
icon: 'visualizeApp',
|
||||
title: 'Visualization',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
icon: 'visualizeApp',
|
||||
title: 'VisualizationFromSavedSearch',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter based on savedObjectTypes', async () => {
|
||||
await supertest
|
||||
.get(
|
||||
`${baseApiUrl}/dashboard/b70c7ae0-3224-11e8-a572-ffca06da1357?${getSavedObjectTypesQuery(
|
||||
'search'
|
||||
)}`
|
||||
)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: 'add810b0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
meta: {
|
||||
icon: 'visualizeApp',
|
||||
title: 'Visualization',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
relationship: 'child',
|
||||
},
|
||||
{
|
||||
id: 'a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
meta: {
|
||||
icon: 'visualizeApp',
|
||||
title: 'VisualizationFromSavedSearch',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
relationship: 'child',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
//TODO: https://github.com/elastic/kibana/issues/19713 causes this test to fail.
|
||||
it.skip('should return 404 if dashboard finds no results', async () => {
|
||||
await supertest
|
||||
.get(`${baseApiUrl}/dashboard/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx${defaultQuery}`)
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('visualizations', () => {
|
||||
it('should validate visualization response schema', async () => {
|
||||
await supertest
|
||||
.get(`${baseApiUrl}/visualization/a42c0580-3224-11e8-a572-ffca06da1357?${defaultQuery}`)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
const validationResult = Joi.validate(resp.body, GENERIC_RESPONSE_SCHEMA);
|
||||
expect(validationResult.error).to.be(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for visualizations', async () => {
|
||||
await supertest
|
||||
.get(`${baseApiUrl}/visualization/a42c0580-3224-11e8-a572-ffca06da1357?${defaultQuery}`)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: '960372e0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'search',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
icon: 'discoverApp',
|
||||
title: 'OneRecord',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'b70c7ae0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'dashboard',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
icon: 'dashboardApp',
|
||||
title: 'Dashboard',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedDashboards/b70c7ae0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/dashboard/b70c7ae0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter based on savedObjectTypes', async () => {
|
||||
await supertest
|
||||
.get(
|
||||
`${baseApiUrl}/visualization/a42c0580-3224-11e8-a572-ffca06da1357?${getSavedObjectTypesQuery(
|
||||
'search'
|
||||
)}`
|
||||
)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: '960372e0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'search',
|
||||
meta: {
|
||||
icon: 'discoverApp',
|
||||
title: 'OneRecord',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
},
|
||||
},
|
||||
relationship: 'child',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return 404 if visualizations finds no results', async () => {
|
||||
await supertest
|
||||
.get(`${baseApiUrl}/visualization/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?${defaultQuery}`)
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('index patterns', () => {
|
||||
it('should validate visualization response schema', async () => {
|
||||
await supertest
|
||||
.get(`${baseApiUrl}/index-pattern/8963ca30-3224-11e8-a572-ffca06da1357?${defaultQuery}`)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
const validationResult = Joi.validate(resp.body, GENERIC_RESPONSE_SCHEMA);
|
||||
expect(validationResult.error).to.be(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for index patterns', async () => {
|
||||
await supertest
|
||||
.get(`${baseApiUrl}/index-pattern/8963ca30-3224-11e8-a572-ffca06da1357?${defaultQuery}`)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: '960372e0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'search',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
icon: 'discoverApp',
|
||||
title: 'OneRecord',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'add810b0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
icon: 'visualizeApp',
|
||||
title: 'Visualization',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter based on savedObjectTypes', async () => {
|
||||
await supertest
|
||||
.get(
|
||||
`${baseApiUrl}/index-pattern/8963ca30-3224-11e8-a572-ffca06da1357?${getSavedObjectTypesQuery(
|
||||
'search'
|
||||
)}`
|
||||
)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: '960372e0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'search',
|
||||
meta: {
|
||||
icon: 'discoverApp',
|
||||
title: 'OneRecord',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
},
|
||||
},
|
||||
relationship: 'parent',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return 404 if index pattern finds no results', async () => {
|
||||
await supertest
|
||||
.get(`${baseApiUrl}/index-pattern/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?${defaultQuery}`)
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -18,8 +18,10 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { Response } from 'supertest';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function({ getService }) {
|
||||
export default function({ getService }: FtrProviderContext) {
|
||||
const es = getService('legacyEs');
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
@ -33,7 +35,7 @@ export default function({ getService }) {
|
|||
await supertest
|
||||
.get('/api/kibana/management/saved_objects/_find?type=visualization&fields=title')
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body).to.eql({
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
|
@ -75,7 +77,7 @@ export default function({ getService }) {
|
|||
await supertest
|
||||
.get('/api/kibana/management/saved_objects/_find?type=wigwags')
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body).to.eql({
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
|
@ -92,7 +94,7 @@ export default function({ getService }) {
|
|||
'/api/kibana/management/saved_objects/_find?type=visualization&page=100&perPage=100'
|
||||
)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body).to.eql({
|
||||
page: 100,
|
||||
per_page: 100,
|
||||
|
@ -107,15 +109,11 @@ export default function({ getService }) {
|
|||
await supertest
|
||||
.get('/api/kibana/management/saved_objects/_find?type=url&searchFields=a')
|
||||
.expect(400)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body).to.eql({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: '"searchFields" is not allowed',
|
||||
validation: {
|
||||
source: 'query',
|
||||
keys: ['searchFields'],
|
||||
},
|
||||
message: '[request query.searchFields]: definition for this key is missing',
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
@ -135,7 +133,7 @@ export default function({ getService }) {
|
|||
await supertest
|
||||
.get('/api/kibana/management/saved_objects/_find?type=visualization')
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body).to.eql({
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
|
@ -149,7 +147,7 @@ export default function({ getService }) {
|
|||
await supertest
|
||||
.get('/api/kibana/management/saved_objects/_find?type=wigwags')
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body).to.eql({
|
||||
page: 1,
|
||||
per_page: 20,
|
||||
|
@ -164,15 +162,12 @@ export default function({ getService }) {
|
|||
await supertest
|
||||
.get('/api/kibana/management/saved_objects/_find')
|
||||
.expect(400)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body).to.eql({
|
||||
error: 'Bad Request',
|
||||
message: 'child "type" fails because ["type" is required]',
|
||||
message:
|
||||
'[request query.type]: expected at least one defined value but got [undefined]',
|
||||
statusCode: 400,
|
||||
validation: {
|
||||
keys: ['type'],
|
||||
source: 'query',
|
||||
},
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
@ -184,7 +179,7 @@ export default function({ getService }) {
|
|||
'/api/kibana/management/saved_objects/_find?type=visualization&page=100&perPage=100'
|
||||
)
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body).to.eql({
|
||||
page: 100,
|
||||
per_page: 100,
|
||||
|
@ -199,29 +194,25 @@ export default function({ getService }) {
|
|||
await supertest
|
||||
.get('/api/kibana/management/saved_objects/_find?type=url&searchFields=a')
|
||||
.expect(400)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body).to.eql({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: '"searchFields" is not allowed',
|
||||
validation: {
|
||||
source: 'query',
|
||||
keys: ['searchFields'],
|
||||
},
|
||||
message: '[request query.searchFields]: definition for this key is missing',
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('meta attributes injected properly', () => {
|
||||
before(() => esArchiver.load('management/saved_objects'));
|
||||
after(() => esArchiver.unload('management/saved_objects'));
|
||||
before(() => esArchiver.load('management/saved_objects/search'));
|
||||
after(() => esArchiver.unload('management/saved_objects/search'));
|
||||
|
||||
it('should inject meta attributes for searches', async () =>
|
||||
await supertest
|
||||
.get('/api/kibana/management/saved_objects/_find?type=search')
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body.saved_objects).to.have.length(1);
|
||||
expect(resp.body.saved_objects[0].meta).to.eql({
|
||||
icon: 'discoverApp',
|
||||
|
@ -239,7 +230,7 @@ export default function({ getService }) {
|
|||
await supertest
|
||||
.get('/api/kibana/management/saved_objects/_find?type=dashboard')
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body.saved_objects).to.have.length(1);
|
||||
expect(resp.body.saved_objects[0].meta).to.eql({
|
||||
icon: 'dashboardApp',
|
||||
|
@ -257,7 +248,7 @@ export default function({ getService }) {
|
|||
await supertest
|
||||
.get('/api/kibana/management/saved_objects/_find?type=visualization')
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body.saved_objects).to.have.length(2);
|
||||
expect(resp.body.saved_objects[0].meta).to.eql({
|
||||
icon: 'visualizeApp',
|
||||
|
@ -285,7 +276,7 @@ export default function({ getService }) {
|
|||
await supertest
|
||||
.get('/api/kibana/management/saved_objects/_find?type=index-pattern')
|
||||
.expect(200)
|
||||
.then(resp => {
|
||||
.then((resp: Response) => {
|
||||
expect(resp.body.saved_objects).to.have.length(1);
|
||||
expect(resp.body.saved_objects[0].meta).to.eql({
|
||||
icon: 'indexPatternApp',
|
|
@ -17,13 +17,12 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { registerFind } from './saved_objects/find';
|
||||
import { registerRelationships } from './saved_objects/relationships';
|
||||
import { registerScrollForExportRoute, registerScrollForCountRoute } from './saved_objects/scroll';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function managementApi(server) {
|
||||
registerRelationships(server);
|
||||
registerFind(server);
|
||||
registerScrollForExportRoute(server);
|
||||
registerScrollForCountRoute(server);
|
||||
export default function({ loadTestFile }: FtrProviderContext) {
|
||||
describe('saved objects management apis', () => {
|
||||
loadTestFile(require.resolve('./find'));
|
||||
loadTestFile(require.resolve('./relationships'));
|
||||
loadTestFile(require.resolve('./scroll_count'));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,423 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
const responseSchema = schema.arrayOf(
|
||||
schema.object({
|
||||
id: schema.string(),
|
||||
type: schema.string(),
|
||||
relationship: schema.oneOf([schema.literal('parent'), schema.literal('child')]),
|
||||
meta: schema.object({
|
||||
title: schema.string(),
|
||||
icon: schema.string(),
|
||||
editUrl: schema.string(),
|
||||
inAppUrl: schema.object({
|
||||
path: schema.string(),
|
||||
uiCapabilitiesPath: schema.string(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
describe('relationships', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('management/saved_objects/relationships');
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('management/saved_objects/relationships');
|
||||
});
|
||||
|
||||
const baseApiUrl = `/api/kibana/management/saved_objects/relationships`;
|
||||
const defaultTypes = ['visualization', 'index-pattern', 'search', 'dashboard'];
|
||||
|
||||
const relationshipsUrl = (type: string, id: string, types: string[] = defaultTypes) => {
|
||||
const typesQuery = types.map(t => `savedObjectTypes=${t}`).join('&');
|
||||
return `${baseApiUrl}/${type}/${id}?${typesQuery}`;
|
||||
};
|
||||
|
||||
describe('searches', () => {
|
||||
it('should validate search response schema', async () => {
|
||||
const resp = await supertest
|
||||
.get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357'))
|
||||
.expect(200);
|
||||
|
||||
expect(() => {
|
||||
responseSchema.validate(resp.body);
|
||||
}).not.to.throwError();
|
||||
});
|
||||
|
||||
it('should work for searches', async () => {
|
||||
const resp = await supertest
|
||||
.get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357'))
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: '8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
type: 'index-pattern',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
title: 'saved_objects*',
|
||||
icon: 'indexPatternApp',
|
||||
editUrl: '/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path:
|
||||
'/app/kibana#/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'management.kibana.index_patterns',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
title: 'VisualizationFromSavedSearch',
|
||||
icon: 'visualizeApp',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should filter based on savedObjectTypes', async () => {
|
||||
const resp = await supertest
|
||||
.get(
|
||||
relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357', ['visualization'])
|
||||
)
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: '8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
type: 'index-pattern',
|
||||
meta: {
|
||||
icon: 'indexPatternApp',
|
||||
title: 'saved_objects*',
|
||||
editUrl: '/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path:
|
||||
'/app/kibana#/management/kibana/index_patterns/8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'management.kibana.index_patterns',
|
||||
},
|
||||
},
|
||||
relationship: 'child',
|
||||
},
|
||||
{
|
||||
id: 'a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
meta: {
|
||||
icon: 'visualizeApp',
|
||||
title: 'VisualizationFromSavedSearch',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
relationship: 'parent',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// TODO: https://github.com/elastic/kibana/issues/19713 causes this test to fail.
|
||||
it.skip('should return 404 if search finds no results', async () => {
|
||||
await supertest
|
||||
.get(relationshipsUrl('search', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'))
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dashboards', () => {
|
||||
it('should validate dashboard response schema', async () => {
|
||||
const resp = await supertest
|
||||
.get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357'))
|
||||
.expect(200);
|
||||
|
||||
expect(() => {
|
||||
responseSchema.validate(resp.body);
|
||||
}).not.to.throwError();
|
||||
});
|
||||
|
||||
it('should work for dashboards', async () => {
|
||||
const resp = await supertest
|
||||
.get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357'))
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: 'add810b0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
icon: 'visualizeApp',
|
||||
title: 'Visualization',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
icon: 'visualizeApp',
|
||||
title: 'VisualizationFromSavedSearch',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should filter based on savedObjectTypes', async () => {
|
||||
const resp = await supertest
|
||||
.get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357', ['search']))
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: 'add810b0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
meta: {
|
||||
icon: 'visualizeApp',
|
||||
title: 'Visualization',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
relationship: 'child',
|
||||
},
|
||||
{
|
||||
id: 'a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
meta: {
|
||||
icon: 'visualizeApp',
|
||||
title: 'VisualizationFromSavedSearch',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/a42c0580-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
relationship: 'child',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// TODO: https://github.com/elastic/kibana/issues/19713 causes this test to fail.
|
||||
it.skip('should return 404 if dashboard finds no results', async () => {
|
||||
await supertest
|
||||
.get(relationshipsUrl('dashboard', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'))
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('visualizations', () => {
|
||||
it('should validate visualization response schema', async () => {
|
||||
const resp = await supertest
|
||||
.get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357'))
|
||||
.expect(200);
|
||||
|
||||
expect(() => {
|
||||
responseSchema.validate(resp.body);
|
||||
}).not.to.throwError();
|
||||
});
|
||||
|
||||
it('should work for visualizations', async () => {
|
||||
const resp = await supertest
|
||||
.get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357'))
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: '960372e0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'search',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
icon: 'discoverApp',
|
||||
title: 'OneRecord',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'b70c7ae0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'dashboard',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
icon: 'dashboardApp',
|
||||
title: 'Dashboard',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedDashboards/b70c7ae0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/dashboard/b70c7ae0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should filter based on savedObjectTypes', async () => {
|
||||
const resp = await supertest
|
||||
.get(
|
||||
relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357', ['search'])
|
||||
)
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: '960372e0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'search',
|
||||
meta: {
|
||||
icon: 'discoverApp',
|
||||
title: 'OneRecord',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
},
|
||||
},
|
||||
relationship: 'child',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return 404 if visualizations finds no results', async () => {
|
||||
await supertest
|
||||
.get(relationshipsUrl('visualization', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'))
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('index patterns', () => {
|
||||
it('should validate visualization response schema', async () => {
|
||||
const resp = await supertest
|
||||
.get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357'))
|
||||
.expect(200);
|
||||
|
||||
expect(() => {
|
||||
responseSchema.validate(resp.body);
|
||||
}).not.to.throwError();
|
||||
});
|
||||
|
||||
it('should work for index patterns', async () => {
|
||||
const resp = await supertest
|
||||
.get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357'))
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: '960372e0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'search',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
icon: 'discoverApp',
|
||||
title: 'OneRecord',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'add810b0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
icon: 'visualizeApp',
|
||||
title: 'Visualization',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/visualize/edit/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should filter based on savedObjectTypes', async () => {
|
||||
const resp = await supertest
|
||||
.get(
|
||||
relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357', ['search'])
|
||||
)
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
{
|
||||
id: '960372e0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'search',
|
||||
meta: {
|
||||
icon: 'discoverApp',
|
||||
title: 'OneRecord',
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/discover/960372e0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
},
|
||||
},
|
||||
relationship: 'parent',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return 404 if index pattern finds no results', async () => {
|
||||
await supertest
|
||||
.get(relationshipsUrl('index-pattern', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'))
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { SuperTest, Test } from 'supertest';
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
const apiUrl = '/api/kibana/management/saved_objects/scroll/counts';
|
||||
const defaultTypes = ['visualization', 'index-pattern', 'search', 'dashboard'];
|
||||
|
||||
export default function({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest') as SuperTest<Test>;
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
describe('scroll_count', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('management/saved_objects/scroll_count');
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('management/saved_objects/scroll_count');
|
||||
});
|
||||
|
||||
it('returns the count for each included types', async () => {
|
||||
const res = await supertest
|
||||
.post(apiUrl)
|
||||
.send({
|
||||
typesToInclude: defaultTypes,
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(res.body).to.eql({
|
||||
dashboard: 2,
|
||||
'index-pattern': 1,
|
||||
search: 1,
|
||||
visualization: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('only returns count for types to include', async () => {
|
||||
const res = await supertest
|
||||
.post(apiUrl)
|
||||
.send({
|
||||
typesToInclude: ['dashboard', 'search'],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(res.body).to.eql({
|
||||
dashboard: 2,
|
||||
search: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('filters on title when `searchString` is provided', async () => {
|
||||
const res = await supertest
|
||||
.post(apiUrl)
|
||||
.send({
|
||||
typesToInclude: defaultTypes,
|
||||
searchString: 'Amazing',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(res.body).to.eql({
|
||||
dashboard: 1,
|
||||
visualization: 1,
|
||||
'index-pattern': 0,
|
||||
search: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('includes all requested types even when none match the search', async () => {
|
||||
const res = await supertest
|
||||
.post(apiUrl)
|
||||
.send({
|
||||
typesToInclude: ['dashboard', 'search', 'visualization'],
|
||||
searchString: 'nothing-will-match',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(res.body).to.eql({
|
||||
dashboard: 0,
|
||||
visualization: 0,
|
||||
search: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,213 @@
|
|||
{
|
||||
"type": "index",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"settings": {
|
||||
"index": {
|
||||
"number_of_shards": "1",
|
||||
"auto_expand_replicas": "0-1",
|
||||
"number_of_replicas": "0"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"defaultIndex": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"telemetry:optIn": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"index-pattern": {
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,283 @@
|
|||
{
|
||||
"type": "index",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"settings": {
|
||||
"index": {
|
||||
"number_of_shards": "1",
|
||||
"auto_expand_replicas": "0-1",
|
||||
"number_of_replicas": "0"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"defaultIndex": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"telemetry:optIn": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"graph-workspace": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"numLinks": {
|
||||
"type": "integer"
|
||||
},
|
||||
"numVertices": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"wsState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"index-pattern": {
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion-sheet": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue