Skip to content

Commit

Permalink
core route handler context is now lazy (#78957)
Browse files Browse the repository at this point in the history
* Core route handler context is now lazy

* Removing `coreStart` intermediate variable

* Adding unit tests for CoreRouteHandlerContext

Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
kobelb and elasticmachine authored Oct 1, 2020
1 parent 727d626 commit af517a0
Show file tree
Hide file tree
Showing 3 changed files with 373 additions and 19 deletions.
239 changes: 239 additions & 0 deletions src/core/server/core_route_handler_context.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/*
* 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 { CoreRouteHandlerContext } from './core_route_handler_context';
import { coreMock, httpServerMock } from './mocks';

describe('#auditor', () => {
test('returns the results of coreStart.audiTrail.asScoped', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const auditor = context.auditor;
expect(auditor).toBe(coreStart.auditTrail.asScoped.mock.results[0].value);
});

test('lazily created', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

expect(coreStart.auditTrail.asScoped).not.toHaveBeenCalled();
const auditor = context.auditor;
expect(coreStart.auditTrail.asScoped).toHaveBeenCalled();
expect(auditor).toBeDefined();
});

test('only creates one instance', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const auditor1 = context.auditor;
const auditor2 = context.auditor;
expect(coreStart.auditTrail.asScoped.mock.calls.length).toBe(1);
const mockResult = coreStart.auditTrail.asScoped.mock.results[0].value;
expect(auditor1).toBe(mockResult);
expect(auditor2).toBe(mockResult);
});
});

describe('#elasticsearch', () => {
describe('#client', () => {
test('returns the results of coreStart.elasticsearch.client.asScoped', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const client = context.elasticsearch.client;
expect(client).toBe(coreStart.elasticsearch.client.asScoped.mock.results[0].value);
});

test('lazily created', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

expect(coreStart.elasticsearch.client.asScoped).not.toHaveBeenCalled();
const client = context.elasticsearch.client;
expect(coreStart.elasticsearch.client.asScoped).toHaveBeenCalled();
expect(client).toBeDefined();
});

test('only creates one instance', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const client1 = context.elasticsearch.client;
const client2 = context.elasticsearch.client;
expect(coreStart.elasticsearch.client.asScoped.mock.calls.length).toBe(1);
const mockResult = coreStart.elasticsearch.client.asScoped.mock.results[0].value;
expect(client1).toBe(mockResult);
expect(client2).toBe(mockResult);
});
});

describe('#legacy', () => {
describe('#client', () => {
test('returns the results of coreStart.elasticsearch.legacy.client.asScoped', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const client = context.elasticsearch.legacy.client;
expect(client).toBe(coreStart.elasticsearch.legacy.client.asScoped.mock.results[0].value);
});

test('lazily created', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

expect(coreStart.elasticsearch.legacy.client.asScoped).not.toHaveBeenCalled();
const client = context.elasticsearch.legacy.client;
expect(coreStart.elasticsearch.legacy.client.asScoped).toHaveBeenCalled();
expect(client).toBeDefined();
});

test('only creates one instance', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const client1 = context.elasticsearch.legacy.client;
const client2 = context.elasticsearch.legacy.client;
expect(coreStart.elasticsearch.legacy.client.asScoped.mock.calls.length).toBe(1);
const mockResult = coreStart.elasticsearch.legacy.client.asScoped.mock.results[0].value;
expect(client1).toBe(mockResult);
expect(client2).toBe(mockResult);
});
});
});
});

describe('#savedObjects', () => {
describe('#client', () => {
test('returns the results of coreStart.savedObjects.getScopedClient', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const client = context.savedObjects.client;
expect(client).toBe(coreStart.savedObjects.getScopedClient.mock.results[0].value);
});

test('lazily created', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const savedObjects = context.savedObjects;
expect(coreStart.savedObjects.getScopedClient).not.toHaveBeenCalled();
const client = savedObjects.client;
expect(coreStart.savedObjects.getScopedClient).toHaveBeenCalled();
expect(client).toBeDefined();
});

test('only creates one instance', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const client1 = context.savedObjects.client;
const client2 = context.savedObjects.client;
expect(coreStart.savedObjects.getScopedClient.mock.calls.length).toBe(1);
const mockResult = coreStart.savedObjects.getScopedClient.mock.results[0].value;
expect(client1).toBe(mockResult);
expect(client2).toBe(mockResult);
});
});

describe('#typeRegistry', () => {
test('returns the results of coreStart.savedObjects.getTypeRegistry', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const typeRegistry = context.savedObjects.typeRegistry;
expect(typeRegistry).toBe(coreStart.savedObjects.getTypeRegistry.mock.results[0].value);
});

test('lazily created', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

expect(coreStart.savedObjects.getTypeRegistry).not.toHaveBeenCalled();
const typeRegistry = context.savedObjects.typeRegistry;
expect(coreStart.savedObjects.getTypeRegistry).toHaveBeenCalled();
expect(typeRegistry).toBeDefined();
});

test('only creates one instance', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const typeRegistry1 = context.savedObjects.typeRegistry;
const typeRegistry2 = context.savedObjects.typeRegistry;
expect(coreStart.savedObjects.getTypeRegistry.mock.calls.length).toBe(1);
const mockResult = coreStart.savedObjects.getTypeRegistry.mock.results[0].value;
expect(typeRegistry1).toBe(mockResult);
expect(typeRegistry2).toBe(mockResult);
});
});
});

describe('#uiSettings', () => {
describe('#client', () => {
test('returns the results of coreStart.uiSettings.asScopedToClient', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const client = context.uiSettings.client;
expect(client).toBe(coreStart.uiSettings.asScopedToClient.mock.results[0].value);
});

test('lazily created', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

expect(coreStart.uiSettings.asScopedToClient).not.toHaveBeenCalled();
const client = context.uiSettings.client;
expect(coreStart.uiSettings.asScopedToClient).toHaveBeenCalled();
expect(client).toBeDefined();
});

test('only creates one instance', () => {
const request = httpServerMock.createKibanaRequest();
const coreStart = coreMock.createInternalStart();
const context = new CoreRouteHandlerContext(coreStart, request);

const client1 = context.uiSettings.client;
const client2 = context.uiSettings.client;
expect(coreStart.uiSettings.asScopedToClient.mock.calls.length).toBe(1);
const mockResult = coreStart.uiSettings.asScopedToClient.mock.results[0].value;
expect(client1).toBe(mockResult);
expect(client2).toBe(mockResult);
});
});
});
132 changes: 132 additions & 0 deletions src/core/server/core_route_handler_context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* 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.
*/

// eslint-disable-next-line max-classes-per-file
import { InternalCoreStart } from './internal_types';
import { KibanaRequest } from './http/router';
import { SavedObjectsClientContract } from './saved_objects/types';
import { InternalSavedObjectsServiceStart, ISavedObjectTypeRegistry } from './saved_objects';
import {
InternalElasticsearchServiceStart,
IScopedClusterClient,
LegacyScopedClusterClient,
} from './elasticsearch';
import { Auditor } from './audit_trail';
import { InternalUiSettingsServiceStart, IUiSettingsClient } from './ui_settings';

class CoreElasticsearchRouteHandlerContext {
#client?: IScopedClusterClient;
#legacy?: {
client: Pick<LegacyScopedClusterClient, 'callAsInternalUser' | 'callAsCurrentUser'>;
};

constructor(
private readonly elasticsearchStart: InternalElasticsearchServiceStart,
private readonly request: KibanaRequest
) {}

public get client() {
if (this.#client == null) {
this.#client = this.elasticsearchStart.client.asScoped(this.request);
}
return this.#client;
}

public get legacy() {
if (this.#legacy == null) {
this.#legacy = {
client: this.elasticsearchStart.legacy.client.asScoped(this.request),
};
}
return this.#legacy;
}
}

