refactor: finalize bootstrap-driven layout and widget runtime
This commit is contained in:
@@ -1,27 +1,36 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable, catchError, map, of, shareReplay } from 'rxjs';
|
||||
import { Observable, catchError, map, of, shareReplay, switchMap, take } from 'rxjs';
|
||||
import { WidgetManifestEntry, WidgetManifestFile } from '../contracts/widget-manifest.contract';
|
||||
import { ConfigService } from '../../core/config/config.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class WidgetManifestService {
|
||||
private readonly manifestUrl = '/assets/mock/bootstrap/widget-manifest.json';
|
||||
private manifest$?: Observable<WidgetManifestFile>;
|
||||
private readonly fallbackManifestUrl = '/assets/mock/bootstrap/widget-manifest.json';
|
||||
private readonly manifestByUrl = new Map<string, Observable<WidgetManifestFile>>();
|
||||
|
||||
constructor(private readonly http: HttpClient) {}
|
||||
constructor(
|
||||
private readonly http: HttpClient,
|
||||
private readonly configService: ConfigService
|
||||
) {}
|
||||
|
||||
getManifest(): Observable<WidgetManifestFile> {
|
||||
if (!this.manifest$) {
|
||||
this.manifest$ = this.http.get<WidgetManifestFile>(this.manifestUrl).pipe(
|
||||
shareReplay({ bufferSize: 1, refCount: true }),
|
||||
catchError(() => {
|
||||
this.manifest$ = undefined;
|
||||
return of({ widgets: [] });
|
||||
})
|
||||
);
|
||||
}
|
||||
return this.resolveManifestUrl().pipe(
|
||||
switchMap((manifestUrl) => {
|
||||
const cached = this.manifestByUrl.get(manifestUrl);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
return this.manifest$;
|
||||
const manifest$ = this.http.get<WidgetManifestFile>(manifestUrl).pipe(
|
||||
shareReplay({ bufferSize: 1, refCount: true }),
|
||||
catchError(() => of({ widgets: [] }))
|
||||
);
|
||||
|
||||
this.manifestByUrl.set(manifestUrl, manifest$);
|
||||
return manifest$;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getWidgets(): Observable<WidgetManifestEntry[]> {
|
||||
@@ -31,4 +40,17 @@ export class WidgetManifestService {
|
||||
getWidget(type: string): Observable<WidgetManifestEntry | undefined> {
|
||||
return this.getWidgets().pipe(map((widgets) => widgets.find((widget) => widget.type === type)));
|
||||
}
|
||||
|
||||
private resolveManifestUrl(): Observable<string> {
|
||||
const snapshotUrl = this.configService.getBootstrapSnapshot()?.widgetRegistry?.manifestUrl;
|
||||
if (snapshotUrl) {
|
||||
return of(snapshotUrl);
|
||||
}
|
||||
|
||||
return this.configService.loadBootstrap().pipe(
|
||||
take(1),
|
||||
map((bootstrap) => bootstrap.widgetRegistry?.manifestUrl || this.fallbackManifestUrl),
|
||||
catchError(() => of(this.fallbackManifestUrl))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,10 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable, map, of } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { CategoriesWidgetComponent, FooterNavigationWidgetComponent, HeroWidgetComponent, ProductCarouselWidgetComponent } from '../ui';
|
||||
import { RegisteredWidget } from '../contracts/widget-component.contract';
|
||||
import { WidgetRegistryService } from './widget-registry.service';
|
||||
|
||||
interface WidgetManifestItem {
|
||||
type: string;
|
||||
version: string;
|
||||
componentKey: string;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
interface WidgetManifest {
|
||||
widgets: WidgetManifestItem[];
|
||||
}
|
||||
import { WidgetManifestService } from './widget-manifest.service';
|
||||
|
||||
const APPROVED_WIDGET_COMPONENTS: Record<string, RegisteredWidget['component']> = {
|
||||
'hero': HeroWidgetComponent,
|
||||
@@ -28,17 +17,15 @@ const APPROVED_WIDGET_COMPONENTS: Record<string, RegisteredWidget['component']>
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class WidgetRegistryBootstrapService {
|
||||
private readonly manifestUrl = '/assets/mock/bootstrap/widget-manifest.json';
|
||||
|
||||
constructor(
|
||||
private readonly registry: WidgetRegistryService,
|
||||
private readonly http: HttpClient
|
||||
private readonly widgetManifest: WidgetManifestService
|
||||
) {}
|
||||
|
||||
registerFromManifest(): Observable<void> {
|
||||
return this.http.get<WidgetManifest>(this.manifestUrl).pipe(
|
||||
map((manifest) => {
|
||||
const widgets = (manifest.widgets ?? [])
|
||||
return this.widgetManifest.getWidgets().pipe(
|
||||
map((manifestWidgets) => {
|
||||
const widgets = manifestWidgets
|
||||
.filter((item) => item.enabled !== false)
|
||||
.map((item): RegisteredWidget | null => {
|
||||
const component = APPROVED_WIDGET_COMPONENTS[item.componentKey];
|
||||
|
||||
Reference in New Issue
Block a user