Skip to content

Commit

Permalink
feat: slot service implementation in angular-remote-components (onecx…
Browse files Browse the repository at this point in the history
…#251)

* feat: slot service with permission service to be proxied

* feat: lint

* feat: new topic and structure

* feat: extended SlotService with component presence check

* fix: topic not synchronizable

* refactor: someComponentDefinedForSlot refactor

* fix: fixed comparsion between strings
  • Loading branch information
markuczy authored May 15, 2024
1 parent 32ac035 commit a86e682
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 27 deletions.
4 changes: 3 additions & 1 deletion libs/angular-remote-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
"@angular/common": "^15.2.7",
"@angular/core": "^15.2.7",
"@onecx/angular-accelerator": "^4",
"@onecx/integration-interface": "^4",
"@ngx-translate/core": "^14.0.0",
"rxjs": "~7.8.0"
"rxjs": "~7.8.0",
"@angular-architects/module-federation": "15.0.0"
},
"dependencies": {},
"publishConfig": {
Expand Down
3 changes: 2 additions & 1 deletion libs/angular-remote-components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export * from './lib/model/injection-tokens'
export * from './lib/components/slot/slot.component'
export * from './lib/angular-remote-components.module'
export * from './lib/services/slot.service'
export * from './lib/utils/provide-translate-service-for-root.utils'
export * from './lib/services/permission.service'
export * from './lib/utils/provide-translate-service-for-root.utils'
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import { AppConfigService } from '@onecx/angular-accelerator'
imports: [CommonModule],
declarations: [SlotComponent],
exports: [SlotComponent],
providers: [AppConfigService]
providers: [AppConfigService],
})
export class AngularRemoteComponentsModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Injectable } from '@angular/core'
import { PermissionsRpcTopic } from '@onecx/integration-interface'
import { filter, firstValueFrom, map } from 'rxjs'

@Injectable({ providedIn: 'root' })
export class PermissionService {
private permissionsTopic$ = new PermissionsRpcTopic()

async getPermissions(appId: string, productName: string): Promise<string[]> {
const permissions = firstValueFrom(
this.permissionsTopic$.pipe(
filter(
(message) =>
message.appId === appId && message.productName === productName && Array.isArray(message.permissions)
),
map((message) => message.permissions ?? [])
)
)
this.permissionsTopic$.publish({ appId: appId, productName: productName })
return permissions
}
}
74 changes: 66 additions & 8 deletions libs/angular-remote-components/src/lib/services/slot.service.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,77 @@
import { InjectionToken, Type } from '@angular/core'
import { Observable } from 'rxjs'
import { loadRemoteModule } from '@angular-architects/module-federation'
import { Injectable, InjectionToken, Type } from '@angular/core'
import { RemoteComponent, RemoteComponentsTopic } from '@onecx/integration-interface'
import { Observable, map, shareReplay } from 'rxjs'
import { PermissionService } from './permission.service'

export const SLOT_SERVICE: InjectionToken<SlotService> = new InjectionToken('SLOT_SERVICE')

export type RemoteComponentInfo = { appId: string; productName: string; baseUrl: string }

export type SlotComponentConfiguration = {
componentType: Promise<Type<unknown> | undefined> | Type<unknown> | undefined;
remoteComponent: RemoteComponentInfo;
permissions: Promise<string[]> | string[];
componentType: Promise<Type<unknown> | undefined> | Type<unknown> | undefined
remoteComponent: RemoteComponentInfo
permissions: Promise<string[]> | string[]
}

export interface SlotService {
init(): Promise<void>
getComponentsForSlot(
slotName: string
): Observable<SlotComponentConfiguration[]>
getComponentsForSlot(slotName: string): Observable<SlotComponentConfiguration[]>
isSomeComponentDefinedForSlot(slotName: string): Observable<boolean>
}

