diff --git a/src/plugins/so_management/public/management/components/edition/field.tsx b/src/plugins/so_management/public/management/components/edition/field.tsx new file mode 100644 index 0000000000000..8d0bc2e9fdaf6 --- /dev/null +++ b/src/plugins/so_management/public/management/components/edition/field.tsx @@ -0,0 +1,168 @@ +/* + * 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 React, { PureComponent } from 'react'; +import { + EuiFieldNumber, + EuiFieldText, + EuiFormLabel, + EuiSwitch, + // @ts-ignore + EuiCodeEditor, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { FieldState, FieldType } from '../../types'; + +interface FieldProps { + type: FieldType; + name: string; + value: any; + disabled: boolean; + state?: FieldState; + onChange: (name: string, state: FieldState) => void; +} + +export class Field extends PureComponent { + render() { + const { name } = this.props; + + return ( +
+ + {name} + + {this.renderField()} +
+ ); + } + + onCodeEditorChange(targetValue: any) { + const { name, onChange } = this.props; + let invalid = false; + try { + JSON.parse(targetValue); + } catch (e) { + invalid = true; + } + onChange(name, { + value: targetValue, + invalid, + }); + } + + onFieldChange(targetValue: any) { + const { name, type, onChange } = this.props; + + let newParsedValue = targetValue; + let invalid = false; + if (type === 'number') { + try { + newParsedValue = Number(newParsedValue); + } catch (e) { + invalid = true; + } + } + onChange(name, { + value: newParsedValue, + invalid, + }); + } + + renderField() { + const { type, name, state, disabled } = this.props; + const currentValue = state?.value ?? this.props.value; + + switch (type) { + case 'number': + return ( + this.onFieldChange(e.target.value)} + disabled={disabled} + data-test-subj={`savedObjects-editField-${name}`} + /> + ); + case 'boolean': + return ( + + ) : ( + + ) + } + checked={!!currentValue} + onChange={e => this.onFieldChange(e.target.checked)} + disabled={disabled} + data-test-subj={`savedObjects-editField-${name}`} + /> + ); + case 'json': + case 'array': + return ( +
+ this.onCodeEditorChange(value)} + width="100%" + height="auto" + minLines={6} + maxLines={30} + isReadOnly={disabled} + setOptions={{ + showLineNumbers: true, + tabSize: 2, + softTabs: true, + }} + editorProps={{ + $blockScrolling: Infinity, + }} + showGutter={true} + fullWidth + /> +
+ ); + default: + return ( + this.onFieldChange(e.target.value)} + disabled={disabled} + data-test-subj={`savedObjects-editField-${name}`} + /> + ); + } + } + + private get fieldId() { + const { name } = this.props; + return `savedObjects-editField-${name}`; + } +} diff --git a/src/plugins/so_management/public/management/components/edition/form.tsx b/src/plugins/so_management/public/management/components/edition/form.tsx new file mode 100644 index 0000000000000..c7914254eb6d6 --- /dev/null +++ b/src/plugins/so_management/public/management/components/edition/form.tsx @@ -0,0 +1,267 @@ +/* + * 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 React, { Component } from 'react'; +import { + forOwn, + indexBy, + cloneDeep, + isNumber, + isBoolean, + isPlainObject, + isString, + set, +} from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { SimpleSavedObject, SavedObjectsClientContract } from '../../../../../../core/public'; +import { castEsToKbnFieldTypeName } from '../../../../../data/public'; +import { SavedObjectLoader } from '../../../../../saved_objects/public'; +import { Field } from './field'; +import { ObjectField, FieldState, SubmittedFormData } from '../../types'; + +interface FormProps { + object: SimpleSavedObject; + service: SavedObjectLoader; + savedObjectsClient: SavedObjectsClientContract; + editionEnabled: boolean; + onSave: (form: SubmittedFormData) => void; +} + +interface FormState { + fields: ObjectField[]; + fieldStates: Record; +} + +export class Form extends Component { + constructor(props: FormProps) { + super(props); + this.state = { + fields: [], + fieldStates: {}, + }; + } + + componentDidMount() { + const { object, service } = this.props; + + const fields = Object.entries(object.attributes as Record).reduce( + (objFields, [key, value]) => { + return [...objFields, ...recursiveCreateFields(key, value)]; + }, + [] as ObjectField[] + ); + if ((service as any).Class) { + addFieldsFromClass((service as any).Class, fields); + } + + this.setState({ + fields, + }); + } + + render() { + const { editionEnabled, service } = this.props; + const { fields, fieldStates } = this.state; + const isValid = this.isFormValid(); + return ( + <> +
+ {fields.map(field => ( + + ))} +
+
+ {editionEnabled && ( + + )} + + +
+ + ); + } + + handleFieldChange = (name: string, newState: FieldState) => { + this.setState({ + fieldStates: { + ...this.state.fieldStates, + [name]: newState, + }, + }); + }; + + isFormValid() { + const { fieldStates } = this.state; + return !Object.values(fieldStates).some(state => state.invalid === true); + } + + onCancel = () => { + window.history.back(); + }; + + onSubmit = async () => { + const { object, onSave } = this.props; + const { fields, fieldStates } = this.state; + + if (!this.isFormValid()) { + return; + } + + const source = cloneDeep(object.attributes as any); + fields.forEach(field => { + let value = fieldStates[field.name]?.value ?? field.value; + + if (field.type === 'array' && typeof value === 'string') { + value = JSON.parse(value); + } + + set(source, field.name, value); + }); + + const { references, ...attributes } = source; + + onSave({ attributes, references }); + }; +} + +/** + * Creates a field definition and pushes it to the memo stack. This function + * is designed to be used in conjunction with _.reduce(). If the + * values is plain object it will recurse through all the keys till it hits + * a string, number or an array. + * + * @param {string} key The key of the field + * @param {mixed} value The value of the field + * @param {array} parents The parent keys to the field + * @returns {array} + */ +const recursiveCreateFields = (key: string, value: any, parents: string[] = []): ObjectField[] => { + const path = [...parents, key]; + + const field: ObjectField = { type: 'text', name: path.join('.'), value }; + let fields: ObjectField[] = [field]; + + if (isString(field.value)) { + try { + field.value = JSON.stringify(JSON.parse(field.value), undefined, 2); + field.type = 'json'; + } catch (err) { + field.type = 'text'; + } + } else if (isNumber(field.value)) { + field.type = 'number'; + } else if (Array.isArray(field.value)) { + field.type = 'array'; + field.value = JSON.stringify(field.value, undefined, 2); + } else if (isBoolean(field.value)) { + field.type = 'boolean'; + } else if (isPlainObject(field.value)) { + forOwn(field.value, (childValue, childKey) => { + fields = [...recursiveCreateFields(childKey as string, childValue, path)]; + }); + } + + return fields; +}; + +const addFieldsFromClass = function( + Class: { mapping: Record; searchSource: any }, + fields: ObjectField[] +) { + const fieldMap = indexBy(fields, 'name'); + + _.forOwn(Class.mapping, (esType, name) => { + if (!name || fieldMap[name]) { + return; + } + + const getFieldTypeFromEsType = () => { + switch (castEsToKbnFieldTypeName(esType)) { + case 'string': + return 'text'; + case 'number': + return 'number'; + case 'boolean': + return 'boolean'; + default: + return 'json'; + } + }; + + fields.push({ + name, + type: getFieldTypeFromEsType(), + value: undefined, + }); + }); + + if (Class.searchSource && !fieldMap['kibanaSavedObjectMeta.searchSourceJSON']) { + fields.push({ + name: 'kibanaSavedObjectMeta.searchSourceJSON', + type: 'json', + value: '{}', + }); + } + + if (!fieldMap.references) { + fields.push({ + name: 'references', + type: 'array', + value: '[]', + }); + } +}; diff --git a/src/plugins/so_management/public/management/components/edition/header.tsx b/src/plugins/so_management/public/management/components/edition/header.tsx new file mode 100644 index 0000000000000..77fe04381c1eb --- /dev/null +++ b/src/plugins/so_management/public/management/components/edition/header.tsx @@ -0,0 +1,104 @@ +/* + * 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 React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +interface HeaderProps { + canEdit: boolean; + canDelete: boolean; + canViewInApp: boolean; + type: string; + viewUrl: string; + onDeleteClick: () => void; +} + +export const Header = ({ + canEdit, + canDelete, + canViewInApp, + type, + viewUrl, + onDeleteClick, +}: HeaderProps) => { + return ( +
+
+

+ {canEdit ? ( + + ) : ( + + )} +

+
+ +
+ {canViewInApp && ( + + + + + + + + + )} + + {canDelete && ( + + )} +
+
+ ); +}; diff --git a/src/plugins/so_management/public/management/components/edition/index.ts b/src/plugins/so_management/public/management/components/edition/index.ts new file mode 100644 index 0000000000000..a3a05c05cb4a9 --- /dev/null +++ b/src/plugins/so_management/public/management/components/edition/index.ts @@ -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. + */ + +export { Header } from './header'; +export { NotFoundErrors } from './not_found_errors'; +export { Intro } from './intro'; +export { Form } from './form'; diff --git a/src/plugins/so_management/public/management/components/edition/intro.tsx b/src/plugins/so_management/public/management/components/edition/intro.tsx new file mode 100644 index 0000000000000..f481d9a678333 --- /dev/null +++ b/src/plugins/so_management/public/management/components/edition/intro.tsx @@ -0,0 +1,48 @@ +/* + * 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 React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const Intro = () => { + return ( +
+
+
+ + + + +
+ +
+
+ +
+
+
+
+ ); +}; diff --git a/src/plugins/so_management/public/management/components/edition/not_found_errors.tsx b/src/plugins/so_management/public/management/components/edition/not_found_errors.tsx new file mode 100644 index 0000000000000..f79e5f5afd71f --- /dev/null +++ b/src/plugins/so_management/public/management/components/edition/not_found_errors.tsx @@ -0,0 +1,79 @@ +/* + * 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 React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +interface NotFoundErrors { + type: string; +} + +export const NotFoundErrors = ({ type }: NotFoundErrors) => { + return ( +
+
+
+ + + + +
+ +
+ {type === 'search' && ( +
+ +
+ )} + + {type === 'index-pattern' && ( +
+ +
+ )} + + {type === 'index-pattern-field' && ( +
+ +
+ )} + +
+ +
+
+
+
+ ); +}; diff --git a/src/plugins/so_management/public/management/index.tsx b/src/plugins/so_management/public/management/index.tsx index 4e6b585d74382..d44e5b20e7b63 100644 --- a/src/plugins/so_management/public/management/index.tsx +++ b/src/plugins/so_management/public/management/index.tsx @@ -17,18 +17,20 @@ * under the License. */ -import React from 'react'; +import React, { useEffect } from 'react'; import ReactDOM from 'react-dom'; -import { HashRouter, Switch, Route } from 'react-router-dom'; +import { HashRouter, Switch, Route, useParams, useLocation } from 'react-router-dom'; +import { parse } from 'query-string'; import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; -import { CoreSetup, CoreStart } from 'src/core/public'; +import { CoreSetup, CoreStart, ChromeBreadcrumb } from 'src/core/public'; import { ManagementSetup } from '../../../management/public'; import { DataPublicPluginStart } from '../../../data/public'; import { StartDependencies } from '../plugin'; import { ISavedObjectsManagementRegistry } from '../management_registry'; import { SavedObjectsTable } from './saved_objects_table'; +import { SavedObjectEdition } from './saved_objects_view'; import { getAllowedTypes } from './lib'; interface RegisterOptions { @@ -58,12 +60,20 @@ export const registerManagementSection = ({ core, sections, serviceRegistry }: R - + + + + @@ -79,19 +89,66 @@ export const registerManagementSection = ({ core, sections, serviceRegistry }: R }); }; +const SavedObjectsEditionPage = ({ + coreStart, + serviceRegistry, + setBreadcrumbs, +}: { + coreStart: CoreStart; + serviceRegistry: ISavedObjectsManagementRegistry; + setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; +}) => { + const { service: serviceName, id } = useParams<{ service: string; id: string }>(); + const capabilities = coreStart.application.capabilities; + + const { search } = useLocation(); + const query = parse(search); + + useEffect(() => { + setBreadcrumbs([]); // TODO: proper breadcrumb + }, [setBreadcrumbs]); + + return ( + + ); +}; + const SavedObjectsTablePage = ({ coreStart, dataStart, allowedTypes, serviceRegistry, + setBreadcrumbs, }: { coreStart: CoreStart; dataStart: DataPublicPluginStart; allowedTypes: string[]; serviceRegistry: ISavedObjectsManagementRegistry; + setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; }) => { const capabilities = coreStart.application.capabilities; const itemsPerPage = coreStart.uiSettings.get('savedObjects:perPage', 50); + + useEffect(() => { + setBreadcrumbs([ + { + text: i18n.translate('kbn.management.savedObjects.indexBreadcrumb', { + defaultMessage: 'Saved objects', + }), + href: '#/management/kibana/objects', + }, + ]); + }, [setBreadcrumbs]); + return ( { const { editUrl } = savedObject.meta; if (editUrl) { - // TODO: fix, this doesnt work. find solution to change hashbang - // kbnUrl.change(object.meta.editUrl); - window.location.href = editUrl; + // TODO: it seems only editable objects are done from without the management page. + // previously, kbnUrl.change(object.meta.editUrl); was used. + // using direct access to location.hash seems the only option for now. + + // TODO: remove redirect hack + window.location.hash = editUrl.replace('/objects', '/toto'); } }} canGoInApp={savedObject => { diff --git a/src/plugins/so_management/public/management/lib/in_app_url.ts b/src/plugins/so_management/public/management/lib/in_app_url.ts new file mode 100644 index 0000000000000..33c945c2dbf4c --- /dev/null +++ b/src/plugins/so_management/public/management/lib/in_app_url.ts @@ -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 { Capabilities } from 'src/core/public'; + +export function canViewInApp(uiCapabilities: Capabilities, type: string) { + switch (type) { + case 'search': + case 'searches': + return uiCapabilities.discover.show as boolean; + case 'visualization': + case 'visualizations': + return uiCapabilities.visualize.show as boolean; + case 'index-pattern': + case 'index-patterns': + case 'indexPatterns': + return uiCapabilities.management.kibana.index_patterns as boolean; + case 'dashboard': + case 'dashboards': + return uiCapabilities.dashboard.show as boolean; + default: + return uiCapabilities[type].show as boolean; + } +} diff --git a/src/plugins/so_management/public/management/lib/index.ts b/src/plugins/so_management/public/management/lib/index.ts index a0c44313a1704..809af13d931f0 100644 --- a/src/plugins/so_management/public/management/lib/index.ts +++ b/src/plugins/so_management/public/management/lib/index.ts @@ -32,3 +32,4 @@ export { importLegacyFile } from './import_legacy_file'; export { logLegacyImport } from './log_legacy_import'; export { processImportResponse, ProcessedImportResponse } from './process_import_response'; export { resolveImportErrors } from './resolve_import_errors'; +export { canViewInApp } from './in_app_url'; diff --git a/src/plugins/so_management/public/management/saved_objects_view.tsx b/src/plugins/so_management/public/management/saved_objects_view.tsx new file mode 100644 index 0000000000000..ecfd984980884 --- /dev/null +++ b/src/plugins/so_management/public/management/saved_objects_view.tsx @@ -0,0 +1,157 @@ +/* + * 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 React, { Component } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + Capabilities, + SavedObjectsClientContract, + OverlayStart, + NotificationsStart, + SimpleSavedObject, +} from '../../../../core/public'; +import { ISavedObjectsManagementRegistry } from '../management_registry'; +import { Header, NotFoundErrors, Intro, Form } from './components/edition'; +import { canViewInApp } from './lib'; +import { SubmittedFormData } from './types'; + +interface SavedObjectEditionProps { + id: string; + serviceName: string; + serviceRegistry: ISavedObjectsManagementRegistry; + capabilities: Capabilities; + overlays: OverlayStart; + notifications: NotificationsStart; + notFoundType?: string; + savedObjectsClient: SavedObjectsClientContract; +} + +interface SavedObjectEditionState { + type: string; + object?: SimpleSavedObject; +} + +export class SavedObjectEdition extends Component< + SavedObjectEditionProps, + SavedObjectEditionState +> { + constructor(props: SavedObjectEditionProps) { + super(props); + + const { serviceRegistry, serviceName } = props; + const type = serviceRegistry.get(serviceName)!.service.type; + + this.state = { + object: undefined, + type, + }; + } + + componentDidMount() { + const { id, savedObjectsClient } = this.props; + const { type } = this.state; + savedObjectsClient.get(type, id).then(object => { + this.setState({ + object, + }); + }); + } + + render() { + const { + capabilities, + notFoundType, + serviceRegistry, + id, + serviceName, + savedObjectsClient, + } = this.props; + const { type } = this.state; + const { object } = this.state; + const { edit: canEdit, delete: canDelete } = capabilities.savedObjectsManagement as Record< + string, + boolean + >; + const canView = canViewInApp(capabilities, type); + const service = serviceRegistry.get(serviceName)!.service; + + return ( +
+
this.delete()} + viewUrl={service.urlFor(id)} + /> + {notFoundType && } + {canEdit && } + {object && ( +
+ )} +
+ ); + } + + async delete() { + const { id, savedObjectsClient, overlays, notifications } = this.props; + const { type, object } = this.state; + + const confirmed = await overlays.openConfirm( + i18n.translate('kbn.management.objects.confirmModalOptions.modalDescription', { + defaultMessage: "You can't recover deleted objects", + }), + { + confirmButtonText: i18n.translate( + 'kbn.management.objects.confirmModalOptions.deleteButtonLabel', + { + defaultMessage: 'Delete', + } + ), + title: i18n.translate('kbn.management.objects.confirmModalOptions.modalTitle', { + defaultMessage: 'Delete saved Kibana object?', + }), + } + ); + if (confirmed) { + await savedObjectsClient.delete(type, id); + notifications.toasts.addSuccess(`Deleted '${object!.attributes.title}' ${type} object`); + window.location.hash = '/management/kibana/objects'; + } + } + + saveChanges = async ({ attributes, references }: SubmittedFormData) => { + const { savedObjectsClient, notifications } = this.props; + const { object, type } = this.state; + + await savedObjectsClient.update(object!.type, object!.id, attributes, { references }); + notifications.toasts.addSuccess(`Updated '${attributes.title}' ${type} object`); + window.location.hash = '/management/kibana/objects'; + }; +} diff --git a/src/plugins/so_management/public/management/types.ts b/src/plugins/so_management/public/management/types.ts index 3893be8a4e068..b913e600572c5 100644 --- a/src/plugins/so_management/public/management/types.ts +++ b/src/plugins/so_management/public/management/types.ts @@ -17,6 +17,7 @@ * under the License. */ +import { SavedObjectReference } from 'src/core/public'; import { SavedObjectWithMetadata } from '../types'; // TODO: same as in `src/plugins/so_management/server/lib/find_relationships.ts`, create common folder @@ -26,3 +27,21 @@ export interface SavedObjectRelation { relationship: 'child' | 'parent'; meta: SavedObjectWithMetadata['meta']; } + +export interface ObjectField { + type: FieldType; + name: string; + value: any; +} + +export type FieldType = 'text' | 'number' | 'boolean' | 'array' | 'json'; + +export interface FieldState { + value?: any; + invalid?: boolean; +} + +export interface SubmittedFormData { + attributes: any; + references: SavedObjectReference[]; +} diff --git a/src/plugins/so_management/public/management_registry.ts b/src/plugins/so_management/public/management_registry.ts index ddb7b9a5ce6dc..be511f1a69807 100644 --- a/src/plugins/so_management/public/management_registry.ts +++ b/src/plugins/so_management/public/management_registry.ts @@ -38,7 +38,7 @@ export class SavedObjectsManagementRegistry { } public all(): SavedObjectsManagementRegistryEntry[] { - return Object.values(this.registry); + return [...this.registry.values()]; } public get(id: string): SavedObjectsManagementRegistryEntry | undefined {