Skip to content

fix: migrate docudesk to @conduction/nextcloud-vue useObjectStore#130

Merged
rubenvdlinde merged 1 commit into
developmentfrom
feature/docudesk-store-migration
May 23, 2026
Merged

fix: migrate docudesk to @conduction/nextcloud-vue useObjectStore#130
rubenvdlinde merged 1 commit into
developmentfrom
feature/docudesk-store-migration

Conversation

@rubenvdlinde
Copy link
Copy Markdown
Contributor

Summary

Phase 1 of the @conduction/nextcloud-vue store migration for docudesk. Mirrors the side-by-side pattern from zaakafhandelapp PR #190 and the project-memory rule feedback_store-pattern.md: adopt the lib's useObjectStore alongside docudesk's existing eight legacy Pinia stores, register the seven OR-backed object types from lib/Settings/docudesk_register.json against the lib store at boot, and leave every legacy store + Vue consumer untouched.

Companion fleet migrations just merged: decidesk #163 (thin-wrap), softwarecatalog #219 (spec follow-up), procest #321 (call-site), zaakafhandelapp #190 (side-by-side). Docudesk is the last app in the fleet to adopt.

Why side-by-side and not full / thin-wrap

Docudesk's eight legacy stores (anonymization, batchAnonymization, consent, folderAnonymization, navigation, settings, signing, template) talk to docudesk-specific REST controllers (/apps/docudesk/api/{anonymization,consents,signing,templates,settings}), not to the OR canonical ${baseUrl}/${register}/${schema}/${id} shape. Most expose workflow-specific surface (file queues, batch lifecycle, signing audit trails, version/lock management) with no lib equivalent. A full migration would require rewriting each legacy controller to delegate to OR — multi-week effort, out of scope.

A thin-wrap shim has no caller today (no Vue file imports useObjectStore); adding one would create an unused abstraction.

Side-by-side makes the lib store available for any future manifest page, lib component, or sub-resource plugin while keeping the legacy stores running unchanged.

Object types registered

From lib/Settings/docudesk_register.json:

Slug Schema Register
consent publicationConsent consent
signing-request signingRequest signing
signer-record signerRecord signing
signing-audit-entry signingAuditEntry signing
template template templates
correspondence correspondence document
huisstijl huisstijl document

Files changed

  • openspec/changes/docudesk-store-migration/{proposal,design,tasks}.md + specs/docudesk-store-migration/spec.md — full openspec change.
  • src/store/store.js — additive: createObjectStore('docudesk-objects') + useObjectStore re-export, initializeStores() upgraded to register the seven OR types after the existing settings fetch (idempotency-guarded).
  • src/main.js — single fire-and-forget initializeStores() call (App.vue's existing await initializeStores() continues to work via the helper's idempotency guard).

No legacy store renamed or deleted. No legacy Vue consumer touched. Pinia store id 'docudesk-objects' (distinct from lib default 'conduction-objects' and from every legacy id) — no collision risk.

Lib gap (deferred follow-up)

