Sprint 8: add widget manifest and data source engine

This commit is contained in:
sdarbinyan
2026-07-05 02:08:15 +04:00
parent 91d9444875
commit c3d1153f0e
20 changed files with 867 additions and 375 deletions

View File

@@ -1,4 +1,6 @@
import { Type } from '@angular/core';
import { WidgetConfig } from '../../shared/models/config';
import { SectionConfig } from '../../shared/models/config';
export interface WidgetRenderNode {
id: string;
@@ -9,3 +11,11 @@ export interface WidgetRenderNode {
visible?: boolean;
source: WidgetConfig;
}
export interface ResolvedWidgetRenderNode {
type: string;
version: string;
component: Type<unknown>;
section: SectionConfig;
data: unknown;
}

View File

@@ -1,23 +1,77 @@
import { Injectable } from '@angular/core';
import { WidgetConfig } from '../../shared/models/config';
import { WidgetRegistryService } from '../../widgets/registry/widget-registry.service';
import { Observable, map, shareReplay } from 'rxjs';
import { WidgetConfig, SectionConfig } from '../../shared/models/config';
import { ConfigService } from '../../core/config/config.service';
import { RuntimeDiagnosticsService } from '../../core/runtime/runtime-diagnostics.service';
import { UnknownWidgetComponent } from '../../widgets/ui';
import { ResolvedWidget } from '../../widgets/contracts/widget-component.contract';
import { DataSourceResolverService } from '../../widgets/resolvers/data-source-resolver.service';
import { WidgetRegistryService } from '../../widgets/registry/widget-registry.service';
@Injectable({ providedIn: 'root' })
export class WidgetHostService {
constructor(private readonly registry: WidgetRegistryService) {}
private readonly resolvedWidgets = new Map<string, Observable<ResolvedWidget>>();
private readonly loggedUnknownWidgets = new Set<string>();
resolveWidget(widget: WidgetConfig): ResolvedWidget | null {
const registered = this.registry.resolve(widget.type, widget.version);
if (!registered) {
return null;
constructor(
private readonly registry: WidgetRegistryService,
private readonly dataSourceResolver: DataSourceResolverService,
private readonly diagnostics: RuntimeDiagnosticsService,
private readonly configService: ConfigService
) {}
resolveWidget(widget: WidgetConfig, section: SectionConfig, pageId: string): Observable<ResolvedWidget> {
const cacheKey = `${pageId}:${section.id}:${widget.id}`;
const cached = this.resolvedWidgets.get(cacheKey);
if (cached) {
return cached;
}
return {
type: registered.type,
version: registered.version,
component: registered.component,
props: widget.props ?? {}
};
const registered = this.registry.resolve(widget.type, widget.version);
const resolved$ = this.dataSourceResolver.resolve(widget, section).pipe(
map((data) => {
if (!registered) {
this.logUnknownWidget(pageId, section.id, widget);
return {
type: widget.type,
version: widget.version,
component: UnknownWidgetComponent,
section,
data
};
}
return {
type: registered.type,
version: registered.version,
component: registered.component,
section,
data
};
}),
shareReplay({ bufferSize: 1, refCount: true })
);
this.resolvedWidgets.set(cacheKey, resolved$);
return resolved$;
}
private logUnknownWidget(pageId: string, sectionId: string, widget: WidgetConfig): void {
const diagnosticKey = `${pageId}:${sectionId}:${widget.id}`;
if (this.loggedUnknownWidgets.has(diagnosticKey)) {
return;
}
this.loggedUnknownWidgets.add(diagnosticKey);
const tenantId = this.configService.getBootstrapSnapshot()?.tenant.id ?? 'unknown';
this.diagnostics.logUnknownWidget({
tenant: tenantId,
page: pageId,
section: sectionId,
widget: `${widget.type}@${widget.version}`,
reason: 'not_registered_in_widget_registry'
});
}
}