Skip to content

Commit

Permalink
Change links mapping from object to array of links
Browse files Browse the repository at this point in the history
This makes it easier to use the Elasticsearch `_update` API for saved
objects. With arrays we are always replacing the entire selection. Using
`_update` with an object mapping applies only partial updates. So it was
not possible to delete a link.
  • Loading branch information
nickpeihl committed Jul 28, 2023
1 parent 6046fde commit c024f9c
Show file tree
Hide file tree
Showing 14 changed files with 66 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,6 @@
}
},
"navigation_embeddable": {
"dynamic": false,
"properties": {
"id": {
"type": "text"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
EmbeddableStateTransfer,
EmbeddableInput,
Container,
isReferenceOrValueEmbeddable,
} from '../..';

export const ACTION_EDIT_PANEL = 'editPanel';
Expand Down Expand Up @@ -94,6 +95,12 @@ export class EditPanelAction implements Action<ActionContext> {
const oldExplicitInput = embeddable.getExplicitInput();
const newExplicitInput = await factory.getExplicitInput(oldExplicitInput, embeddable.parent);
embeddable.parent?.replaceEmbeddable(embeddable.id, newExplicitInput);
if (
isReferenceOrValueEmbeddable(embeddable) &&
embeddable.inputIsRefType(embeddable.getInput())
) {
embeddable.reload();
}
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export type {
NavigationEmbeddableItem,
NavigationLinkType,
NavigationEmbeddableLink,
NavigationEmbeddableLinkList,
} from './latest';

export { DASHBOARD_LINK_TYPE, EXTERNAL_LINK_TYPE } from './latest';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const navigationEmbeddableAttributesSchema = schema.object(
{
title: schema.string(),
description: schema.maybe(schema.string()),
links: schema.maybe(schema.recordOf(schema.string(), navigationEmbeddableLinkSchema)),
links: schema.maybe(schema.arrayOf(navigationEmbeddableLinkSchema)),
},
{ unknowns: 'forbid' }
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export type {
NavigationEmbeddableCrudTypes,
NavigationEmbeddableAttributes,
NavigationEmbeddableLink,
NavigationEmbeddableLinkList,
NavigationLinkType,
} from './types';
export type NavigationEmbeddableItem = NavigationEmbeddableCrudTypes['Item'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,9 @@ export interface NavigationEmbeddableLink {
order: number;
}

export interface NavigationEmbeddableLinkList {
[id: string]: NavigationEmbeddableLink;
}

// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export type NavigationEmbeddableAttributes = {
title: string;
description?: string;
links?: NavigationEmbeddableLinkList;
links?: NavigationEmbeddableLink[];
};
2 changes: 1 addition & 1 deletion src/plugins/navigation_embeddable/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import type { SavedObjectsResolveResponse } from '@kbn/core-saved-objects-api-server';

export type NavigationEmbeddableContentType = 'navigationEmbeddable';
export type NavigationEmbeddableContentType = 'navigation_embeddable';

// TODO does this type need to be versioned?
export interface SharingSavedObjectProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ import { useNavigationEmbeddable } from '../embeddable/navigation_embeddable';
import { ExternalLinkComponent } from './external_link/external_link_component';
import { DashboardLinkComponent } from './dashboard_link/dashboard_link_component';
import { memoizedGetOrderedLinkList } from '../editor/navigation_embeddable_editor_tools';
import { NavigationEmbeddableByValueInput } from '../embeddable/types';

export const NavigationEmbeddableComponent = () => {
const navEmbeddable = useNavigationEmbeddable();

const links = navEmbeddable.select((state) => state.output.attributes?.links);
const links = navEmbeddable.select(
(state) => (state.explicitInput as NavigationEmbeddableByValueInput).attributes?.links
);

const orderedLinks = useMemo(() => {
if (!links) return [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import { coreServices } from '../services/kibana_services';
import {
NavigationEmbeddableAttributes,
NavigationEmbeddableLink,
NavigationEmbeddableLinkList,
} from '../../common/content_management';
import { NavEmbeddableStrings } from './navigation_embeddable_strings';

Expand Down Expand Up @@ -84,7 +83,11 @@ const NavigationEmbeddablePanelEditor = ({
const onDragEnd = useCallback(
({ source, destination }) => {
if (source && destination) {
const newList = euiDragDropReorder(orderedLinks, source.index, destination.index);
const newList = euiDragDropReorder(orderedLinks, source.index, destination.index).map(
(link, i) => {
return { ...link, order: i };
}
);
setOrderedLinks(newList);
}
},
Expand Down Expand Up @@ -136,9 +139,7 @@ const NavigationEmbeddablePanelEditor = ({
isLoading={isSaving}
onClick={async () => {
setIsSaving(true);
const newLinks = orderedLinks.reduce((prev, link, i) => {
return { ...prev, [link.id]: { ...link, order: i } };
}, {} as NavigationEmbeddableLinkList);
const newLinks = [...orderedLinks];
const newAttributes: NavigationEmbeddableAttributes = {
title: libraryTitle,
links: newLinks,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,12 @@
*/

import { memoize } from 'lodash';
import {
NavigationEmbeddableLink,
NavigationEmbeddableLinkList,
} from '../../common/content_management';
import { NavigationEmbeddableLink } from '../../common/content_management';

const getOrderedLinkList = (links: NavigationEmbeddableLinkList): NavigationEmbeddableLink[] => {
return Object.keys(links)
.map((linkId) => {
return links[linkId];
})
.sort((linkA, linkB) => {
return linkA.order - linkB.order;
});
const getOrderedLinkList = (links: NavigationEmbeddableLink[]): NavigationEmbeddableLink[] => {
return [...links].sort((linkA, linkB) => {
return linkA.order - linkB.order;
});
};

/**
Expand All @@ -28,10 +21,10 @@ const getOrderedLinkList = (links: NavigationEmbeddableLinkList): NavigationEmbe
* calculated this so, we can get away with using the cached version in the editor
*/
export const memoizedGetOrderedLinkList = memoize(
(links: NavigationEmbeddableLinkList) => {
(links: NavigationEmbeddableLink[]) => {
return getOrderedLinkList(links);
},
(links: NavigationEmbeddableLinkList) => {
(links: NavigationEmbeddableLink[]) => {
return links;
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ export async function openEditorFlyout(
const onSave = async (newAttributes: NavigationEmbeddableAttributes, useRefType: boolean) => {
const newInput = await getNavigationEmbeddableAttributeService().wrapAttributes(
newAttributes,
useRefType
useRefType,
initialInput
);
resolve(newInput);
parentDashboard?.reload();
editorFlyout.close();
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import React, { createContext, useContext } from 'react';
import { Subscription } from 'rxjs';

import {
AttributeService,
Expand All @@ -16,7 +17,7 @@ import {
} from '@kbn/embeddable-plugin/public';
import { DashboardContainer } from '@kbn/dashboard-plugin/public/dashboard_container';
import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util-plugin/public';
import { Subscription } from 'rxjs';

import { navigationEmbeddableReducers } from './navigation_embeddable_reducers';
import {
NavigationEmbeddableByReferenceInput,
Expand Down Expand Up @@ -57,8 +58,7 @@ export class NavigationEmbeddable
public readonly type = NAVIGATION_EMBEDDABLE_TYPE;
deferEmbeddableLoad = true;

private attributes?: NavigationEmbeddableAttributes;
private savedObjectId?: string;
private isDestroyed?: boolean;
private subscriptions: Subscription = new Subscription();

// state management
Expand Down Expand Up @@ -101,23 +101,31 @@ export class NavigationEmbeddable
this.cleanupStateTools = reduxEmbeddableTools.cleanup;
this.onStateChange = reduxEmbeddableTools.onStateChange;

this.subscriptions.add(
this.getInput$().subscribe(async () => {
const savedObjectId = (this.getInput() as NavigationEmbeddableByReferenceInput)
.savedObjectId;
const attributes = (this.getInput() as NavigationEmbeddableByValueInput).attributes;
if (this.attributes !== attributes || this.savedObjectId !== savedObjectId) {
this.savedObjectId = savedObjectId;
this.reload();
} else {
this.updateOutput({
attributes: this.attributes,
defaultTitle: this.attributes.title,
});
}
})
);
this.setInitializationFinished();
this.initializeSavedLinks(initialInput)
.then(() => this.setInitializationFinished())
.catch((e: Error) => this.onFatalError(e));
}

private async initializeSavedLinks(input: NavigationEmbeddableInput) {
const { metaInfo, attributes } = await this.attributeService.unwrapAttributes(input);
if (this.isDestroyed) return;

// TODO handle metaInfo

this.updateInput({ attributes });

await this.initializeOutput();
}

private async initializeOutput() {
const { attributes } = this.getInput() as NavigationEmbeddableByValueInput;
const { title, description } = this.getInput();
this.updateOutput({
defaultTitle: attributes.title,
defaultDescription: attributes.description,
title: title ?? attributes.title,
description: description ?? attributes.description,
});
}

public inputIsRefType(
Expand All @@ -137,21 +145,20 @@ export class NavigationEmbeddable
}

public async reload() {
this.attributes = (await this.attributeService.unwrapAttributes(this.input)).attributes;

this.updateOutput({
attributes: this.attributes,
defaultTitle: this.attributes.title,
defaultDescription: this.attributes.description,
});
if (this.isDestroyed) return;
await this.initializeSavedLinks(this.getInput());
this.render();
}

public destroy() {
this.isDestroyed = true;
super.destroy();
this.subscriptions.unsubscribe();
this.cleanupStateTools();
}

public render() {
if (this.isDestroyed) return;
return (
<NavigationEmbeddableContext.Provider value={this}>
<NavigationEmbeddableComponent />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,10 @@ export class NavigationEmbeddableFactoryDefinition
if (!(input as NavigationEmbeddableByReferenceInput).savedObjectId) {
(input as NavigationEmbeddableByReferenceInput).savedObjectId = savedObjectId;
}
return this.create(input, parent, savedObjectId);
return this.create(input, parent);
}

public async create(
initialInput: NavigationEmbeddableInput,
parent: DashboardContainer,
savedObjectId?: string
) {
public async create(initialInput: NavigationEmbeddableInput, parent: DashboardContainer) {
await untilPluginStartServicesReady();

const reduxEmbeddablePackage = await lazyLoadReduxToolsPackage();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export const navigationEmbeddableSavedObjectType: SavedObjectsType = {
},
},
mappings: {
dynamic: false,
properties: {
id: { type: 'text' },
title: { type: 'text' },
Expand Down

0 comments on commit c024f9c

Please sign in to comment.