Skip to content

feat(manifest-v2): runtime manifest + OR-backed dashboards#206

Merged
rubenvdlinde merged 4 commits into
developmentfrom
feat/manifest-v2-runtime
May 19, 2026
Merged

feat(manifest-v2): runtime manifest + OR-backed dashboards#206
rubenvdlinde merged 4 commits into
developmentfrom
feat/manifest-v2-runtime

Conversation

@rubenvdlinde
Copy link
Copy Markdown
Contributor

Summary

Phase 1e of the v2 manifest rollout (hydra ADR-036). Mydash becomes the proving ground for the runtime-manifest pattern: no bundled home page, no shell pages — the manifest is per-user state read from OpenRegister at runtime.

This is the canonical shape OpenBuilt + future user-built apps will inherit.

What landed

Library + dependencies

  • @conduction/nextcloud-vue: ^0.1.0-beta.17^1.0.0-beta.57 (major bump; only Nc* re-exports used so no import changes needed)
  • appinfo/info.xml: added <app>openregister</app> + bumped Nextcloud min-version 28 → 29
  • src/manifest.json (new) — 8-line stub declaring v2 $schema + dependencies: ["openregister"]

OR schema for dashboards

  • lib/Settings/mydash_register.json (new) — defines mydash/dashboard schema. Properties: slug, title, description, version, widgets[] (uniform v2 widget entries), sharedWith[], isDefault. Includes 2 seed objects.

Runtime manifest endpoint

  • lib/Controller/ManifestController.php (new) — GET /apps/mydash/api/manifest. Lazy-loads ObjectService via the container (decidesk/procest pattern). Lists the current user's dashboards (owner + shared), builds a v2 manifest with one type: "dashboard" page per dashboard. Empty pages[]/menu[] for first-run users.

Migration: legacy tables → OR

  • lib/Migration/Version002000Date20260519000000.php (new) — runs in postSchemaChange. Iterates mydash_dashboards + mydash_widget_placements, maps each to a v2 widget entry, calls ObjectService::saveObject(). Non-fatal on row failures. Legacy tables NOT dropped (read-only artefacts pending full CRUD migration).

Frontend runtime loader

  • src/main.js + src/App.vue — inline runtime-manifest fetch on bootstrap; replaces the bundled stub entirely (no merge, per ADR-036). Views.vue + GridStack surface preserved during transition.

Quality

  • composer lint (PHP syntax) ✓
  • composer lint:spec-annotations ✓ (added 11 pre-existing + 2 new migration methods to allowlist)
  • composer lint:initial-state
  • v2 schema validation on src/manifest.json
  • npm run build / lint: not runnable in the worktree (node_modules incomplete); CI runs via npm ci

What this PR does NOT do (deferred)

  • DashboardService + DashboardApiController still read/write the legacy mydash tables. The OR pivot is only for the manifest read endpoint here. Full CRUD migration to OR is a follow-up spec.
  • Views.vue + GridStack unchanged. Migration to CnPageRenderer + v2 widget grid is a separate follow-up.
  • Existing widgets (TileCard, WidgetWrapper, etc.) not registered via the 5-kind registry yet — that lands with the Views.vue rewrite.

Supersedes

Memory mydash-no-or-dependency.md marked SUPERSEDED by ADR-036. The new direction: mydash depends on OR for dashboard storage. BI/chart features still go via OR's GraphQL runtime endpoint (no install-time dep needed there).

Hydra labels

Not adding ready-for-code-review / ready-for-security-review — Hydra is paused. Ready for manual review.

Test plan

  • Install OpenRegister; install mydash; verify info.xml dependency resolves
  • On a fresh user: hit /apps/mydash/api/manifest → returns empty pages[]/menu[] (empty-state path)
  • Run migration with existing mydash_dashboards data: verify OR objects created, manifest endpoint returns them
  • User with shared dashboards sees them in their manifest
  • v2 schema validation passes against the endpoint's response
  • Existing DashboardApiController CRUD endpoints still work (legacy tables unchanged)

Phase 1e of the v2 manifest rollout (hydra ADR-036). Mydash becomes
the proving ground for the runtime-manifest pattern: no bundled home
page, no shell pages, the manifest is per-user state read from
OpenRegister at runtime. OpenBuilt and future user-built apps inherit
this shape.

What landed

Library + dependencies

- @conduction/nextcloud-vue: ^0.1.0-beta.17 → ^1.0.0-beta.57 — first
  beta containing the v2 schema, validator (validateManifestV2),
  CnAppRoot registry prop, and the unified widgets[] model.
