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 all 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
13 changes: 12 additions & 1 deletion src/extensions/renderer-api/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@

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

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

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

/**
* Add a onBeforeRun hook to a catalog entity. If `onBeforeRun` was previously added then it will not be added again
* @param catalogEntityUid The uid of the catalog entity
* @param onBeforeRun The function that should return a boolean if the onRun of catalog entity should be triggered.
* @returns A function to remove that hook
*/
addOnBeforeRun(entity: CatalogEntity, onBeforeRun: CatalogEntityOnBeforeRun): Disposer {
return registry.addOnBeforeRun(entity, onBeforeRun);
}
}

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
77 changes: 76 additions & 1 deletion src/renderer/api/catalog-entity-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,31 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import { computed, observable, makeObservable, action } from "mobx";
import { computed, observable, makeObservable, action, ObservableSet } from "mobx";
import { ipcRendererOn } from "../../common/ipc";
import { CatalogCategory, CatalogEntity, CatalogEntityData, catalogCategoryRegistry, CatalogCategoryRegistry, CatalogEntityKindData } from "../../common/catalog";
import "../../common/catalog-entities";
import type { Cluster } from "../../main/cluster";
import { ClusterStore } from "../../common/cluster-store";
import { Disposer, iter } from "../utils";
import { once } from "lodash";
import logger from "../../common/logger";
import { catalogEntityRunContext } from "./catalog-entity";

export type EntityFilter = (entity: CatalogEntity) => any;
export type CatalogEntityOnBeforeRun = (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 onBeforeRunHooks = observable.map<CatalogEntityUid, ObservableSet<CatalogEntityOnBeforeRun>>({}, {
deep: false,
});

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

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

/**
* Add a onBeforeRun hook to a catalog entity. If `onBeforeRun` was previously added then it will not be added again
* @param catalogEntityUid The uid of the catalog entity
* @param onBeforeRun The function that should return a boolean if the onRun of catalog entity should be triggered.
* @returns A function to remove that hook
*/
addOnBeforeRun(entityOrId: CatalogEntity | CatalogEntityUid, onBeforeRun: CatalogEntityOnBeforeRun): Disposer {
logger.debug(`[CATALOG-ENTITY-REGISTRY]: adding onBeforeRun to ${entityOrId}`);

const id = typeof entityOrId === "string"
? entityOrId
: entityOrId.getId();
const hooks = this.onBeforeRunHooks.get(id) ??
this.onBeforeRunHooks.set(id, observable.set([], { deep: false })).get(id);

hooks.add(onBeforeRun);

return once(() => void hooks.delete(onBeforeRun));
}

/**
* Runs all the registered `onBeforeRun` hooks, short circuiting on the first falsy returned/resolved valued
* @param entity The entity to run the hooks on
* @returns Whether the entities `onRun` method should be executed
*/
async onBeforeRun(entity: CatalogEntity): Promise<boolean> {
logger.debug(`[CATALOG-ENTITY-REGISTRY]: run onBeforeRun on ${entity.getId()}`);

const hooks = this.onBeforeRunHooks.get(entity.getId());

if (!hooks) {
return true;
}

for (const onBeforeRun of hooks) {
try {
if (!await onBeforeRun(entity)) {
return false;
}
} catch (error) {
logger.warn(`[CATALOG-ENTITY-REGISTRY]: entity ${entity.getId()} onBeforeRun threw an error`, error);

// If a handler throws treat it as if it has returned `false`
// Namely: assume that its internal logic has failed and didn't complete as expected
return false;
}
}

return true;
}

/**
* Perform the onBeforeRun check and, if successful, then proceed to call `entity`'s onRun method
* @param entity The instance to invoke the hooks and then execute the onRun
*/
onRun(entity: CatalogEntity): void {
this.onBeforeRun(entity)
.then(doOnRun => {
if (doOnRun) {
return entity.onRun?.(catalogEntityRunContext);
} else {
logger.debug(`onBeforeRun for ${entity.getId()} returned false`);
}
})
.catch(error => logger.error(`[CATALOG-ENTITY-REGISTRY]: entity ${entity.getId()} onRun threw an error`, error));
}
}

export const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
Expand Down
9 changes: 5 additions & 4 deletions src/renderer/components/+catalog/catalog-entity-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@ 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> {
Expand Down Expand Up @@ -68,8 +67,10 @@ 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={() => item.onRun()}
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
114 changes: 114 additions & 0 deletions src/renderer/components/+catalog/catalog-entity-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* 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 } from "../../api/catalog-entity";
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 type { CatalogEntityRegistry } from "../../api/catalog-entity-registry";

const css = makeCss(styles);

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

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() {
this.registry.onRun(this.entity);
}

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