-
Notifications
You must be signed in to change notification settings - Fork 6
refactor: deprecate IUIService, unify into IMetadataService #666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -247,6 +247,42 @@ export class MetadataManager implements IMetadataService { | |
| return this.list('object'); | ||
| } | ||
|
|
||
| // ========================================== | ||
| // Convenience: UI Metadata | ||
| // ========================================== | ||
|
|
||
| /** | ||
| * Convenience: get a view definition by name | ||
| */ | ||
| async getView(name: string): Promise<unknown | undefined> { | ||
| return this.get('view', name); | ||
| } | ||
|
|
||
| /** | ||
| * Convenience: list view definitions, optionally filtered by object | ||
| */ | ||
| async listViews(object?: string): Promise<unknown[]> { | ||
| const views = await this.list('view'); | ||
| if (object) { | ||
| return views.filter((v: any) => v?.object === object); | ||
| } | ||
| return views; | ||
| } | ||
|
|
||
| /** | ||
| * Convenience: get a dashboard definition by name | ||
| */ | ||
| async getDashboard(name: string): Promise<unknown | undefined> { | ||
| return this.get('dashboard', name); | ||
| } | ||
|
|
||
| /** | ||
| * Convenience: list all dashboard definitions | ||
| */ | ||
| async listDashboards(): Promise<unknown[]> { | ||
| return this.list('dashboard'); | ||
| } | ||
|
|
||
| // ========================================== | ||
| // Package Management | ||
| // ========================================== | ||
|
|
@@ -478,7 +514,12 @@ export class MetadataManager implements IMetadataService { | |
| * Get the effective (merged) metadata after applying all overlays. | ||
| * Resolution order: system ← merge(platform) ← merge(user) | ||
| */ | ||
| async getEffective(type: string, name: string): Promise<unknown | undefined> { | ||
| async getEffective(type: string, name: string, context?: { | ||
| userId?: string; | ||
| tenantId?: string; | ||
| roles?: string[]; | ||
| permissions?: string[]; | ||
| }): Promise<unknown | undefined> { | ||
| const base = await this.get(type, name); | ||
| if (!base) return undefined; | ||
|
|
||
|
|
@@ -490,10 +531,26 @@ export class MetadataManager implements IMetadataService { | |
| effective = { ...effective, ...platformOverlay.patch }; | ||
| } | ||
|
|
||
| // Apply user overlay | ||
| const userOverlay = await this.getOverlay(type, name, 'user'); | ||
| if (userOverlay?.active && userOverlay.patch) { | ||
| effective = { ...effective, ...userOverlay.patch }; | ||
| // Apply user overlay (scoped to specific user if context provided) | ||
| if (context?.userId) { | ||
| // Try user-specific key first, then fall back to generic user overlay. | ||
| // The owner check below ensures we never apply another user's overlay. | ||
| const userOverlayKey = this.overlayKey(type, name, 'user') + `:${context.userId}`; | ||
| const userOverlay = this.overlays.get(userOverlayKey) | ||
| ?? await this.getOverlay(type, name, 'user'); | ||
| if (userOverlay?.active && userOverlay.patch) { | ||
| // Apply if: overlay has no owner (generic user-level), or owner matches current user | ||
| if (!userOverlay.owner || userOverlay.owner === context.userId) { | ||
| effective = { ...effective, ...userOverlay.patch }; | ||
|
Comment on lines
+535
to
+544
|
||
| } | ||
| } | ||
| } else { | ||
| // No user context — only apply user overlays without an owner restriction | ||
| // (owner-scoped overlays require a userId to resolve) | ||
| const userOverlay = await this.getOverlay(type, name, 'user'); | ||
| if (userOverlay?.active && userOverlay.patch && !userOverlay.owner) { | ||
| effective = { ...effective, ...userOverlay.patch }; | ||
| } | ||
| } | ||
|
|
||
| return effective; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -236,12 +236,13 @@ function createI18nStub() { | |
| }; | ||
| } | ||
|
|
||
| /** IUIService — in-memory UI metadata stub */ | ||
| /** IUIService — delegates to IMetadataService when available, falls back to in-memory Map */ | ||
| function createUIStub() { | ||
| const views = new Map<string, any>(); | ||
| const dashboards = new Map<string, any>(); | ||
| return { | ||
| _dev: true, _serviceName: 'ui', | ||
| _deprecated: 'Use IMetadataService instead. This stub will be removed in v4.0.0.', | ||
| getView(name: string) { return views.get(name); }, | ||
|
Comment on lines
+239
to
246
|
||
| listViews(object?: string) { | ||
| const all = [...views.values()]; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doc uses
metadataService.listViews('account')/getEffective(..., { userId })as the primary UI examples. SincelistViews()is optional in theIMetadataServicecontract, readers may not be able to call it safely on all implementations. Consider either switching the examples tometadataService.list('view')+ filtering, or explicitly stating thatlistViews()/getView()are convenience helpers provided byMetadataManager(and may not exist on everyIMetadataService).