Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
e0aca37
sample UI for copying a single object to multiple spaces
legrego Jun 14, 2019
933817e
Simplify action
legrego Jun 25, 2019
8401fee
Merge branch 'master' of github.com:elastic/kibana into copy-to-space-ui
legrego Jun 25, 2019
46a68df
fix merge from master
legrego Jun 25, 2019
d33e73d
WIP status indicator
legrego Jun 27, 2019
c07f905
POC conflict resolution UI
legrego Jul 3, 2019
e27e111
prefix exported types with SavedObjects
legrego Jul 3, 2019
29f2bdc
Merge branch 'master' of github.com:elastic/kibana into copy-to-space-ui
legrego Jul 3, 2019
4b18c51
show finish button
legrego Jul 8, 2019
61491ff
adopting design's mockup
legrego Jul 30, 2019
a1b662e
Merge branch 'master' of github.com:elastic/kibana into copy-to-space-ui
legrego Jul 31, 2019
8e00c79
enabling conflict resolution, and other improvements
legrego Jul 31, 2019
8c4425b
move inline styles to sass
legrego Aug 1, 2019
bea29e3
ux adjustments
legrego Aug 1, 2019
92edadc
add loading indicator
legrego Aug 1, 2019
6fb21b3
copy to spaces design tweaks (#22)
Aug 2, 2019
c673e2f
Merge branch 'master' of github.com:elastic/kibana into copy-to-space-ui
legrego Aug 13, 2019
4996c5d
reverting temporary core changes
legrego Aug 13, 2019
4a7569a
minor refactoring
legrego Aug 14, 2019
e233d71
unit testing
legrego Aug 15, 2019
e40d2c7
functional UI tests
legrego Aug 15, 2019
31cc78d
fix typo in tooltip
legrego Aug 15, 2019
91b8b02
type fixes and misc cleanup
legrego Aug 15, 2019
486dafd
i18n improvements
legrego Aug 16, 2019
e3eff07
a11y improvements
legrego Aug 16, 2019
aa39d43
additonal cleanup
legrego Aug 16, 2019
efb6f7f
renaming folders
legrego Aug 20, 2019
75d5944
preparing for real API calls
legrego Aug 20, 2019
c942b87
testing error scenarios
legrego Aug 20, 2019
34cf67b
testing conflict resolution
legrego Aug 20, 2019
472a3f6
testing the action registry
legrego Aug 21, 2019
5dc168f
making typescript happy
legrego Aug 21, 2019
d4183ac
Merge branch 'master' of github.com:elastic/kibana into copy-to-space-ui
legrego Aug 21, 2019
7845745
moving GetSpacePurpose to common location
legrego Aug 22, 2019
b94a02a
adjustments following API integration
legrego Aug 22, 2019
1a4a1c4
re-enabling tests
legrego Aug 22, 2019
ec953af
fix duplicate message id
legrego Aug 22, 2019
139d606
Merge branch 'master' of github.com:elastic/kibana into copy-to-space-ui
legrego Aug 22, 2019
aa14457
renaming interfaces
legrego Aug 23, 2019
0d62721
reorganize types; type SpaecsManager CTS functions
legrego Aug 23, 2019
9beea0c
transient => transitive
legrego Aug 23, 2019
56f949d
removing incorrect union type (null)
legrego Aug 23, 2019
fcf1870
using spaces manager to retrieve the list of available spaces
legrego Aug 23, 2019
189f872
removing unnecessary useEffect
legrego Aug 23, 2019
e615838
removing cts_ prefix
legrego Aug 23, 2019
917fc63
improved button text when resolving conflicts
legrego Aug 23, 2019
2fe0e21
test updates
legrego Aug 23, 2019
09ba89e
Merge branch 'master' of github.com:elastic/kibana into copy-to-space-ui
legrego Aug 23, 2019
5f1cde4
remove 'missing references' functionality
legrego Aug 23, 2019
65e3594
require related objects to be included
legrego Aug 23, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ import {
importLegacyFile,
resolveImportErrors,
logLegacyImport,
processImportResponse,
getDefaultTitle,
} from '../../../../lib';
import { processImportResponse } from '../../../../lib/process_import_response';
import {
resolveSavedObjects,
resolveSavedSearches,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

import chrome from 'ui/chrome';
import { SavedObjectsManagementActionRegistry } from 'ui/management/saved_objects_management';
import React, { PureComponent, Fragment } from 'react';
import PropTypes from 'prop-types';

Expand Down Expand Up @@ -73,6 +74,12 @@ class TableUI extends PureComponent {
parseErrorMessage: null,
isExportPopoverOpen: false,
isIncludeReferencesDeepChecked: true,
activeAction: null,
}

constructor(props) {
super(props);
this.extraActions = SavedObjectsManagementActionRegistry.get();
}

onChange = ({ query, error }) => {
Expand Down Expand Up @@ -238,6 +245,24 @@ class TableUI extends PureComponent {
icon: 'kqlSelector',
onClick: object => onShowRelationships(object),
},
...this.extraActions.map(action => {
return {
...action.euiAction,
onClick: (object) => {
this.setState({
activeAction: action
});

action.registerOnFinishCallback(() => {
this.setState({
activeAction: null,
});
});

action.euiAction.onClick(object);
}
};
})
],
},
];
Expand Down Expand Up @@ -269,8 +294,11 @@ class TableUI extends PureComponent {
</EuiButton>
);

