Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Before hook for onRun of Catalog entities #3911

Merged
merged 38 commits into from
Oct 5, 2021
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e2f5978
Add prettier to be able to use .toMatchInlineSnapshot
chenhunghan Sep 29, 2021
b9c485d
Add 'onClickDetailIcon' event when user clicks the icon detail panel
chenhunghan Sep 29, 2021
aed5760
Add triggerOnRunAfterOnClickDetailIcon
chenhunghan Sep 29, 2021
22787a1
Move to .spec
chenhunghan Sep 29, 2021
4ebbccb
Fix test description
chenhunghan Sep 29, 2021
184612e
Fix typo
chenhunghan Sep 30, 2021
7cb1219
Fix indents
chenhunghan Sep 30, 2021
74ba7ed
Fix indents
chenhunghan Sep 30, 2021
9ed5b98
Remove onClickDetailIcon
chenhunghan Oct 4, 2021
b18ab31
Remove unused
chenhunghan Oct 4, 2021
2484b89
Remove unused
chenhunghan Oct 4, 2021
ddbee47
Remove unused
chenhunghan Oct 4, 2021
265b648
Remove triggerOnRunAfterOnClickDetailIcon
chenhunghan Oct 4, 2021
8b7080e
Remove unused
chenhunghan Oct 4, 2021
6a0e4d3
Fix typos
chenhunghan Oct 4, 2021
daed2aa
Fix react error in <Icon /> 'Objects are not valid as a React child (…
chenhunghan Oct 4, 2021
21272f7
Fix TypeError: category.metadata.icon.includes is not a function
chenhunghan Oct 4, 2021
0f63ccb
Disable safeDescriptors for mobx in test because we need to use jest.…
chenhunghan Oct 4, 2021
cfdb4c9
Add onRunHook extension api
chenhunghan Oct 4, 2021
a9e6d8b
Add missing ;
chenhunghan Oct 4, 2021
a877e10
Test if onRun get trigger, and use inlineSnapshot
chenhunghan Oct 4, 2021
613f84b
remove getAllOnRunHooks
chenhunghan Oct 5, 2021
6fe1809
Fix typo CatelogEntityOnRunHook > CatalogEntityOnRunHook
chenhunghan Oct 5, 2021
c053286
Remove CatalogEntityItem
chenhunghan Oct 5, 2021
f0b8479
Add defaultProps.catalogEntityStore
chenhunghan Oct 5, 2021
bbde39e
Should be optional
chenhunghan Oct 5, 2021
fcdd032
Update tests
chenhunghan Oct 5, 2021
d65e970
Add addOnRunHook return false test
chenhunghan Oct 5, 2021
2ba9315
Add tests when addOnRunHook return a promise
chenhunghan Oct 5, 2021
24f5f99
Catch the cases where onRunHook throws or reject
chenhunghan Oct 5, 2021
807aefd
Samne to <HotBarMenu />
chenhunghan Oct 5, 2021
74eb89c
onRunHook > onBeforeRun
chenhunghan Oct 5, 2021
c863134
observable.set > observable.map
chenhunghan Oct 5, 2021
44a8824
Support multiple onBeforeHooks per entity
Nokel81 Oct 5, 2021
c264d5f
Fix tests
Nokel81 Oct 5, 2021
87babc8
Cleanup and fix activate-entity
Nokel81 Oct 5, 2021
fe212ad
Fix
Nokel81 Oct 5, 2021
5c3337c
Fix docs
Nokel81 Oct 5, 2021
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@
"postcss": "^8.3.6",
"postcss-loader": "4.3.0",
"postinstall-postinstall": "^2.1.0",
"prettier": "^2.4.1",
chenhunghan marked this conversation as resolved.
Show resolved Hide resolved
"progress-bar-webpack-plugin": "^2.1.0",
"randomcolor": "^0.6.2",
"raw-loader": "^4.0.2",
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/lens-renderer-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class LensRendererExtension extends LensExtension {
}

/**
* Add a filtering function for the catalog catogries. This will be removed if the extension is disabled.
* Add a filtering function for the catalog categories. This will be removed if the extension is disabled.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Was a typo.

* @param fn The function which should return a truthy value for those categories which should be kept.
* @returns A function to clean up the filter
*/
Expand Down
19 changes: 18 additions & 1 deletion src/extensions/renderer-api/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import type { CatalogCategory, CatalogEntity } from "../../common/catalog";
import { catalogEntityRegistry as registry } from "../../renderer/api/catalog-entity-registry";

import type { CatalogEntityOnRunHook } from "../../renderer/api/catalog-entity-registry";
export { catalogCategoryRegistry as catalogCategories } from "../../common/catalog/catalog-category-registry";