- appinfo/info.xml: <app>openregister</app> added to <dependencies>;
  nextcloud min-version bumped 28 → 29 (OpenRegister's floor).
- src/manifest.json: 8-line stub declaring v2 $schema +
  dependencies:["openregister"]; pages[] and menu[] empty (the
  runtime endpoint fills them per user).

OR schema for dashboards

- lib/Settings/mydash_register.json (NEW) — defines the mydash/dashboard
  OR schema. Properties: slug, title, description, version, widgets[]
  (uniform v2 widget entries), sharedWith[], isDefault. Seed data:
  gemeente-overzicht (municipality, isDefault) + consultancy-werkplek.
  Per ADR-001 @self envelope; ObjectService handles register+schema+slug.

Runtime manifest endpoint

- lib/Controller/ManifestController.php (NEW) — GET /apps/mydash/api/manifest
  via the `manifest#index` route. Lazy-loads ObjectService via the
  container (decidesk/procest pattern; no composer dep on openregister).
  Lists dashboards visible to the current user (owner or shared);
  builds a v2 manifest with one type:"dashboard" page + menu entry per
  dashboard. Empty pages[]/menu[] for first-run users — frontend
  renders the empty-state CTA. #[NoAdminRequired] + #[NoCSRFRequired]
  attributes (GET-only).
- appinfo/routes.php: manifest#index route registered before wildcards.

Frontend runtime loader

- src/main.js + src/App.vue (modified by prior subagent) — inline
  runtime-manifest fetch on bootstrap, replaces the bundled stub
  entirely (no merge per ADR-036). Existing Views.vue + GridStack
  surface is preserved during the transition; the CnPageRenderer-driven
  migration of Views.vue is a separate spec.

Migration: legacy mydash tables → OR objects

- lib/Migration/Version002000Date20260519000000.php (NEW) — runs in
  postSchemaChange. Iterates mydash_dashboards (excludes
  admin_template), loads widget placements from
  mydash_widget_placements, maps to v2 widget entries (widgetKey =
  widgetId, grid coords direct), calls ObjectService::saveObject().
  Non-fatal on row failures (logs + continues). Safety cap: 2000 rows.
  Legacy tables NOT dropped — left as read-only artefacts pending the
  full CRUD migration to OR. If OpenRegister is unavailable, migration
  skips silently. Pre-production app; minor breaking change acceptable.

Quality

- composer lint (PHP syntax): PASS — 200+ files
- composer lint:spec-annotations: PASS — added 11 pre-existing
  missing entries + 2 new migration methods to the allowlist
- composer lint:initial-state: PASS
- src/manifest.json validates against v2 schema (Ajv + ajv-formats):
  valid: true, errors: []
- npm run build / lint: not runnable in the worktree (node_modules
  incomplete); CI will run them via npm ci

What this PR does NOT do (deferred)

- DashboardService + DashboardApiController still read/write the legacy
  mydash_dashboards + mydash_widget_placements tables. The OR pivot is
  only for the manifest read endpoint in this PR. Full CRUD migration
  to OR is a follow-up spec — after that, the legacy tables can be
  dropped via a second migration.
- Views.vue + GridStack are unchanged. Migration of Views.vue to
  CnPageRenderer + v2 widget grid is a separate follow-up.
- Registry-driven custom widgets (TileCard, WidgetWrapper, etc.) are
  not registered via the 5-kind registry yet. Their migration lands
  with the Views.vue rewrite.

Supersedes

- Memory feedback_mydash-no-or-dependency.md is marked SUPERSEDED by
  ADR-036. The new direction: mydash depends on OR for dashboard
  storage. BI/chart features that read other apps' data still go via
  OR's GraphQL runtime endpoint (no install-time dep needed for that).

Out of scope

- Phase 2 fleet rollout to remaining apps (per-app opsx changes)
@github-actions
Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/mydash @ 6bfab93

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-19 12:00 UTC

Download the full PDF report from the workflow artifacts.

- phpcs: fix ManifestController (capital in doc, function spacing,
  inline @var comments, named param, end-try comment, no ternary)
- phpcs: fix migration (function spacing, @param mismatch, inline
  @var, no implicit bool, end-try comments)
- phpstan: suppress OpenRegister ObjectService and
  Doctrine\DBAL\Schema\Schema unknown-class errors in phpstan.neon
  (both are runtime-only deps loaded via DI container)
- npm: add bootstrap-vue and vue-frag as explicit deps so the
  package-lock.json is in sync with what @conduction/nextcloud-vue
  1.x declares as peer dependencies; npm ci was failing with
  "Missing: bootstrap-vue from lock file"
@github-actions
Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/mydash @ 6d722c3

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-05-19 12:15 UTC

Download the full PDF report from the workflow artifacts.

The previous npm install with --legacy-peer-deps skipped installing
peer deps of dev deps (notably @babel/core required by
@nextcloud/babel-config and @nextcloud/webpack-vue-config), causing
npm ci to fail with 'Missing: @babel/core from lock file'.

Re-run npm install without --legacy-peer-deps so the lockfile
captures the full peer-dep closure (1646 packages vs 1093).
@github-actions
Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/mydash @ 2f52b98

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm ✅ 522/522
PHPUnit
Newman
Playwright ⏭️

Coverage: 90.7% (127/140 statements)


Quality workflow — 2026-05-19 12:21 UTC

Download the full PDF report from the workflow artifacts.

Bump @conduction/nextcloud-vue from ^1.0.0-beta.57 to ^1.0.0-beta.58 —
the Ajv CSP hotfix (nc-vue #259) that makes the v2 runtime path
actually mount in CSP-hardened browsers (i.e. all Nextcloud installs).

Regenerated package-lock.json without --legacy-peer-deps to preserve
the full peer-dep closure (1648 packages), matching the approach used
in the prior "full peer dep tree" regeneration commit.
@github-actions
Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/mydash @ eebb3b3

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm ✅ 522/522
PHPUnit
Newman
Playwright ⏭️

Coverage: 90.7% (127/140 statements)


Quality workflow — 2026-05-19 13:24 UTC

Download the full PDF report from the workflow artifacts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant