Skip to content

Commit

Permalink
Add LegacyUrlAlias and SavedObjectsClient.resolve()
Browse files Browse the repository at this point in the history
New integration tests (except for Jest integration tests) are
intentionally excluded. Will be added in a separate commit after
conversion transforms include aliases.
  • Loading branch information
jportner committed Nov 30, 2020
1 parent 73957ff commit 82fa05a
Show file tree
Hide file tree
Showing 26 changed files with 1,098 additions and 35 deletions.
23 changes: 23 additions & 0 deletions src/core/server/saved_objects/object_types/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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.
*/

/**
* @internal
*/
export const LEGACY_URL_ALIAS_TYPE = 'legacy-url-alias';
22 changes: 22 additions & 0 deletions src/core/server/saved_objects/object_types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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.
*/

export { LEGACY_URL_ALIAS_TYPE } from './constants';
export { LegacyUrlAlias } from './types';
export { registerCoreObjectTypes } from './registration';
36 changes: 36 additions & 0 deletions src/core/server/saved_objects/object_types/registration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 { typeRegistryMock } from '../saved_objects_type_registry.mock';
import { LEGACY_URL_ALIAS_TYPE } from './constants';
import { registerCoreObjectTypes } from './registration';

describe('Core saved object types registration', () => {
describe('#registerCoreObjectTypes', () => {
it('registers all expected types', () => {
const typeRegistry = typeRegistryMock.create();
registerCoreObjectTypes(typeRegistry);

expect(typeRegistry.registerType).toHaveBeenCalledTimes(1);
expect(typeRegistry.registerType).toHaveBeenCalledWith(
expect.objectContaining({ name: LEGACY_URL_ALIAS_TYPE })
);
});
});
});
46 changes: 46 additions & 0 deletions src/core/server/saved_objects/object_types/registration.ts
Original file line number Diff line number Diff line change
@@ -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 { LEGACY_URL_ALIAS_TYPE } from './constants';
import { ISavedObjectTypeRegistry, SavedObjectTypeRegistry } from '..';

const legacyUrlAliasMappings = {
properties: {
targetNamespace: { type: 'keyword' },
targetType: { type: 'keyword' },
targetId: { type: 'keyword' },
lastResolved: { type: 'date' },
resolveCounter: { type: 'integer' },
disabled: { type: 'boolean' },
},
};

/**
* @internal
*/
export function registerCoreObjectTypes(
typeRegistry: ISavedObjectTypeRegistry & Pick<SavedObjectTypeRegistry, 'registerType'>
) {
typeRegistry.registerType({
name: LEGACY_URL_ALIAS_TYPE,
namespaceType: 'agnostic',
mappings: legacyUrlAliasMappings,
hidden: true,
});
}
30 changes: 30 additions & 0 deletions src/core/server/saved_objects/object_types/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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.
*/

/**
* @internal
*/
export interface LegacyUrlAlias {
targetNamespace: string;
targetType: string;
targetId: string;
lastResolved?: string;
resolveCounter?: number;
disabled?: boolean;
}
2 changes: 2 additions & 0 deletions src/core/server/saved_objects/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Logger } from '../../logging';
import { SavedObjectConfig } from '../saved_objects_config';
import { IKibanaMigrator } from '../migrations';
import { registerGetRoute } from './get';
import { registerResolveRoute } from './resolve';
import { registerCreateRoute } from './create';
import { registerDeleteRoute } from './delete';
import { registerFindRoute } from './find';
Expand Down Expand Up @@ -49,6 +50,7 @@ export function registerRoutes({
const router = http.createRouter('/api/saved_objects/');

registerGetRoute(router);
registerResolveRoute(router);
registerCreateRoute(router);
registerDeleteRoute(router);
registerFindRoute(router);
Expand Down
Original file line number Diff line number Diff line change
@@ -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 supertest from 'supertest';
import { registerResolveRoute } from '../resolve';
import { ContextService } from '../../../context';
import { savedObjectsClientMock } from '../../service/saved_objects_client.mock';
import { HttpService, InternalHttpServiceSetup } from '../../../http';
import { createHttpServer, createCoreContext } from '../../../http/test_utils';
import { coreMock } from '../../../mocks';

const coreId = Symbol('core');

describe('GET /api/saved_objects/resolve/{type}/{id}', () => {
let server: HttpService;
let httpSetup: InternalHttpServiceSetup;
let handlerContext: ReturnType<typeof coreMock.createRequestHandlerContext>;
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;

beforeEach(async () => {
const coreContext = createCoreContext({ coreId });
server = createHttpServer(coreContext);

const contextService = new ContextService(coreContext);
httpSetup = await server.setup({
context: contextService.setup({ pluginDependencies: new Map() }),
});

handlerContext = coreMock.createRequestHandlerContext();
savedObjectsClient = handlerContext.savedObjects.client;

httpSetup.registerRouteHandlerContext(coreId, 'core', async (ctx, req, res) => {
return handlerContext;
});

const router = httpSetup.createRouter('/api/saved_objects/');
registerResolveRoute(router);

await server.start();
});

afterEach(async () => {
await server.stop();
});

it('formats successful response', async () => {
const clientResponse = {
saved_object: {
id: 'logstash-*',
title: 'logstash-*',
type: 'logstash-type',
attributes: {},
timeFieldName: '@timestamp',
notExpandable: true,
references: [],
},
outcome: 'exactMatch' as 'exactMatch',
};

savedObjectsClient.resolve.mockResolvedValue(clientResponse);

const result = await supertest(httpSetup.server.listener)
.get('/api/saved_objects/resolve/index-pattern/logstash-*')
.expect(200);

expect(result.body).toEqual(clientResponse);
});

it('calls upon savedObjectClient.resolve', async () => {
await supertest(httpSetup.server.listener)
.get('/api/saved_objects/resolve/index-pattern/logstash-*')
.expect(200);

expect(savedObjectsClient.resolve).toHaveBeenCalled();

const args = savedObjectsClient.resolve.mock.calls[0];
expect(args).toEqual(['index-pattern', 'logstash-*']);
});
});
40 changes: 40 additions & 0 deletions src/core/server/saved_objects/routes/resolve.ts
Original file line number Diff line number Diff line change
@@ -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 { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';

export const registerResolveRoute = (router: IRouter) => {
router.get(
{
path: '/resolve/{type}/{id}',
validate: {
params: schema.object({
type: schema.string(),
id: schema.string(),
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
const { type, id } = req.params;
const result = await context.core.savedObjects.client.resolve(type, id);
return res.ok({ body: result });
})
);
};
2 changes: 2 additions & 0 deletions src/core/server/saved_objects/saved_objects_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { NodesVersionCompatibility } from '../elasticsearch/version_check/ensure
import { SavedObjectsRepository } from './service/lib/repository';

jest.mock('./service/lib/repository');
jest.mock('./object_types');

describe('SavedObjectsService', () => {
const createCoreContext = ({
Expand Down Expand Up @@ -142,6 +143,7 @@ describe('SavedObjectsService', () => {

describe('#registerType', () => {
it('registers the type to the internal typeRegistry', async () => {
// we mocked registerCoreObjectTypes above, so this test case only reflects direct calls to the registerType method
const coreContext = createCoreContext();
const soService = new SavedObjectsService(coreContext);
const setup = await soService.setup(createSetupDeps());
Expand Down
3 changes: 3 additions & 0 deletions src/core/server/saved_objects/saved_objects_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { registerRoutes } from './routes';
import { ServiceStatus } from '../status';
import { calculateStatus$ } from './status';
import { createMigrationEsClient } from './migrations/core/';
import { registerCoreObjectTypes } from './object_types';
/**
* Saved Objects is Kibana's data persistence mechanism allowing plugins to
* use Elasticsearch for storing and querying state. The SavedObjectsServiceSetup API exposes methods
Expand Down Expand Up @@ -306,6 +307,8 @@ export class SavedObjectsService
migratorPromise: this.migrator$.pipe(first()).toPromise(),
});

registerCoreObjectTypes(this.typeRegistry);

return {
status$: calculateStatus$(
this.migrator$.pipe(switchMap((migrator) => migrator.getStatus$())),
Expand Down
22 changes: 22 additions & 0 deletions src/core/server/saved_objects/serialization/serializer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { SavedObjectsSerializer } from './serializer';
import { SavedObjectsRawDoc } from './types';
import { typeRegistryMock } from '../saved_objects_type_registry.mock';
import { encodeVersion } from '../version';
import { LEGACY_URL_ALIAS_TYPE } from '../object_types';

let typeRegistry = typeRegistryMock.create();
typeRegistry.isNamespaceAgnostic.mockReturnValue(true);
Expand Down Expand Up @@ -1269,3 +1270,24 @@ describe('#generateRawId', () => {
});
});
});

describe('#generateRawLegacyUrlAliasId', () => {
describe(`returns expected value`, () => {
const expected = `${LEGACY_URL_ALIAS_TYPE}:foo:bar:baz`;

test(`for single-namespace types`, () => {
const id = singleNamespaceSerializer.generateRawLegacyUrlAliasId('foo', 'bar', 'baz');
expect(id).toEqual(expected);
});

test(`for multi-namespace types`, () => {
const id = multiNamespaceSerializer.generateRawLegacyUrlAliasId('foo', 'bar', 'baz');
expect(id).toEqual(expected);
});

test(`for namespace-agnostic types`, () => {
const id = namespaceAgnosticSerializer.generateRawLegacyUrlAliasId('foo', 'bar', 'baz');
expect(id).toEqual(expected);
});
});
});
Loading

0 comments on commit 82fa05a

Please sign in to comment.