const activeActionContents = this.state.activeAction ? this.state.activeAction.render() : null;

return (
<Fragment>
{activeActionContents}
<EuiSearchBar
box={{ 'data-test-subj': 'savedObjectSearchBar' }}
filters={filters}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,38 @@
* under the License.
*/

export function processImportResponse(response) {
import {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No functional changes in this file -- simply a conversion to TypeScript

SavedObjectsImportResponse,
SavedObjectsImportConflictError,
SavedObjectsImportUnsupportedTypeError,
SavedObjectsImportMissingReferencesError,
SavedObjectsImportUnknownError,
SavedObjectsImportError,
} from 'src/core/server';

export interface ProcessedImportResponse {
failedImports: Array<{
obj: Pick<SavedObjectsImportError, 'id' | 'type' | 'title'>;
error:
| SavedObjectsImportConflictError
| SavedObjectsImportUnsupportedTypeError
| SavedObjectsImportMissingReferencesError
| SavedObjectsImportUnknownError;
}>;
unmatchedReferences: Array<{
existingIndexPatternId: string;
list: Array<Record<string, any>>;
newIndexPatternId: string | undefined;
}>;
status: 'success' | 'idle';
importCount: number;
conflictedSavedObjectsLinkedToSavedSearches: undefined;
conflictedSearchDocs: undefined;
}

export function processImportResponse(
response: SavedObjectsImportResponse
): ProcessedImportResponse {
// Go through the failures and split between unmatchedReferences and failedImports
const failedImports = [];
const unmatchedReferences = new Map();
Expand All @@ -29,7 +60,9 @@ export function processImportResponse(response) {
// Currently only supports resolving references on index patterns
const indexPatternRefs = error.references.filter(ref => ref.type === 'index-pattern');
for (const missingReference of indexPatternRefs) {
const conflict = unmatchedReferences.get(`${missingReference.type}:${missingReference.id}`) || {
const conflict = unmatchedReferences.get(
`${missingReference.type}:${missingReference.id}`
) || {
existingIndexPatternId: missingReference.id,
list: [],
newIndexPatternId: undefined,
Expand All @@ -44,9 +77,11 @@ export function processImportResponse(response) {
unmatchedReferences: Array.from(unmatchedReferences.values()),
// Import won't be successful in the scenario unmatched references exist, import API returned errors of type unknown or import API
// returned errors of type missing_references.
status: unmatchedReferences.size === 0 && !failedImports.some(issue => issue.error.type === 'conflict')
? 'success'
: 'idle',
status:
unmatchedReferences.size === 0 &&
!failedImports.some(issue => issue.error.type === 'conflict')
? 'success'
: 'idle',
importCount: response.successCount,
conflictedSavedObjectsLinkedToSavedSearches: undefined,
conflictedSearchDocs: undefined,
Expand Down
29 changes: 29 additions & 0 deletions src/legacy/ui/public/management/saved_objects_management/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 { SavedObjectsManagementActionRegistry } from './saved_objects_management_action_registry';
export {
SavedObjectsManagementAction,
SavedObjectsManagementRecord,
SavedObjectsManagementRecordReference,
} from './saved_objects_management_action';
export {
processImportResponse,
ProcessedImportResponse,
} from '../../../../core_plugins/kibana/public/management/sections/objects/lib/process_import_response';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't be reaching into the kibana plugin from here, should we move process_import_response to src/legacy/ui/public/management/saved_objects_management/?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I thought this was acceptable because we do the same thing from src/legacy/ui/public/management/index.js today to share functionality between OSS and X-Pack

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting... well, let the "bad practices" roll :) Feel free to ignore this.

Original file line number Diff line number Diff line change
@@ -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 { ReactNode } from '@elastic/eui/node_modules/@types/react';

export interface SavedObjectsManagementRecordReference {
type: string;
id: string;
name: string;
}
export interface SavedObjectsManagementRecord {
type: string;
id: string;
meta: {
icon: string;
title: string;
};
references: SavedObjectsManagementRecordReference[];
}

export abstract class SavedObjectsManagementAction {
public abstract render: () => ReactNode;
public abstract id: string;
public abstract euiAction: {
name: string;
description: string;
icon: string;
type: string;
available?: (item: SavedObjectsManagementRecord) => boolean;
enabled?: (item: SavedObjectsManagementRecord) => boolean;
onClick?: (item: SavedObjectsManagementRecord) => void;
render?: (item: SavedObjectsManagementRecord) => any;
};

private callbacks: Function[] = [];

protected record: SavedObjectsManagementRecord | null = null;

public registerOnFinishCallback(callback: Function) {
this.callbacks.push(callback);
}

protected start(record: SavedObjectsManagementRecord) {
this.record = record;
}

protected finish() {
this.record = null;
this.callbacks.forEach(callback => callback());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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 { SavedObjectsManagementActionRegistry } from './saved_objects_management_action_registry';
import { SavedObjectsManagementAction } from './saved_objects_management_action';

describe('SavedObjectsManagementActionRegistry', () => {
it('allows actions to be registered and retrieved', () => {
const action = { id: 'foo' } as SavedObjectsManagementAction;
SavedObjectsManagementActionRegistry.register(action);
expect(SavedObjectsManagementActionRegistry.get()).toContain(action);
});

it('requires an "id" property', () => {
expect(() =>
SavedObjectsManagementActionRegistry.register({} as SavedObjectsManagementAction)
).toThrowErrorMatchingInlineSnapshot(`"Saved Objects Management Actions must have an id"`);
});

it('does not allow actions with duplicate ids to be registered', () => {
const action = { id: 'my-action' } as SavedObjectsManagementAction;
SavedObjectsManagementActionRegistry.register(action);
expect(() =>
SavedObjectsManagementActionRegistry.register(action)
).toThrowErrorMatchingInlineSnapshot(
`"Saved Objects Management Action with id 'my-action' already exists"`
);
});

it('#has returns true when an action with a matching ID exists', () => {
const action = { id: 'existing-action' } as SavedObjectsManagementAction;
SavedObjectsManagementActionRegistry.register(action);
expect(SavedObjectsManagementActionRegistry.has('existing-action')).toEqual(true);
});

it(`#has returns false when an action with doesn't exist`, () => {
expect(SavedObjectsManagementActionRegistry.has('missing-action')).toEqual(false);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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 { SavedObjectsManagementAction } from './saved_objects_management_action';

const actions: Map<string, SavedObjectsManagementAction> = new Map();

export const SavedObjectsManagementActionRegistry = {
register: (action: SavedObjectsManagementAction) => {
if (!action.id) {
throw new TypeError('Saved Objects Management Actions must have an id');
}
if (actions.has(action.id)) {
throw new Error(`Saved Objects Management Action with id '${action.id}' already exists`);
}
actions.set(action.id, action);
},

has: (actionId: string) => actions.has(actionId),

get: () => Array.from(actions.values()),
};
1 change: 1 addition & 0 deletions x-pack/dev-tools/jest/create_jest_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export function createJestConfig({ kibanaDirectory, xPackKibanaDirectory }) {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': fileMockPath,
'\\.(css|less|scss)$': `${kibanaDirectory}/src/dev/jest/mocks/style_mock.js`,
'^test_utils/enzyme_helpers': `${xPackKibanaDirectory}/test_utils/enzyme_helpers.tsx`,
'^test_utils/find_test_subject': `${xPackKibanaDirectory}/test_utils/find_test_subject.ts`,
},
setupFiles: [
`${kibanaDirectory}/src/dev/jest/setup/babel_polyfill.js`,
Expand Down
7 changes: 7 additions & 0 deletions x-pack/legacy/plugins/spaces/common/model/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export type GetSpacePurpose = 'any' | 'copySavedObjectsIntoSpace';
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import {
SavedObjectsManagementAction,
SavedObjectsManagementRecord,
} from 'ui/management/saved_objects_management';
import { i18n } from '@kbn/i18n';
import { toastNotifications } from 'ui/notify';
import { CopySavedObjectsToSpaceFlyout } from '../../views/management/components/copy_saved_objects_to_space';
import { Space } from '../../../common/model/space';
import { SpacesManager } from '../spaces_manager';

export class CopyToSpaceSavedObjectsManagementAction extends SavedObjectsManagementAction {
public id: string = 'copy_saved_objects_to_space';

public euiAction = {
name: i18n.translate('xpack.spaces.management.copyToSpace.actionTitle', {
defaultMessage: 'Copy to space',
}),
description: i18n.translate('xpack.spaces.management.copyToSpace.actionDescription', {
defaultMessage: 'Copy this saved object to one or more spaces',
}),
icon: 'spacesApp',
type: 'icon',
onClick: (object: SavedObjectsManagementRecord) => {
this.start(object);
},
};

constructor(private readonly spacesManager: SpacesManager, private readonly activeSpace: Space) {
super();
}

public render = () => {
if (!this.record) {
throw new Error('No record available! `render()` was likely called before `start()`.');
}
return (
<CopySavedObjectsToSpaceFlyout
onClose={this.onClose}
savedObject={this.record}
spacesManager={this.spacesManager}
activeSpace={this.activeSpace}
toastNotifications={toastNotifications}
/>
);
};

private onClose = () => {
this.finish();
};
}
Loading