export class CatalogEntityRegistry {
Expand All @@ -48,6 +48,23 @@ export class CatalogEntityRegistry {
getItemsForCategory<T extends CatalogEntity>(category: CatalogCategory): T[] {
return registry.getItemsForCategory(category);
}

/**
* Add a onRun hook to a catalog entity.
* @param catalogEntityUid The uid of the catalog entity
* @param onRunHook The function that should return a boolean if the onRun of catalog entity should be triggered.
* @returns A function to remove that hook
*/
addOnRunHook(catalogEntityUid: CatalogEntity["metadata"]["uid"], onRunHook: CatalogEntityOnRunHook) {
return registry.addOnRunHook(catalogEntityUid, onRunHook);
}

/**
* Returns one catalog entity onRun hook by catalog entity uid
*/
getOnRunHook(catalogEntityUid: CatalogEntity["metadata"]["uid"]): CatalogEntityOnRunHook | undefined {
return registry.getOnRunHook(catalogEntityUid);
}
}

export const catalogEntities = new CatalogEntityRegistry();
7 changes: 7 additions & 0 deletions src/jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,17 @@

import fetchMock from "jest-fetch-mock";
import configurePackages from "./common/configure-packages";
import { configure } from "mobx";

// setup default configuration for external npm-packages
configurePackages();

configure({
// Needed because we want to use jest.spyOn()
// ref https://github.com/mobxjs/mobx/issues/2784
safeDescriptors: false,
});

// rewire global.fetch to call 'fetchMock'
fetchMock.enableMocks();

Expand Down
34 changes: 34 additions & 0 deletions src/renderer/api/catalog-entity-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,22 @@ import { Disposer, iter } from "../utils";
import { once } from "lodash";

export type EntityFilter = (entity: CatalogEntity) => any;
export type CatalogEntityOnRunHook = (entity: CatalogEntity) => boolean | Promise<boolean>;

type CatalogEntityUid = CatalogEntity["metadata"]["uid"];
chenhunghan marked this conversation as resolved.
Show resolved Hide resolved

export class CatalogEntityRegistry {
@observable protected activeEntityId: string | undefined = undefined;
protected _entities = observable.map<string, CatalogEntity>([], { deep: true });
protected filters = observable.set<EntityFilter>([], {
deep: false,
});
protected entityOnRunHooks = observable.set<{
catalogEntityUid: CatalogEntityUid;
onRunHook: CatalogEntityOnRunHook
}>([], {
deep: false,
});

/**
* Buffer for keeping entities that don't yet have CatalogCategory synced
Expand Down Expand Up @@ -169,6 +178,31 @@ export class CatalogEntityRegistry {

return once(() => void this.filters.delete(fn));
}

/**
* Add a onRun hook to a catalog entity.
* @param uid The uid of the catalog entity
* @param onRunHook The function that should return a boolean if the onRun of catalog entity should be triggered.
* @returns A function to remove that hook
*/
addOnRunHook(catalogEntityUid: CatalogEntityUid, onRunHook: CatalogEntityOnRunHook): Disposer {
this.entityOnRunHooks.add({
catalogEntityUid,
onRunHook,
});

return once(() => void this.entityOnRunHooks.delete({
catalogEntityUid,
onRunHook,
}));
}

/**
* Returns one catalog entity onRun hook by catalog entity uid
*/
getOnRunHook(_catalogEntityUid: CatalogEntityUid): CatalogEntityOnRunHook | undefined {
return Array.from(this.entityOnRunHooks).find(({ catalogEntityUid }) => catalogEntityUid === _catalogEntityUid)?.onRunHook;
}
}

export const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
Expand Down
12 changes: 8 additions & 4 deletions src/renderer/components/+catalog/catalog-entity-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ import "./catalog-entity-details.scss";
import React, { Component } from "react";
import { observer } from "mobx-react";
import { Drawer, DrawerItem } from "../drawer";
import { catalogEntityRunContext } from "../../api/catalog-entity";
import type { CatalogCategory, CatalogEntity } from "../../../common/catalog";
import { Icon } from "../icon";
import { CatalogEntityDrawerMenu } from "./catalog-entity-drawer-menu";
import { CatalogEntityDetailRegistry } from "../../../extensions/registries";
import { HotbarIcon } from "../hotbar/hotbar-icon";
import type { CatalogEntityItem } from "./catalog-entity.store";
import type { CatalogEntityItem } from "./catalog-entity-item";
import { isDevelopment } from "../../../common/vars";

interface Props<T extends CatalogEntity> {
item: CatalogEntityItem<T> | null | undefined;
hideDetails(): void;
onClickDetailPanelIcon: (catalogEntitiyItem: CatalogEntityItem<T>) => void;
}

@observer
Expand Down Expand Up @@ -68,8 +68,12 @@ export class CatalogEntityDetails<T extends CatalogEntity> extends Component<Pro
material={item.entity.spec.icon?.material}
background={item.entity.spec.icon?.background}
disabled={!item?.enabled}
onClick={() => item.onRun(catalogEntityRunContext)}
size={128} />
onClick={() => {
this.props.onClickDetailPanelIcon(item);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think we should pass this in as props, we should have an unexported function that takes an CatalogEntity and does the work once (DRY as it were).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can the refactoring be another PR?

}}
size={128}
data-testid="detail-panel-hot-bar-icon"
/>
{item?.enabled && (
<div className="IconHint">
Click to open
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { MenuItem } from "../menu";
import { ConfirmDialog } from "../confirm-dialog";
import { HotbarStore } from "../../../common/hotbar-store";
import { Icon } from "../icon";
import type { CatalogEntityItem } from "./catalog-entity.store";
import type { CatalogEntityItem } from "./catalog-entity-item";

export interface CatalogEntityDrawerMenuProps<T extends CatalogEntity> extends MenuActionsProps {
item: CatalogEntityItem<T> | null | undefined;
Expand Down
141 changes: 141 additions & 0 deletions src/renderer/components/+catalog/catalog-entity-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/**
* Copyright (c) 2021 OpenLens Authors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import styles from "./catalog.module.css";
import React from "react";
import { action, computed } from "mobx";
import type { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity";
import type { CatalogEntityOnRunHook } from "../../api/catalog-entity-registry";
import type { ItemObject } from "../../../common/item.store";
import { Badge } from "../badge";
import { navigation } from "../../navigation";
import { searchUrlParam } from "../input";
import { makeCss } from "../../../common/utils/makeCss";
import { KubeObject } from "../../../common/k8s-api/kube-object";
import { toJS } from "mobx";

const css = makeCss(styles);

const isPromise = (obj: any): obj is Promise<any> => (obj?.then && typeof obj?.then === "function") ? true: false;

export class CatalogEntityItem<T extends CatalogEntity> implements ItemObject {
constructor(public entity: T) {}

get kind() {
return this.entity.kind;
}

get apiVersion() {
return this.entity.apiVersion;
}

get name() {
return this.entity.metadata.name;
}

getName() {
return this.entity.metadata.name;
}

get id() {
return this.entity.metadata.uid;
}

getId() {
return this.id;
}

@computed get phase() {
return this.entity.status.phase;
}

get enabled() {
return this.entity.status.enabled ?? true;
}

get labels() {
return KubeObject.stringifyLabels(this.entity.metadata.labels);
}

getLabelBadges(onClick?: React.MouseEventHandler<any>) {
return this.labels
.map(label => (
<Badge
className={css.badge}
key={label}
label={label}
title={label}
onClick={(event) => {
navigation.searchParams.set(searchUrlParam.name, label);
onClick?.(event);
event.stopPropagation();
}}
expandable={false}
/>
));
}

get source() {
return this.entity.metadata.source || "unknown";
}

get searchFields() {
return [
this.name,
this.id,
this.phase,
`source=${this.source}`,
...this.labels,
];
}

onRun(onRunHook: CatalogEntityOnRunHook | undefined, ctx: CatalogEntityActionContext) {
if (!onRunHook) {
this.entity.onRun(ctx);

return;
}

if (typeof onRunHook === "function") {
Copy link
Collaborator

Choose a reason for hiding this comment

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

These two checks can be combined:

if (typeof onRunHook !== "function") {
  ...
  return;
}

...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe, but with less readability IMHO.

let shouldRun;

try {
shouldRun = onRunHook(toJS(this.entity));
} catch (error) {
if (process?.env?.NODE_ENV !== "test") console.warn(`[CATALOG-ENTITY-ITEM] onRunHook of entity.metadata.uid ${this.entity?.metadata?.uid} throw an exception, stop before onRun`, error);
}

if (isPromise(shouldRun)) {
Promise.resolve(shouldRun).then((shouldRun) => {
if (shouldRun) this.entity.onRun(ctx);
}).catch((error) => {
if (process?.env?.NODE_ENV !== "test") console.warn(`[CATALOG-ENTITY-ITEM] onRunHook of entity.metadata.uid ${this.entity?.metadata?.uid} rejects, stop before onRun`, error);
});
} else if (shouldRun) {
this.entity.onRun(ctx);
}
}
}

@action
async onContextMenuOpen(ctx: any) {
return this.entity.onContextMenuOpen(ctx);
}
}
Loading