mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Dashboard First] Genericize Attribute Service (#76057)
Genericized attribute service, with custom save and unwrap methods and added unit tests.
This commit is contained in:
parent
b3eaf9b629
commit
260643483e
8 changed files with 349 additions and 77 deletions
|
@ -71,11 +71,7 @@ export class BookEmbeddable
|
|||
|
||||
constructor(
|
||||
initialInput: BookEmbeddableInput,
|
||||
private attributeService: AttributeService<
|
||||
BookSavedObjectAttributes,
|
||||
BookByValueInput,
|
||||
BookByReferenceInput
|
||||
>,
|
||||
private attributeService: AttributeService<BookSavedObjectAttributes>,
|
||||
{
|
||||
parent,
|
||||
}: {
|
||||
|
@ -99,18 +95,21 @@ export class BookEmbeddable
|
|||
});
|
||||
}
|
||||
|
||||
inputIsRefType = (input: BookEmbeddableInput): input is BookByReferenceInput => {
|
||||
readonly inputIsRefType = (input: BookEmbeddableInput): input is BookByReferenceInput => {
|
||||
return this.attributeService.inputIsRefType(input);
|
||||
};
|
||||
|
||||
getInputAsValueType = async (): Promise<BookByValueInput> => {
|
||||
readonly getInputAsValueType = async (): Promise<BookByValueInput> => {
|
||||
const input = this.attributeService.getExplicitInputFromEmbeddable(this);
|
||||
return this.attributeService.getInputAsValueType(input);
|
||||
};
|
||||
|
||||
getInputAsRefType = async (): Promise<BookByReferenceInput> => {
|
||||
readonly getInputAsRefType = async (): Promise<BookByReferenceInput> => {
|
||||
const input = this.attributeService.getExplicitInputFromEmbeddable(this);
|
||||
return this.attributeService.getInputAsRefType(input, { showSaveModal: true });
|
||||
return this.attributeService.getInputAsRefType(input, {
|
||||
showSaveModal: true,
|
||||
saveModalTitle: this.getTitle(),
|
||||
});
|
||||
};
|
||||
|
||||
public render(node: HTMLElement) {
|
||||
|
|
|
@ -31,8 +31,6 @@ import {
|
|||
BOOK_EMBEDDABLE,
|
||||
BookEmbeddableInput,
|
||||
BookEmbeddableOutput,
|
||||
BookByValueInput,
|
||||
BookByReferenceInput,
|
||||
} from './book_embeddable';
|
||||
import { CreateEditBookComponent } from './create_edit_book_component';
|
||||
import { OverlayStart } from '../../../../src/core/public';
|
||||
|
@ -66,11 +64,7 @@ export class BookEmbeddableFactoryDefinition
|
|||
getIconForSavedObject: () => 'pencil',
|
||||
};
|
||||
|
||||
private attributeService?: AttributeService<
|
||||
BookSavedObjectAttributes,
|
||||
BookByValueInput,
|
||||
BookByReferenceInput
|
||||
>;
|
||||
private attributeService?: AttributeService<BookSavedObjectAttributes>;
|
||||
|
||||
constructor(private getStartServices: () => Promise<StartServices>) {}
|
||||
|
||||
|
@ -126,9 +120,7 @@ export class BookEmbeddableFactoryDefinition
|
|||
private async getAttributeService() {
|
||||
if (!this.attributeService) {
|
||||
this.attributeService = await (await this.getStartServices()).getAttributeService<
|
||||
BookSavedObjectAttributes,
|
||||
BookByValueInput,
|
||||
BookByReferenceInput
|
||||
BookSavedObjectAttributes
|
||||
>(this.type);
|
||||
}
|
||||
return this.attributeService!;
|
||||
|
|
|
@ -57,13 +57,13 @@ export const createEditBookAction = (getStartServices: () => Promise<StartServic
|
|||
},
|
||||
execute: async ({ embeddable }: ActionContext) => {
|
||||
const { openModal, getAttributeService } = await getStartServices();
|
||||
const attributeService = getAttributeService<
|
||||
BookSavedObjectAttributes,
|
||||
BookByValueInput,
|
||||
BookByReferenceInput
|
||||
>(BOOK_SAVED_OBJECT);
|
||||
const attributeService = getAttributeService<BookSavedObjectAttributes>(BOOK_SAVED_OBJECT);
|
||||
const onSave = async (attributes: BookSavedObjectAttributes, useRefType: boolean) => {
|
||||
const newInput = await attributeService.wrapAttributes(attributes, useRefType, embeddable);
|
||||
const newInput = await attributeService.wrapAttributes(
|
||||
attributes,
|
||||
useRefType,
|
||||
attributeService.getExplicitInputFromEmbeddable(embeddable)
|
||||
);
|
||||
if (!useRefType && (embeddable.getInput() as SavedObjectEmbeddableInput).savedObjectId) {
|
||||
// Set the saved object ID to null so that update input will remove the existing savedObjectId...
|
||||
(newInput as BookByValueInput & { savedObjectId: unknown }).savedObjectId = null;
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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 { ATTRIBUTE_SERVICE_KEY } from './attribute_service';
|
||||
import { mockAttributeService } from './attribute_service_mock';
|
||||
import { coreMock } from '../../../../core/public/mocks';
|
||||
|
||||
interface TestAttributes {
|
||||
title: string;
|
||||
testAttr1?: string;
|
||||
testAttr2?: { array: unknown[]; testAttr3: string };
|
||||
}
|
||||
|
||||
interface TestByValueInput {
|
||||
id: string;
|
||||
[ATTRIBUTE_SERVICE_KEY]: TestAttributes;
|
||||
}
|
||||
|
||||
describe('attributeService', () => {
|
||||
const defaultTestType = 'defaultTestType';
|
||||
let attributes: TestAttributes;
|
||||
let byValueInput: TestByValueInput;
|
||||
let byReferenceInput: { id: string; savedObjectId: string };
|
||||
|
||||
beforeEach(() => {
|
||||
attributes = {
|
||||
title: 'ultra title',
|
||||
testAttr1: 'neat first attribute',
|
||||
testAttr2: { array: [1, 2, 3], testAttr3: 'super attribute' },
|
||||
};
|
||||
byValueInput = {
|
||||
id: '456',
|
||||
attributes,
|
||||
};
|
||||
byReferenceInput = {
|
||||
id: '456',
|
||||
savedObjectId: '123',
|
||||
};
|
||||
});
|
||||
|
||||
describe('determining input type', () => {
|
||||
const defaultAttributeService = mockAttributeService<TestAttributes>(defaultTestType);
|
||||
const customAttributeService = mockAttributeService<TestAttributes, TestByValueInput>(
|
||||
defaultTestType
|
||||
);
|
||||
|
||||
it('can determine input type given default types', () => {
|
||||
expect(
|
||||
defaultAttributeService.inputIsRefType({ id: '456', savedObjectId: '123' })
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
defaultAttributeService.inputIsRefType({
|
||||
id: '456',
|
||||
attributes: { title: 'wow I am by value' },
|
||||
})
|
||||
).toBeFalsy();
|
||||
});
|
||||
it('can determine input type given custom types', () => {
|
||||
expect(
|
||||
customAttributeService.inputIsRefType({ id: '456', savedObjectId: '123' })
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
customAttributeService.inputIsRefType({
|
||||
id: '456',
|
||||
[ATTRIBUTE_SERVICE_KEY]: { title: 'wow I am by value' },
|
||||
})
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('unwrapping attributes', () => {
|
||||
it('can unwrap all default attributes when given reference type input', async () => {
|
||||
const core = coreMock.createStart();
|
||||
core.savedObjects.client.get = jest.fn().mockResolvedValueOnce({
|
||||
attributes,
|
||||
});
|
||||
const attributeService = mockAttributeService<TestAttributes>(
|
||||
defaultTestType,
|
||||
undefined,
|
||||
core
|
||||
);
|
||||
expect(await attributeService.unwrapAttributes(byReferenceInput)).toEqual(attributes);
|
||||
});
|
||||
|
||||
it('returns attributes when when given value type input', async () => {
|
||||
const attributeService = mockAttributeService<TestAttributes>(defaultTestType);
|
||||
expect(await attributeService.unwrapAttributes(byValueInput)).toEqual(attributes);
|
||||
});
|
||||
|
||||
it('runs attributes through a custom unwrap method', async () => {
|
||||
const core = coreMock.createStart();
|
||||
core.savedObjects.client.get = jest.fn().mockResolvedValueOnce({
|
||||
attributes,
|
||||
});
|
||||
const attributeService = mockAttributeService<TestAttributes>(
|
||||
defaultTestType,
|
||||
{
|
||||
customUnwrapMethod: (savedObject) => ({
|
||||
...savedObject.attributes,
|
||||
testAttr2: { array: [1, 2, 3, 4, 5], testAttr3: 'kibanana' },
|
||||
}),
|
||||
},
|
||||
core
|
||||
);
|
||||
expect(await attributeService.unwrapAttributes(byReferenceInput)).toEqual({
|
||||
...attributes,
|
||||
testAttr2: { array: [1, 2, 3, 4, 5], testAttr3: 'kibanana' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('wrapping attributes', () => {
|
||||
it('returns given attributes when use ref type is false', async () => {
|
||||
const attributeService = mockAttributeService<TestAttributes>(defaultTestType);
|
||||
expect(await attributeService.wrapAttributes(attributes, false)).toEqual({ attributes });
|
||||
});
|
||||
|
||||
it('updates existing saved object with new attributes when given id', async () => {
|
||||
const core = coreMock.createStart();
|
||||
const attributeService = mockAttributeService<TestAttributes>(
|
||||
defaultTestType,
|
||||
undefined,
|
||||
core
|
||||
);
|
||||
expect(await attributeService.wrapAttributes(attributes, true, byReferenceInput)).toEqual(
|
||||
byReferenceInput
|
||||
);
|
||||
expect(core.savedObjects.client.update).toHaveBeenCalledWith(
|
||||
defaultTestType,
|
||||
'123',
|
||||
attributes
|
||||
);
|
||||
});
|
||||
|
||||
it('creates new saved object with attributes when given no id', async () => {
|
||||
const core = coreMock.createStart();
|
||||
core.savedObjects.client.create = jest.fn().mockResolvedValueOnce({
|
||||
id: '678',
|
||||
});
|
||||
const attributeService = mockAttributeService<TestAttributes>(
|
||||
defaultTestType,
|
||||
undefined,
|
||||
core
|
||||
);
|
||||
expect(await attributeService.wrapAttributes(attributes, true)).toEqual({
|
||||
savedObjectId: '678',
|
||||
});
|
||||
expect(core.savedObjects.client.create).toHaveBeenCalledWith(defaultTestType, attributes);
|
||||
});
|
||||
|
||||
it('uses custom save method when given an id', async () => {
|
||||
const customSaveMethod = jest.fn().mockReturnValue({ id: '123' });
|
||||
const attributeService = mockAttributeService<TestAttributes>(defaultTestType, {
|
||||
customSaveMethod,
|
||||
});
|
||||
expect(await attributeService.wrapAttributes(attributes, true, byReferenceInput)).toEqual(
|
||||
byReferenceInput
|
||||
);
|
||||
expect(customSaveMethod).toHaveBeenCalledWith(
|
||||
defaultTestType,
|
||||
attributes,
|
||||
byReferenceInput.savedObjectId
|
||||
);
|
||||
});
|
||||
|
||||
it('uses custom save method given no id', async () => {
|
||||
const customSaveMethod = jest.fn().mockReturnValue({ id: '678' });
|
||||
const attributeService = mockAttributeService<TestAttributes>(defaultTestType, {
|
||||
customSaveMethod,
|
||||
});
|
||||
expect(await attributeService.wrapAttributes(attributes, true)).toEqual({
|
||||
savedObjectId: '678',
|
||||
});
|
||||
expect(customSaveMethod).toHaveBeenCalledWith(defaultTestType, attributes, undefined);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -19,11 +19,16 @@
|
|||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { get } from 'lodash';
|
||||
import {
|
||||
EmbeddableInput,
|
||||
SavedObjectEmbeddableInput,
|
||||
isSavedObjectEmbeddableInput,
|
||||
IEmbeddable,
|
||||
Container,
|
||||
EmbeddableStart,
|
||||
EmbeddableFactory,
|
||||
EmbeddableFactoryNotFoundError,
|
||||
} from '../embeddable_plugin';
|
||||
import {
|
||||
SavedObjectsClientContract,
|
||||
|
@ -34,17 +39,10 @@ import {
|
|||
} from '../../../../core/public';
|
||||
import {
|
||||
SavedObjectSaveModal,
|
||||
showSaveModal,
|
||||
OnSaveProps,
|
||||
SaveResult,
|
||||
checkForDuplicateTitle,
|
||||
} from '../../../saved_objects/public';
|
||||
import {
|
||||
EmbeddableStart,
|
||||
EmbeddableFactory,
|
||||
EmbeddableFactoryNotFoundError,
|
||||
Container,
|
||||
} from '../../../embeddable/public';
|
||||
|
||||
/**
|
||||
* The attribute service is a shared, generic service that embeddables can use to provide the functionality
|
||||
|
@ -52,26 +50,46 @@ import {
|
|||
* can also be used as a higher level wrapper to transform an embeddable input shape that references a saved object
|
||||
* into an embeddable input shape that contains that saved object's attributes by value.
|
||||
*/
|
||||
export const ATTRIBUTE_SERVICE_KEY = 'attributes';
|
||||
|
||||
export interface AttributeServiceOptions<A extends { title: string }> {
|
||||
customSaveMethod?: (
|
||||
type: string,
|
||||
attributes: A,
|
||||
savedObjectId?: string
|
||||
) => Promise<{ id: string }>;
|
||||
customUnwrapMethod?: (savedObject: SimpleSavedObject<A>) => A;
|
||||
}
|
||||
|
||||
export class AttributeService<
|
||||
SavedObjectAttributes extends { title: string },
|
||||
ValType extends EmbeddableInput & { attributes: SavedObjectAttributes },
|
||||
RefType extends SavedObjectEmbeddableInput
|
||||
ValType extends EmbeddableInput & {
|
||||
[ATTRIBUTE_SERVICE_KEY]: SavedObjectAttributes;
|
||||
} = EmbeddableInput & { [ATTRIBUTE_SERVICE_KEY]: SavedObjectAttributes },
|
||||
RefType extends SavedObjectEmbeddableInput = SavedObjectEmbeddableInput
|
||||
> {
|
||||
private embeddableFactory: EmbeddableFactory;
|
||||
private embeddableFactory?: EmbeddableFactory;
|
||||
|
||||
constructor(
|
||||
private type: string,
|
||||
private showSaveModal: (
|
||||
saveModal: React.ReactElement,
|
||||
I18nContext: I18nStart['Context']
|
||||
) => void,
|
||||
private savedObjectsClient: SavedObjectsClientContract,
|
||||
private overlays: OverlayStart,
|
||||
private i18nContext: I18nStart['Context'],
|
||||
private toasts: NotificationsStart['toasts'],
|
||||
getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']
|
||||
getEmbeddableFactory?: EmbeddableStart['getEmbeddableFactory'],
|
||||
private options?: AttributeServiceOptions<SavedObjectAttributes>
|
||||
) {
|
||||
const factory = getEmbeddableFactory(this.type);
|
||||
if (!factory) {
|
||||
throw new EmbeddableFactoryNotFoundError(this.type);
|
||||
if (getEmbeddableFactory) {
|
||||
const factory = getEmbeddableFactory(this.type);
|
||||
if (!factory) {
|
||||
throw new EmbeddableFactoryNotFoundError(this.type);
|
||||
}
|
||||
this.embeddableFactory = factory;
|
||||
}
|
||||
this.embeddableFactory = factory;
|
||||
}
|
||||
|
||||
public async unwrapAttributes(input: RefType | ValType): Promise<SavedObjectAttributes> {
|
||||
|
@ -79,43 +97,54 @@ export class AttributeService<
|
|||
const savedObject: SimpleSavedObject<SavedObjectAttributes> = await this.savedObjectsClient.get<
|
||||
SavedObjectAttributes
|
||||
>(this.type, input.savedObjectId);
|
||||
return savedObject.attributes;
|
||||
return this.options?.customUnwrapMethod
|
||||
? this.options?.customUnwrapMethod(savedObject)
|
||||
: { ...savedObject.attributes };
|
||||
}
|
||||
return input.attributes;
|
||||
return input[ATTRIBUTE_SERVICE_KEY];
|
||||
}
|
||||
|
||||
public async wrapAttributes(
|
||||
newAttributes: SavedObjectAttributes,
|
||||
useRefType: boolean,
|
||||
embeddable?: IEmbeddable
|
||||
input?: ValType | RefType
|
||||
): Promise<Omit<ValType | RefType, 'id'>> {
|
||||
const originalInput = input ? input : {};
|
||||
const savedObjectId =
|
||||
embeddable && isSavedObjectEmbeddableInput(embeddable.getInput())
|
||||
? (embeddable.getInput() as SavedObjectEmbeddableInput).savedObjectId
|
||||
input && this.inputIsRefType(input)
|
||||
? (input as SavedObjectEmbeddableInput).savedObjectId
|
||||
: undefined;
|
||||
if (!useRefType) {
|
||||
return { attributes: newAttributes } as ValType;
|
||||
} else {
|
||||
try {
|
||||
if (savedObjectId) {
|
||||
await this.savedObjectsClient.update(this.type, savedObjectId, newAttributes);
|
||||
return { savedObjectId } as RefType;
|
||||
} else {
|
||||
const savedItem = await this.savedObjectsClient.create(this.type, newAttributes);
|
||||
return { savedObjectId: savedItem.id } as RefType;
|
||||
}
|
||||
} catch (error) {
|
||||
this.toasts.addDanger({
|
||||
title: i18n.translate('dashboard.attributeService.saveToLibraryError', {
|
||||
defaultMessage: `Panel was not saved to the library. Error: {errorMessage}`,
|
||||
values: {
|
||||
errorMessage: error.message,
|
||||
},
|
||||
}),
|
||||
'data-test-subj': 'saveDashboardFailure',
|
||||
});
|
||||
return Promise.reject({ error });
|
||||
return { [ATTRIBUTE_SERVICE_KEY]: newAttributes } as ValType;
|
||||
}
|
||||
try {
|
||||
if (this.options?.customSaveMethod) {
|
||||
const savedItem = await this.options.customSaveMethod(
|
||||
this.type,
|
||||
newAttributes,
|
||||
savedObjectId
|
||||
);
|
||||
return { ...originalInput, savedObjectId: savedItem.id } as RefType;
|
||||
}
|
||||
|
||||
if (savedObjectId) {
|
||||
await this.savedObjectsClient.update(this.type, savedObjectId, newAttributes);
|
||||
return { ...originalInput, savedObjectId } as RefType;
|
||||
}
|
||||
|
||||
const savedItem = await this.savedObjectsClient.create(this.type, newAttributes);
|
||||
return { ...originalInput, savedObjectId: savedItem.id } as RefType;
|
||||
} catch (error) {
|
||||
this.toasts.addDanger({
|
||||
title: i18n.translate('dashboard.attributeService.saveToLibraryError', {
|
||||
defaultMessage: `Panel was not saved to the library. Error: {errorMessage}`,
|
||||
values: {
|
||||
errorMessage: error.message,
|
||||
},
|
||||
}),
|
||||
'data-test-subj': 'saveDashboardFailure',
|
||||
});
|
||||
return Promise.reject({ error });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +175,7 @@ export class AttributeService<
|
|||
|
||||
getInputAsRefType = async (
|
||||
input: ValType | RefType,
|
||||
saveOptions?: { showSaveModal: boolean } | { title: string }
|
||||
saveOptions?: { showSaveModal: boolean; saveModalTitle?: string } | { title: string }
|
||||
): Promise<RefType> => {
|
||||
if (this.inputIsRefType(input)) {
|
||||
return input;
|
||||
|
@ -159,7 +188,7 @@ export class AttributeService<
|
|||
copyOnSave: false,
|
||||
lastSavedTitle: '',
|
||||
getEsType: () => this.type,
|
||||
getDisplayName: this.embeddableFactory.getDisplayName,
|
||||
getDisplayName: this.embeddableFactory?.getDisplayName || (() => this.type),
|
||||
},
|
||||
props.isTitleDuplicateConfirmed,
|
||||
props.onTitleDuplicate,
|
||||
|
@ -169,7 +198,7 @@ export class AttributeService<
|
|||
}
|
||||
);
|
||||
try {
|
||||
const newAttributes = { ...input.attributes };
|
||||
const newAttributes = { ...input[ATTRIBUTE_SERVICE_KEY] };
|
||||
newAttributes.title = props.newTitle;
|
||||
const wrappedInput = (await this.wrapAttributes(newAttributes, true)) as RefType;
|
||||
resolve(wrappedInput);
|
||||
|
@ -181,11 +210,11 @@ export class AttributeService<
|
|||
};
|
||||
|
||||
if (saveOptions && (saveOptions as { showSaveModal: boolean }).showSaveModal) {
|
||||
showSaveModal(
|
||||
this.showSaveModal(
|
||||
<SavedObjectSaveModal
|
||||
onSave={onSave}
|
||||
onClose={() => reject()}
|
||||
title={input.attributes.title}
|
||||
title={get(saveOptions, 'saveModalTitle', input[ATTRIBUTE_SERVICE_KEY].title)}
|
||||
showCopyOnSave={false}
|
||||
objectType={this.type}
|
||||
showDescription={false}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 { EmbeddableInput, SavedObjectEmbeddableInput } from '../embeddable_plugin';
|
||||
import { coreMock } from '../../../../core/public/mocks';
|
||||
import { AttributeServiceOptions } from './attribute_service';
|
||||
import { CoreStart } from '../../../../core/public';
|
||||
import { AttributeService, ATTRIBUTE_SERVICE_KEY } from '..';
|
||||
|
||||
export const mockAttributeService = <
|
||||
A extends { title: string },
|
||||
V extends EmbeddableInput & { [ATTRIBUTE_SERVICE_KEY]: A } = EmbeddableInput & {
|
||||
[ATTRIBUTE_SERVICE_KEY]: A;
|
||||
},
|
||||
R extends SavedObjectEmbeddableInput = SavedObjectEmbeddableInput
|
||||
>(
|
||||
type: string,
|
||||
options?: AttributeServiceOptions<A>,
|
||||
customCore?: jest.Mocked<CoreStart>
|
||||
): AttributeService<A, V, R> => {
|
||||
const core = customCore ? customCore : coreMock.createStart();
|
||||
const service = new AttributeService<A, V, R>(
|
||||
type,
|
||||
jest.fn(),
|
||||
core.savedObjects.client,
|
||||
core.overlays,
|
||||
core.i18n.Context,
|
||||
core.notifications.toasts,
|
||||
jest.fn().mockReturnValue(() => ({ getDisplayName: () => type })),
|
||||
options
|
||||
);
|
||||
return service;
|
||||
};
|
|
@ -40,7 +40,7 @@ export {
|
|||
export { addEmbeddableToDashboardUrl } from './url_utils/url_helper';
|
||||
export { SavedObjectDashboard } from './saved_dashboards';
|
||||
export { SavedDashboardPanel } from './types';
|
||||
export { AttributeService } from './attribute_service/attribute_service';
|
||||
export { AttributeService, ATTRIBUTE_SERVICE_KEY } from './attribute_service/attribute_service';
|
||||
|
||||
export function plugin(initializerContext: PluginInitializerContext) {
|
||||
return new DashboardPlugin(initializerContext);
|
||||
|
|
|
@ -52,6 +52,7 @@ import {
|
|||
getSavedObjectFinder,
|
||||
SavedObjectLoader,
|
||||
SavedObjectsStart,
|
||||
showSaveModal,
|
||||
} from '../../saved_objects/public';
|
||||
import {
|
||||
ExitFullScreenButton as ExitFullScreenButtonUi,
|
||||
|
@ -102,6 +103,10 @@ import { addEmbeddableToDashboardUrl } from './url_utils/url_helper';
|
|||
import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder';
|
||||
import { UrlGeneratorState } from '../../share/public';
|
||||
import { AttributeService } from '.';
|
||||
import {
|
||||
AttributeServiceOptions,
|
||||
ATTRIBUTE_SERVICE_KEY,
|
||||
} from './attribute_service/attribute_service';
|
||||
|
||||
declare module '../../share/public' {
|
||||
export interface UrlGeneratorStateMapping {
|
||||
|
@ -150,10 +155,13 @@ export interface DashboardStart {
|
|||
DashboardContainerByValueRenderer: ReturnType<typeof createDashboardContainerByValueRenderer>;
|
||||
getAttributeService: <
|
||||
A extends { title: string },
|
||||
V extends EmbeddableInput & { attributes: A },
|
||||
R extends SavedObjectEmbeddableInput
|
||||
V extends EmbeddableInput & { [ATTRIBUTE_SERVICE_KEY]: A } = EmbeddableInput & {
|
||||
[ATTRIBUTE_SERVICE_KEY]: A;
|
||||
},
|
||||
R extends SavedObjectEmbeddableInput = SavedObjectEmbeddableInput
|
||||
>(
|
||||
type: string
|
||||
type: string,
|
||||
options?: AttributeServiceOptions<A>
|
||||
) => AttributeService<A, V, R>;
|
||||
}
|
||||
|
||||
|
@ -465,14 +473,16 @@ export class DashboardPlugin
|
|||
DashboardContainerByValueRenderer: createDashboardContainerByValueRenderer({
|
||||
factory: dashboardContainerFactory,
|
||||
}),
|
||||
getAttributeService: (type: string) =>
|
||||
getAttributeService: (type: string, options) =>
|
||||
new AttributeService(
|
||||
type,
|
||||
showSaveModal,
|
||||
core.savedObjects.client,
|
||||
core.overlays,
|
||||
core.i18n.Context,
|
||||
core.notifications.toasts,
|
||||
embeddable.getEmbeddableFactory
|
||||
embeddable.getEmbeddableFactory,
|
||||
options
|
||||
),
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue