Skip to content

Commit

Permalink
Merge pull request #737 from codeSafari10/eventBus
Browse files Browse the repository at this point in the history
Implement permissions flow
  • Loading branch information
aabidsofi19 committed Sep 14, 2024
2 parents 6cde861 + b2f0696 commit 8c9cb0e
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 8 deletions.
15 changes: 7 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions src/actors/eventBus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Observable, Subject, filter } from 'rxjs';

// Generic Event interface
export interface EventBusEvent<Type extends string, Data = unknown> {
type: Type;
data: Data;
}

// Helper type to extract event types from a union
export type EventType<T> = T extends EventBusEvent<infer Type, any> ? Type : never;

// Helper type to extract payload type for a given event type
export type DataType<T, Type extends string> = T extends EventBusEvent<Type, infer Data>
? Data
: never;

// Generic EventBus class
export class EventBus<T extends EventBusEvent<string, any>> {
private eventSubject: Subject<T>;
private eventObservable: Observable<T>;

constructor() {
this.eventSubject = new Subject<T>();
this.eventObservable = this.eventSubject.asObservable();
}

// Method to publish an event
publish<E extends T>(event: E): void {
this.eventSubject.next(event);
}

// Method to subscribe to a specific event type
on(eventType: EventType<T>): Observable<T> {
return this.eventObservable.pipe(filter((event): event is T => event.type === eventType));
}

// Method to subscribe to all events
onAny(): Observable<T> {
return this.eventObservable;
}
}
2 changes: 2 additions & 0 deletions src/actors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ export {
sendToActor,
sendToActors
} from './utils';

export * from './eventBus';
1 change: 1 addition & 0 deletions src/custom/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,4 @@ export type {
};

export * from './Dialog';
export * from './permissions';
110 changes: 110 additions & 0 deletions src/custom/permissions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// import { CAN, getCapabilitiesRegistry, getMesheryEventBus } from '@/globals/mesherySdk';
import React from 'react';
import { EventBus } from '../actors/eventBus';

export interface Key {
subject: string;
action: string;
}

export 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;

export interface HasKeyProps<ReasonEvent> {
Key?: Key;
predicate?: (capabilitiesRegistry: unknown) => [boolean, ReasonEvent]; // returns a boolean and an event if the user does not have the permission
children: React.ReactNode;
notifyOnclick?: 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<ReasonEvent>
) => {
return ({
Key,
children,
notifyOnclick = true,
predicate,
invert_action = ['disable']
}: HasKeyProps<ReasonEvent>) => {
if (!children) {
return null;
}

const hasKey = Key?.subject ? CAN(Key?.action, Key?.subject) : true;
const predicateRes = predicate && predicate(getCapabilitiesRegistry());

const can = predicateRes ? predicateRes[0] && hasKey : hasKey;

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 pointerEvents = notifyOnclick ? 'auto' : 'none';

const onClick = notifyOnclick
? (e: React.MouseEvent<HTMLDivElement | HTMLElement>) => {
e.stopPropagation();
console.log('cant perform action : reason', reason, eventBus);
const mesheryEventBus = eventBus();
mesheryEventBus.publish(reason);
}
: () => {};

const opacity = invert_action.includes('disable') ? 0.5 : 1;

return (
<div
style={{
cursor: 'pointer',
pointerEvents,
opacity: opacity
}}
onClick={onClick}
>
{React.cloneElement(children as React.ReactElement, {
style: {
...((children as React.ReactElement).props.style as React.CSSProperties),
cursor: 'pointer',
pointerEvents,
opacity: opacity
},
onClick: onClick
})}
</div>
);

return null;
};
};

0 comments on commit 8c9cb0e

Please sign in to comment.