liveUpdatesPlugin is not exported by @conduction/nextcloud-vue@0.1.0-beta.8 (the version pinned in docudesk's lockfile). beta.8 only exports auditTrailsPlugin, relationsPlugin, filesPlugin, lifecyclePlugin, selectionPlugin, searchPlugin. The plugin lives on the lib's beta branch but has not been released.

Wiring is deferred to a follow-up that bumps the lib version + adds { plugins: [liveUpdatesPlugin()] } to the createObjectStore call. The file-level comment in src/store/store.js documents the exact line to update on bump.

Validation

  • npm ci — succeeds (1748 packages installed).
  • npm run lint — clean (zero errors, zero warnings on touched files).
  • npx webpack --mode production — not run (out of scope for this PR; pre-existing build issues are tracked separately).
  • Manual app-boot verification — not run; manual step in test plan.

Test plan

  • Open the docudesk app in dev — confirm boot proceeds without store-related console errors.
  • Confirm legacy views still render via the docudesk REST controllers (Anonymization, BatchAnonymization, FolderAnonymization, Consent, ConsentDetail, Templates, TemplateDetail, Signing requests).
  • In DevTools, inspect useObjectStore().objectTypes — confirm the seven slugs are present (consent, signing-request, signer-record, signing-audit-entry, template, correspondence, huisstijl).
  • Confirm useObjectStore Pinia store id is 'docudesk-objects' (no collision with lib default 'conduction-objects').

References

@github-actions
Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/docudesk @ 9cc5ecd

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

Coverage: 0% (0/10 statements)


Quality workflow — 2026-05-10 09:10 UTC

Download the full PDF report from the workflow artifacts.

@MWest2020
Copy link
Copy Markdown
Member

Review — fix: migrate docudesk to @conduction/nextcloud-vue useObjectStore

Verdict: 🟢 APPROVE — Zuivere additieve migratie die het side-by-side patroon van zaakafhandelapp #190 correct spiegelt. Alle 7 object-type registraties kloppen met docudesk_register.json. Geen backwards-compat regressies. Eén 🟡 concern over een potentiële race condition bij de idempotency guard die in een follow-up gefixed kan worden.

Wat goed gaat

Uitstekend voorbereid: heldere openspec-change (proposal + design + tasks + spec), alle 7 type-registraties exact gespiegeld uit docudesk_register.json, onderbouwd liveUpdatesPlugin-deferral met file-level comment die de exacte fix-locatie aanwijst, volledige Pinia store-id isolatie ('docudesk-objects' — geen collision met legacy stores of de lib-default), én het SPDX-header correct aan store.js toegevoegd. Het PR-lichaam is exemplarisch in het documenteren van de waarom-keuze (side-by-side vs full vs thin-wrap) en de Phase 2 cutover-triggers.

Findings

1. 🟡 Concern — initializeStores() idempotency guard beschermt niet tegen gelijktijdige aanroepen — src/store/store.js:71

main.js start initializeStores() als fire-and-forget vóór new Vue().$mount(). Vrijwel onmiddellijk daarna roept App.vue's async created() ook await initializeStores() aan. Omdat initialized pas op true wordt gezet nádat alle async stappen zijn afgerond (inclusief settingsStore.fetchSettings()), zijn beide aanroepen gelijktijdig actief: beide passeren de if (initialized) return check en beide sturen een fetch('/index.php/apps/docudesk/api/settings') de lucht in.

Impact: De configure- en registerObjectType-aanroepen zijn idempotent (last-write-wins), dus functioneel werkt het — maar de settings-fetch vuurt dubbel. Marginale netwerkkost, geen correctnessbug.

Suggested fix: Vervang de boolean door een Promise-singleton: let initPromise = null; async function initializeStores() { ... if (initPromise) return initPromise; initPromise = (async () => { ... })(); return initPromise; }. Elke volgende aanroep retourneert dan dezelfde Promise i.p.v. een tweede async keten te starten.

2. 🟡 Concern — liveUpdatesPlugin-deferral claim is moeilijk verifieerbaar — documenteer de geïnstalleerde versie in store.js — src/store/store.js:35

PR-body en file-level comment stellen dat liveUpdatesPlugin niet geëxporteerd wordt door @conduction/nextcloud-vue@0.1.0-beta.8. De GitHub-repo van de lib toont echter alleen v1.0.0-beta.* tags, en src/index.js op tag v1.0.0-beta.8 exporteert liveUpdatesPlugin wél. De versie-discrepantie (package.json claimt ^0.1.0-beta.8, GitHub-tags starten bij v1.0.0-beta.1) maakt het onduidelijk welke versie de lockfile resolved.

Impact: Als het de npm-registry-versie is die de plugin WEL bevat, is de deferral onnodig en kan het al in deze PR worden aangesloten.

Suggested fix: Voeg in het file-level comment de npm-version string toe zoals die in npm list @conduction/nextcloud-vue staat, zodat toekomstige reviewers het deferral-besluit kunnen verifiëren zonder de lockfile te ontcijferen.

3. 🟢 Minor — main.js mist SPDX-header bij substantiële uitbreiding — src/main.js:1

src/store/store.js krijgt een SPDX-License-Identifier: EUPL-1.2 header toegevoegd (correct), maar src/main.js — eveneens gewijzigd in deze PR — krijgt die header niet. Per ADR-014 dienen EUPL-1.2 headers op alle (nieuwe en substantieel uitgebreide) bestanden te staan. main.js bestond al zonder header, maar dit PR voegt +20 regels toe (verdubbelt het bestand), wat het een goed moment maakt de header ook daar te plaatsen.

Impact: Geen merge-blocker, maar consistentie met store.js is wenselijk.

Suggested fix: Voeg bovenaan main.js: // SPDX-License-Identifier: EUPL-1.2 toe.


Geconsolideerde review via /review-pr — Thorough mode.

MWest2020
MWest2020 previously approved these changes May 11, 2026
Copy link
Copy Markdown
Member

@MWest2020 MWest2020 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zie consolidated review comment voor complimenten, findings en suggested fixes.

… side-by-side)

Add the lib's useObjectStore to docudesk's pinia ecosystem alongside
the existing eight legacy docudesk-specific stores. Boot the lib
store with the canonical OpenRegister base URL and register the
seven OR-backed object types declared in
lib/Settings/docudesk_register.json (consent, signing-request,
signer-record, signing-audit-entry, template, correspondence,
huisstijl) under their respective register slugs.

Phase 1 is purely additive:
- src/store/store.js re-exports useObjectStore alongside the
  existing legacy *Store exports; legacy consumers see zero
  behavioural change.
- src/main.js calls initializeStores() before mount using the
  fire-and-forget try/catch pattern (mirrors zaakafhandelapp PR
  #190); App.vue's existing await is preserved via the helper's
  idempotency guard.
- Pinia store id 'docudesk-objects' is distinct from the lib's
  default and from every legacy id — no collision risk.

Manifest pages, CnIndexPage / CnDetailPage form-dialog save paths,
and the lib's sub-resource plugins (audit trails, files, relations,
selection) now bind to a known-shape store, eliminating the decidesk
#162 class of bug for OR-backed code paths.

Lib gap: liveUpdatesPlugin is not yet exported by
@conduction/nextcloud-vue@0.1.0-beta.8 (only auditTrailsPlugin,
relationsPlugin, filesPlugin, lifecyclePlugin, selectionPlugin,
searchPlugin ship in beta.8). The plugin lives on the lib's beta
branch but has not been released. Wiring deferred to a follow-up
once the next lib release exposes it; the file-level comment in
store.js documents the exact line to update on bump.

References:
- Project memory: feedback_store-pattern.md ("Do not use custom stores")
- Decidesk #162: missing fetchObject / live-updates plugin failure
- Decidesk PR #163: full thin-wrap migration template
- zaakafhandelapp PR #190: side-by-side migration template
- procest PR #321 / softwarecatalog PR #219: companion fleet PRs

Spec: openspec/changes/docudesk-store-migration/
@github-actions
Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/docudesk @ f3ece26

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

Quality workflow — 2026-05-23 05:47 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde rubenvdlinde merged commit 0b02303 into development May 23, 2026
19 of 39 checks passed
@rubenvdlinde rubenvdlinde deleted the feature/docudesk-store-migration branch May 23, 2026 05:47
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.

2 participants