@Injectable({ providedIn: 'root' })
export class SlotService implements SlotService {
remoteComponents$ = new RemoteComponentsTopic()

constructor(private permissionsService: PermissionService) {}

async init(): Promise<void> {
return Promise.resolve()
}

getComponentsForSlot(slotName: string): Observable<SlotComponentConfiguration[]> {
return this.remoteComponents$.pipe(
map((remoteComponentsInfo) =>
(remoteComponentsInfo.slots?.find((slotMapping) => slotMapping.name === slotName)?.components ?? [])
.map((remoteComponentName) => remoteComponentsInfo.components.find((rc) => rc.name === remoteComponentName))
.filter((remoteComponent): remoteComponent is RemoteComponent => !!remoteComponent)
.map((remoteComponent) => remoteComponent)
),
map((infos) =>
infos.map((remoteComponent) => ({
componentType: this.loadComponent(remoteComponent),
remoteComponent,
permissions: this.permissionsService.getPermissions(remoteComponent.appId, remoteComponent.productName),
}))
),
shareReplay()
)
}

isSomeComponentDefinedForSlot(slotName: string): Observable<boolean> {
return this.remoteComponents$.pipe(
map((remoteComponentsInfo) => remoteComponentsInfo.slots.some((slotMapping) => slotMapping.name === slotName))
)
}

private async loadComponent(component: {
remoteEntryUrl: string
exposedModule: string
}): Promise<Type<unknown> | undefined> {
try {
const exposedModule = component.exposedModule.startsWith('./')
? component.exposedModule.slice(2)
: component.exposedModule
const m = await loadRemoteModule({
type: 'module',
remoteEntry: component.remoteEntryUrl,
exposedModule: './' + exposedModule,
})
return m[exposedModule]
} catch (e) {
console.log('Failed to load remote module ', component.exposedModule, component.remoteEntryUrl, e)
return undefined
}
}
}
5 changes: 5 additions & 0 deletions libs/integration-interface/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ export * from './lib/topics/message/v1/message.model'
export * from './lib/topics/message/v1/message.topic'

export * from './lib/topics/remote-components/v1/remote-component.model'
export * from './lib/topics/remote-components/v1/remote-components-info.model'
export * from './lib/topics/remote-components/v1/slot.model'
export * from './lib/topics/remote-components/v1/remote-components.topic'

export * from './lib/topics/permissions/v1/permissions.topic'

export * from './lib/topics/permissions-rpc/v1/permissions-rpc.topic'
export * from './lib/topics/permissions-rpc/v1/permissions-rpc.model'

export * from './lib/topics/events/v1/events-topic'
export * from './lib/topics/events/v1/topic-event-type'
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface PermissionsRpc {
appId: string
productName: string
permissions?: Array<string>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Topic } from '@onecx/accelerator'
import { PermissionsRpc } from './permissions-rpc.model'

export class PermissionsRpcTopic extends Topic<PermissionsRpc> {
constructor() {
super('permissionsRpc', 1)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SyncableTopic } from "@onecx/accelerator";
import { SyncableTopic } from '@onecx/accelerator'

export class PermissionsTopic extends SyncableTopic<string[]> {
constructor() {
super('permissions', 1)
}
}
constructor() {
super('permissions', 1)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export interface RemoteComponent {
name: string;
baseUrl: string;
remoteEntryUrl: string;
appId: string;
productName: string;
exposedModule: string;
}
export type RemoteComponent = {
name: string
baseUrl: string
remoteEntryUrl: string
appId: string
productName: string
exposedModule: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { RemoteComponent } from './remote-component.model'
import { Slot } from './slot.model'

export type RemoteComponentsInfo = { components: RemoteComponent[]; slots: Slot[] }
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Topic } from '@onecx/accelerator'
import { RemoteComponent } from './remote-component.model'
import { RemoteComponentsInfo } from './remote-components-info.model'

export class RemoteComponentsTopic extends Topic<RemoteComponent[]> {
export class RemoteComponentsTopic extends Topic<RemoteComponentsInfo> {
constructor() {
super('remoteComponents', 1)
super('remoteComponentsInfo', 1)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type Slot = {
name: string
components: Array<string>
}

0 comments on commit a86e682

Please sign in to comment.