From 8930306c217fccd399def136c94be957200055ff Mon Sep 17 00:00:00 2001 From: codeSafari10 Date: Sat, 14 Sep 2024 15:21:34 +0530 Subject: [PATCH 1/6] feat: create event bus actor Signed-off-by: codeSafari10 --- src/actors/eventBus.ts | 42 ++++++++++++++++++++++++++++++++++++++++++ src/actors/index.ts | 2 ++ 2 files changed, 44 insertions(+) create mode 100644 src/actors/eventBus.ts diff --git a/src/actors/eventBus.ts b/src/actors/eventBus.ts new file mode 100644 index 000000000..ecc6f4940 --- /dev/null +++ b/src/actors/eventBus.ts @@ -0,0 +1,42 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Observable, Subject, filter } from 'rxjs'; + +// Generic Event interface +export interface EventBusEvent { + type: Type; + data: Data; +} + +// Helper type to extract event types from a union +export type EventType = T extends EventBusEvent ? Type : never; + +// Helper type to extract payload type for a given event type +export type DataType = T extends EventBusEvent + ? Data + : never; + +// Generic EventBus class +export class EventBus> { + private eventSubject: Subject; + private eventObservable: Observable; + + constructor() { + this.eventSubject = new Subject(); + this.eventObservable = this.eventSubject.asObservable(); + } + + // Method to publish an event + publish(event: E): void { + this.eventSubject.next(event); + } + + // Method to subscribe to a specific event type + on(eventType: EventType): Observable { + return this.eventObservable.pipe(filter((event): event is T => event.type === eventType)); + } + + // Method to subscribe to all events + onAny(): Observable { + return this.eventObservable; + } +} diff --git a/src/actors/index.ts b/src/actors/index.ts index 2923105d7..a366767db 100644 --- a/src/actors/index.ts +++ b/src/actors/index.ts @@ -37,3 +37,5 @@ export { sendToActor, sendToActors } from './utils'; + +export * from './eventBus'; From 9044d57775c8a8b12b3a0b9bae900fc0bd5d58ad Mon Sep 17 00:00:00 2001 From: codeSafari10 Date: Sat, 14 Sep 2024 15:22:06 +0530 Subject: [PATCH 2/6] feat: create permission component Signed-off-by: codeSafari10 --- src/custom/index.tsx | 1 + src/custom/permissions.tsx | 104 +++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/custom/permissions.tsx diff --git a/src/custom/index.tsx b/src/custom/index.tsx index 658859464..b7734382c 100644 --- a/src/custom/index.tsx +++ b/src/custom/index.tsx @@ -122,3 +122,4 @@ export type { }; export * from './Dialog'; +export * from './permissions'; diff --git a/src/custom/permissions.tsx b/src/custom/permissions.tsx new file mode 100644 index 000000000..04bcef57b --- /dev/null +++ b/src/custom/permissions.tsx @@ -0,0 +1,104 @@ +// import { CAN, getCapabilitiesRegistry, getMesheryEventBus } from '@/globals/mesherySdk'; +import React from 'react'; +import { EventBus } from '../actors/eventBus'; + +interface Key { + subject: string; + action: string; +} + +type InvertAction = 'disable' | 'hide'; + +export type MissingPermissionReason = { + type: 'MISSING_PERMISSION'; + data: { + keyId: string; + }; +}; + +export type MissingCapabilityReason = { + type: 'MISSING_CAPABILITY'; + data: { + capabilityId: string; + }; +}; + +export type ReasonEvent = MissingPermissionReason | MissingCapabilityReason; + +interface HasKeyProps { + Key?: Key; + predicate?: (capabilitiesRegistry: unknown) => [boolean, ReasonEvent]; // returns a boolean and an event if the user does not have the permission + children: React.ReactNode; + allowClick?: boolean; + invert_action?: InvertAction[]; +} + +// returns the children if the user has the permission to view the component or if a key is not provided +// if the user does not have the permission to view the component, it will return null or a disabled version of the component specified by the invert_action prop +export const createCanShow = ( + getCapabilitiesRegistry = () => {}, + CAN: (action: string, subject: string) => boolean, + eventBus: () => EventBus +) => { + return ({ Key, children, predicate, invert_action = ['disable'] }: HasKeyProps) => { + if (!children) { + return null; + } + + const hasKey = Key?.subject ? CAN(Key?.action, Key?.subject) : true; + const predicateRes = predicate && predicate(getCapabilitiesRegistry()); + + //WARNING: remove the false from the below line to enable the feature + const can = predicateRes ? predicateRes[0] && hasKey && false : hasKey && false; + + const reason = predicateRes?.[1] || { + type: 'MISSING_PERMISSION', + data: { + keyId: Key?.action as string + } + }; + + if (can) { + return <>{children}; + } + + if (invert_action.includes('hide')) { + return null; + } + + const isClickable = children && (children as React.ReactElement).props.onClick; + const pointerEvents = isClickable ? 'auto' : 'none'; + + const onClick = isClickable + ? () => { + console.log('cant perform action : reason', reason); + const mesheryEventBus = eventBus(); + mesheryEventBus.publish(reason); + } + : () => {}; + + const opacity = invert_action.includes('disable') ? 0.5 : 1; + + return ( +
+ {React.cloneElement(children as React.ReactElement, { + style: { + ...((children as React.ReactElement).props.style as React.CSSProperties), + cursor: 'pointer', + pointerEvents, + opacity: opacity + }, + onClick: onClick + })} +
+ ); + + return null; + }; +}; From eb4c754eb7362d064b97ba211732219a5ec0397b Mon Sep 17 00:00:00 2001 From: codeSafari10 Date: Sat, 14 Sep 2024 17:42:24 +0530 Subject: [PATCH 3/6] fix: remove hardcoded false Signed-off-by: codeSafari10 --- package-lock.json | 15 +++++++-------- src/custom/permissions.tsx | 11 +++++------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 51cf5208e..1d8b59de3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,7 @@ "name": "@layer5/sistent", "version": "0.14.11", "dependencies": { - "lodash": "^4.17.21", - "@types/lodash": "^4.17.7" + "lodash": "^4.17.21" }, "devDependencies": { "@commitlint/cli": "^17.7.2", @@ -3161,7 +3160,8 @@ "node_modules/@types/lodash": { "version": "4.17.7", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", - "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==" + "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", + "dev": true }, "node_modules/@types/mdast": { "version": "3.0.15", @@ -9299,8 +9299,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "devOptional": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.assignwith": { "version": "4.2.0", @@ -16410,7 +16409,8 @@ "@types/lodash": { "version": "4.17.7", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", - "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==" + "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", + "dev": true }, "@types/mdast": { "version": "3.0.15", @@ -20639,8 +20639,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "devOptional": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.assignwith": { "version": "4.2.0", diff --git a/src/custom/permissions.tsx b/src/custom/permissions.tsx index 04bcef57b..096255ac4 100644 --- a/src/custom/permissions.tsx +++ b/src/custom/permissions.tsx @@ -2,12 +2,12 @@ import React from 'react'; import { EventBus } from '../actors/eventBus'; -interface Key { +export interface Key { subject: string; action: string; } -type InvertAction = 'disable' | 'hide'; +export type InvertAction = 'disable' | 'hide'; export type MissingPermissionReason = { type: 'MISSING_PERMISSION'; @@ -25,7 +25,7 @@ export type MissingCapabilityReason = { export type ReasonEvent = MissingPermissionReason | MissingCapabilityReason; -interface HasKeyProps { +export interface HasKeyProps { Key?: Key; predicate?: (capabilitiesRegistry: unknown) => [boolean, ReasonEvent]; // returns a boolean and an event if the user does not have the permission children: React.ReactNode; @@ -48,8 +48,7 @@ export const createCanShow = ( const hasKey = Key?.subject ? CAN(Key?.action, Key?.subject) : true; const predicateRes = predicate && predicate(getCapabilitiesRegistry()); - //WARNING: remove the false from the below line to enable the feature - const can = predicateRes ? predicateRes[0] && hasKey && false : hasKey && false; + const can = predicateRes ? predicateRes[0] && hasKey : hasKey && false; const reason = predicateRes?.[1] || { type: 'MISSING_PERMISSION', @@ -71,7 +70,7 @@ export const createCanShow = ( const onClick = isClickable ? () => { - console.log('cant perform action : reason', reason); + console.log('cant perform action : reason', reason, eventBus); const mesheryEventBus = eventBus(); mesheryEventBus.publish(reason); } From fe9faf18e68d919f4330f3c5e72963e18b9276bb Mon Sep 17 00:00:00 2001 From: codeSafari10 Date: Sat, 14 Sep 2024 18:16:21 +0530 Subject: [PATCH 4/6] fix: remove hardcoded false Signed-off-by: codeSafari10 --- src/custom/permissions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/custom/permissions.tsx b/src/custom/permissions.tsx index 096255ac4..ffbdcea30 100644 --- a/src/custom/permissions.tsx +++ b/src/custom/permissions.tsx @@ -48,7 +48,7 @@ export const createCanShow = ( const hasKey = Key?.subject ? CAN(Key?.action, Key?.subject) : true; const predicateRes = predicate && predicate(getCapabilitiesRegistry()); - const can = predicateRes ? predicateRes[0] && hasKey : hasKey && false; + const can = predicateRes ? predicateRes[0] && hasKey : hasKey; const reason = predicateRes?.[1] || { type: 'MISSING_PERMISSION', From 4a019a1ae37ca90772d578d90a106754862eeca2 Mon Sep 17 00:00:00 2001 From: codeSafari10 Date: Sat, 14 Sep 2024 19:21:22 +0530 Subject: [PATCH 5/6] fix: add click trigger Signed-off-by: codeSafari10 --- src/custom/permissions.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/custom/permissions.tsx b/src/custom/permissions.tsx index ffbdcea30..ca0d90969 100644 --- a/src/custom/permissions.tsx +++ b/src/custom/permissions.tsx @@ -29,7 +29,7 @@ export interface HasKeyProps { Key?: Key; predicate?: (capabilitiesRegistry: unknown) => [boolean, ReasonEvent]; // returns a boolean and an event if the user does not have the permission children: React.ReactNode; - allowClick?: boolean; + notifyOnclick?: boolean; invert_action?: InvertAction[]; } @@ -40,7 +40,13 @@ export const createCanShow = ( CAN: (action: string, subject: string) => boolean, eventBus: () => EventBus ) => { - return ({ Key, children, predicate, invert_action = ['disable'] }: HasKeyProps) => { + return ({ + Key, + children, + notifyOnclick = true, + predicate, + invert_action = ['disable'] + }: HasKeyProps) => { if (!children) { return null; } @@ -65,11 +71,12 @@ export const createCanShow = ( return null; } - const isClickable = children && (children as React.ReactElement).props.onClick; - const pointerEvents = isClickable ? 'auto' : 'none'; + const pointerEvents = notifyOnclick ? 'auto' : 'none'; + console.log('cant perform action ', reason, eventBus); - const onClick = isClickable - ? () => { + const onClick = notifyOnclick + ? (e: React.MouseEvent) => { + e.stopPropagation(); console.log('cant perform action : reason', reason, eventBus); const mesheryEventBus = eventBus(); mesheryEventBus.publish(reason); @@ -85,6 +92,7 @@ export const createCanShow = ( pointerEvents, opacity: opacity }} + onClick={onClick} > {React.cloneElement(children as React.ReactElement, { style: { From b2f069635d7c608ce7f32a5fcf85509150f48fe0 Mon Sep 17 00:00:00 2001 From: codeSafari10 Date: Sat, 14 Sep 2024 19:38:05 +0530 Subject: [PATCH 6/6] fix: remove logs Signed-off-by: codeSafari10 --- src/custom/permissions.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/custom/permissions.tsx b/src/custom/permissions.tsx index ca0d90969..0951708d3 100644 --- a/src/custom/permissions.tsx +++ b/src/custom/permissions.tsx @@ -64,7 +64,7 @@ export const createCanShow = ( }; if (can) { - return <>{children}; + return children; } if (invert_action.includes('hide')) { @@ -72,7 +72,6 @@ export const createCanShow = ( } const pointerEvents = notifyOnclick ? 'auto' : 'none'; - console.log('cant perform action ', reason, eventBus); const onClick = notifyOnclick ? (e: React.MouseEvent) => {