class CoreSavedObjectsRouteHandlerContext {
constructor(
private readonly savedObjectsStart: InternalSavedObjectsServiceStart,
private readonly request: KibanaRequest
) {}
#scopedSavedObjectsClient?: SavedObjectsClientContract;
#typeRegistry?: ISavedObjectTypeRegistry;

public get client() {
if (this.#scopedSavedObjectsClient == null) {
this.#scopedSavedObjectsClient = this.savedObjectsStart.getScopedClient(this.request);
}
return this.#scopedSavedObjectsClient;
}

public get typeRegistry() {
if (this.#typeRegistry == null) {
this.#typeRegistry = this.savedObjectsStart.getTypeRegistry();
}
return this.#typeRegistry;
}
}

class CoreUiSettingsRouteHandlerContext {
#client?: IUiSettingsClient;
constructor(
private readonly uiSettingsStart: InternalUiSettingsServiceStart,
private readonly savedObjectsRouterHandlerContext: CoreSavedObjectsRouteHandlerContext
) {}

public get client() {
if (this.#client == null) {
this.#client = this.uiSettingsStart.asScopedToClient(
this.savedObjectsRouterHandlerContext.client
);
}
return this.#client;
}
}

export class CoreRouteHandlerContext {
#auditor?: Auditor;

readonly elasticsearch: CoreElasticsearchRouteHandlerContext;
readonly savedObjects: CoreSavedObjectsRouteHandlerContext;
readonly uiSettings: CoreUiSettingsRouteHandlerContext;

constructor(
private readonly coreStart: InternalCoreStart,
private readonly request: KibanaRequest
) {
this.elasticsearch = new CoreElasticsearchRouteHandlerContext(
this.coreStart.elasticsearch,
this.request
);
this.savedObjects = new CoreSavedObjectsRouteHandlerContext(
this.coreStart.savedObjects,
this.request
);
this.uiSettings = new CoreUiSettingsRouteHandlerContext(
this.coreStart.uiSettings,
this.savedObjects
);
}

public get auditor() {
if (this.#auditor == null) {
this.#auditor = this.coreStart.auditTrail.asScoped(this.request);
}
return this.#auditor;
}
}
Loading

0 comments on commit af517a0

Please sign in to comment.