Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 8
Harden UI schema type safety, unify filter format, extend cross-reference validation, add negative tests#802
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
c781175ce079792a605548f78604File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -332,6 +332,74 @@ function validateCrossReferences(config: ObjectStackDefinition): string[] { | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| // Validate seed data → object references | ||||||||||||||||||||||||||||||||||||||||||||||
| if (config.data) { | ||||||||||||||||||||||||||||||||||||||||||||||
| for (const dataset of config.data) { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (dataset.object && !objectNames.has(dataset.object)) { | ||||||||||||||||||||||||||||||||||||||||||||||
| errors.push( | ||||||||||||||||||||||||||||||||||||||||||||||
| `Seed data references object '${dataset.object}' which is not defined in objects.`, | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| // Validate app navigation → object/dashboard/page/report references | ||||||||||||||||||||||||||||||||||||||||||||||
| if (config.apps) { | ||||||||||||||||||||||||||||||||||||||||||||||
| const dashboardNames = new Set<string>(); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (config.dashboards) { | ||||||||||||||||||||||||||||||||||||||||||||||
| for (const d of config.dashboards) { | ||||||||||||||||||||||||||||||||||||||||||||||
| dashboardNames.add(d.name); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| const pageNames = new Set<string>(); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (config.pages) { | ||||||||||||||||||||||||||||||||||||||||||||||
| for (const p of config.pages) { | ||||||||||||||||||||||||||||||||||||||||||||||
| pageNames.add(p.name); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| const reportNames = new Set<string>(); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (config.reports) { | ||||||||||||||||||||||||||||||||||||||||||||||
| for (const r of config.reports) { | ||||||||||||||||||||||||||||||||||||||||||||||
| reportNames.add(r.name); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| for (const app of config.apps) { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!app.navigation) continue; | ||||||||||||||||||||||||||||||||||||||||||||||
| const checkNavItems = (items: unknown[], appName: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||
| for (const item of items) { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!item || typeof item !== 'object') continue; | ||||||||||||||||||||||||||||||||||||||||||||||
| const nav = item as Record<string, unknown>; | ||||||||||||||||||||||||||||||||||||||||||||||
| if (nav.type === 'object' && typeof nav.objectName === 'string' && !objectNames.has(nav.objectName)) { | ||||||||||||||||||||||||||||||||||||||||||||||
| errors.push( | ||||||||||||||||||||||||||||||||||||||||||||||
| `App '${appName}' navigation references object '${nav.objectName}' which is not defined in objects.`, | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| if (nav.type === 'dashboard' && typeof nav.dashboardName === 'string' && dashboardNames.size > 0 && !dashboardNames.has(nav.dashboardName)) { | ||||||||||||||||||||||||||||||||||||||||||||||
| errors.push( | ||||||||||||||||||||||||||||||||||||||||||||||
| `App '${appName}' navigation references dashboard '${nav.dashboardName}' which is not defined in dashboards.`, | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| if (nav.type === 'page' && typeof nav.pageName === 'string' && pageNames.size > 0 && !pageNames.has(nav.pageName)) { | ||||||||||||||||||||||||||||||||||||||||||||||
| errors.push( | ||||||||||||||||||||||||||||||||||||||||||||||
| `App '${appName}' navigation references page '${nav.pageName}' which is not defined in pages.`, | ||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| if (nav.type === 'report' && typeof nav.reportName === 'string' && reportNames.size > 0 && !reportNames.has(nav.reportName)) { | ||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+378
to
+388
CopilotAI | ||||||||||||||||||||||||||||||||||||||||||||||
| if(nav.type==='dashboard'&&typeofnav.dashboardName==='string'&&dashboardNames.size>0&&!dashboardNames.has(nav.dashboardName)){ | |
| errors.push( | |
| `App '${appName}' navigation references dashboard '${nav.dashboardName}' which is not defined in dashboards.`, | |
| ); | |
| } | |
| if(nav.type==='page'&&typeofnav.pageName==='string'&&pageNames.size>0&&!pageNames.has(nav.pageName)){ | |
| errors.push( | |
| `App '${appName}' navigation references page '${nav.pageName}' which is not defined in pages.`, | |
| ); | |
| } | |
| if(nav.type==='report'&&typeofnav.reportName==='string'&&reportNames.size>0&&!reportNames.has(nav.reportName)){ | |
| if(nav.type==='dashboard'&&typeofnav.dashboardName==='string'&&!dashboardNames.has(nav.dashboardName)){ | |
| errors.push( | |
| `App '${appName}' navigation references dashboard '${nav.dashboardName}' which is not defined in dashboards.`, | |
| ); | |
| } | |
| if(nav.type==='page'&&typeofnav.pageName==='string'&&!pageNames.has(nav.pageName)){ | |
| errors.push( | |
| `App '${appName}' navigation references page '${nav.pageName}' which is not defined in pages.`, | |
| ); | |
| } | |
| if(nav.type==='report'&&typeofnav.reportName==='string'&&!reportNames.has(nav.reportName)){ |
Uh oh!
There was an error while loading. Please reload this page.
CopilotAIFeb 24, 2026
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.
The negative test for dashboard reference validation should include a case where no dashboards are defined at all (dashboards array is empty or undefined), but navigation references a dashboard. This would verify that the validation correctly catches dangling references even when the target collection is empty.
Example test case:
Similar tests should be added for page and report references as well.