arch(sprint1): drive widget registry from manifest
This commit is contained in:
@@ -27,15 +27,28 @@ export class PlatformRuntimeService {
|
|||||||
|
|
||||||
this.configService.loadBootstrap().pipe(take(1)).subscribe({
|
this.configService.loadBootstrap().pipe(take(1)).subscribe({
|
||||||
next: (bootstrap) => {
|
next: (bootstrap) => {
|
||||||
this.widgetRegistryBootstrap.registerDefaults();
|
this.widgetRegistryBootstrap.registerFromManifest().subscribe({
|
||||||
this.themeEngine.applyTheme(bootstrap.theme);
|
next: () => {
|
||||||
this.brandingEngine.applyBranding(bootstrap.branding);
|
this.themeEngine.applyTheme(bootstrap.theme);
|
||||||
this.runtimeState.markInitialized({
|
this.brandingEngine.applyBranding(bootstrap.branding);
|
||||||
localization: bootstrap.localization,
|
this.runtimeState.markInitialized({
|
||||||
featureFlags: bootstrap.featureFlags,
|
localization: bootstrap.localization,
|
||||||
permissions: bootstrap.permissions
|
featureFlags: bootstrap.featureFlags,
|
||||||
|
permissions: bootstrap.permissions
|
||||||
|
});
|
||||||
|
this.initializing = false;
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.themeEngine.applyTheme(bootstrap.theme);
|
||||||
|
this.brandingEngine.applyBranding(bootstrap.branding);
|
||||||
|
this.runtimeState.markInitialized({
|
||||||
|
localization: bootstrap.localization,
|
||||||
|
featureFlags: bootstrap.featureFlags,
|
||||||
|
permissions: bootstrap.permissions
|
||||||
|
});
|
||||||
|
this.initializing = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.initializing = false;
|
|
||||||
},
|
},
|
||||||
error: () => {
|
error: () => {
|
||||||
this.initializing = false;
|
this.initializing = false;
|
||||||
|
|||||||
@@ -1,16 +1,59 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Observable, map, of } from 'rxjs';
|
||||||
|
import { catchError } from 'rxjs/operators';
|
||||||
import { HeroWidgetComponent, FooterNavigationWidgetComponent, ProductCarouselWidgetComponent } from '../ui';
|
import { HeroWidgetComponent, FooterNavigationWidgetComponent, ProductCarouselWidgetComponent } from '../ui';
|
||||||
|
import { RegisteredWidget } from '../contracts/widget-component.contract';
|
||||||
import { WidgetRegistryService } from './widget-registry.service';
|
import { WidgetRegistryService } from './widget-registry.service';
|
||||||
|
|
||||||
|
interface WidgetManifestItem {
|
||||||
|
type: string;
|
||||||
|
version: string;
|
||||||
|
componentKey: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WidgetManifest {
|
||||||
|
widgets: WidgetManifestItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const APPROVED_WIDGET_COMPONENTS: Record<string, RegisteredWidget['component']> = {
|
||||||
|
'hero': HeroWidgetComponent,
|
||||||
|
'footer-navigation': FooterNavigationWidgetComponent,
|
||||||
|
'product-carousel': ProductCarouselWidgetComponent
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class WidgetRegistryBootstrapService {
|
export class WidgetRegistryBootstrapService {
|
||||||
constructor(private readonly registry: WidgetRegistryService) {}
|
private readonly manifestUrl = '/assets/mock/bootstrap/widget-manifest.json';
|
||||||
|
|
||||||
registerDefaults(): void {
|
constructor(
|
||||||
this.registry.registerMany([
|
private readonly registry: WidgetRegistryService,
|
||||||
{ type: 'hero', version: '1.0.0', component: HeroWidgetComponent },
|
private readonly http: HttpClient
|
||||||
{ type: 'footer-navigation', version: '1.0.0', component: FooterNavigationWidgetComponent },
|
) {}
|
||||||
{ type: 'product-carousel', version: '1.0.0', component: ProductCarouselWidgetComponent }
|
|
||||||
]);
|
registerFromManifest(): Observable<void> {
|
||||||
|
return this.http.get<WidgetManifest>(this.manifestUrl).pipe(
|
||||||
|
map((manifest) => {
|
||||||
|
const widgets = (manifest.widgets ?? [])
|
||||||
|
.filter((item) => item.enabled !== false)
|
||||||
|
.map((item): RegisteredWidget | null => {
|
||||||
|
const component = APPROVED_WIDGET_COMPONENTS[item.componentKey];
|
||||||
|
if (!component) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: item.type,
|
||||||
|
version: item.version,
|
||||||
|
component
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter((item): item is RegisteredWidget => item !== null);
|
||||||
|
|
||||||
|
this.registry.registerMany(widgets);
|
||||||
|
}),
|
||||||
|
catchError(() => of(undefined))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
src/assets/mock/bootstrap/widget-manifest.json
Normal file
22
src/assets/mock/bootstrap/widget-manifest.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"widgets": [
|
||||||
|
{
|
||||||
|
"type": "hero",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"componentKey": "hero",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "footer-navigation",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"componentKey": "footer-navigation",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "product-carousel",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"componentKey": "product-carousel",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user