fix(register): the portaliq register could never be created on a clean install - #40
Conversation
…n install
lib/Settings/portaliq_register.json declared no components.registers section.
OpenRegister's ImportHandler creates a Register row from that key and nowhere
else on the main/beta lines (ImportHandler.php:1514, the
$data['components']['registers'] loop) -- so a clean install created 9 schemas
and ZERO registers, and then skipped all 5 seed objects, whose @self.register
is resolved through the registersMap that section populates.
Reproduced on a disposable Nextcloud 34 + OpenRegister main (0.2.19):
occ app:enable portaliq -> "portaliq 0.1.8 enabled"
GET /api/registers -> HTTP 200, 0 registers
GET /api/schemas -> HTTP 200, 9 schemas
GET /api/objects/portaliq/portalPage -> HTTP 404
{"message":"Register not found: 'portaliq'"}
The failure is silent: the import itself reports success, and every object
route 404s with a message that reads like a routing problem rather than a
missing register.
After this change, on the same clean install (register absent BEFORE enable,
which is the evidence the fix stands on its own):
register 'portaliq' present AFTER enable: True
id=1 title='Portaliq Register' version=0.12.0 schemas=9
9/9 expected schema slugs present AND bound into the register
9/9 GET /api/objects/portaliq/<schema> -> HTTP 200
The slug is derived, not invented: lib/ and the seed objects address
`objects/portaliq/<schema>` and all 5 seed objects carry
`@self.register: "portaliq"`.
The schema list is the EXACT union of the register document's schemas, by
SLUG. That distinction is load-bearing: ImportHandler keys its schemasMap by
$schema->getSlug() (ImportHandler.php:1397/1477), not by the components.schemas
object key. For portaliq the two coincide; in decidesk, openbuild and scholiq
they do not, and a register listing the keys binds ZERO schemas while still
looking correctly declared.
info.version 0.11.0 -> 0.12.0 so OpenRegister's version-gated importFromApp
actually re-imports; a frozen version would never pick up later schemas.
appinfo/info.xml already lists InitializeSettings under <install> as well as
<post-migration>, so portaliq does NOT have the second, independent gap where
a register import is declared post-migration-only and therefore never runs on
a fresh install (Installer::installAppLastSteps() guards both the pre- and
post-migration blocks with `if ($previousVersion !== '')`).
Quality Report — ConductionNL/portaliq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ❌ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 871/871 | |||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ |
Quality workflow — 2026-08-04 20:00 UTC
Download the full PDF report from the workflow artifacts.
…assertion PortaliqRegisterConfigTest pinned info.version to 0.11.0, so bumping it to 0.12.0 (needed for OpenRegister's version-gated importFromApp to re-import at all) turned that test red. Updated, with the reason recorded alongside the existing per-version notes. Adds testRegisterDeclaresItselfWithExactlyItsOwnSchemaSlugs, which is the actual gate: it asserts components.registers.portaliq exists, that its version tracks info.version, and that its schema list is EXACTLY the set of schema SLUGS (falling back to the components.schemas key only when a schema declares none). Slugs, not keys, because ImportHandler keys its schemasMap by $schema->getSlug() — a register listing the keys binds zero schemas while still looking correctly declared, which is how this defect stayed invisible. Verified as a positive control, not assumed: against the pre-change portaliq_register.json the new test FAILS with "Failed asserting that an array has the key 'portaliq'"; with the fix the class is 9 tests / 50 assertions green.
Quality Report — ConductionNL/portaliq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| check-manifest | ❌ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 871/871 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ |
Quality workflow — 2026-08-04 20:15 UTC
Download the full PDF report from the workflow artifacts.
|
Merging with What it fixesWithout
The part that would have shipped as a silent no-op
Every slug here was derived from the seed objects' own Verified end-to-end on a disposable instance: schemas bound into the register, and seed objects returning 200 where they previously 404'd. |
fix(register): the portaliq register could never be created on a clean install
lib/Settings/portaliq_register.json declared no components.registers section.
OpenRegister's ImportHandler creates a Register row from that key and nowhere
else on the main/beta lines (ImportHandler.php:1514, the
$data['components']['registers'] loop) -- so a clean install created 9 schemas
and ZERO registers, and then skipped all 5 seed objects, whose @self.register
is resolved through the registersMap that section populates.
Reproduced on a disposable Nextcloud 34 + OpenRegister main (0.2.19):
occ app:enable portaliq -> "portaliq 0.1.8 enabled"
GET /api/registers -> HTTP 200, 0 registers
GET /api/schemas -> HTTP 200, 9 schemas
GET /api/objects/portaliq/portalPage -> HTTP 404
{"message":"Register not found: 'portaliq'"}
The failure is silent: the import itself reports success, and every object
route 404s with a message that reads like a routing problem rather than a
missing register.
After this change, on the same clean install (register absent BEFORE enable,
which is the evidence the fix stands on its own):
register 'portaliq' present AFTER enable: True
id=1 title='Portaliq Register' version=0.12.0 schemas=9
9/9 expected schema slugs present AND bound into the register
9/9 GET /api/objects/portaliq/ -> HTTP 200
The slug is derived, not invented: lib/ and the seed objects address
objects/portaliq/<schema>and all 5 seed objects carry@self.register: "portaliq".The schema list is the EXACT union of the register document's schemas, by
SLUG. That distinction is load-bearing: ImportHandler keys its schemasMap by
$schema->getSlug() (ImportHandler.php:1397/1477), not by the components.schemas
object key. For portaliq the two coincide; in decidesk, openbuild and scholiq
they do not, and a register listing the keys binds ZERO schemas while still
looking correctly declared.
info.version 0.11.0 -> 0.12.0 so OpenRegister's version-gated importFromApp
actually re-imports; a frozen version would never pick up later schemas.
appinfo/info.xml already lists InitializeSettings under as well as
, so portaliq does NOT have the second, independent gap where
a register import is declared post-migration-only and therefore never runs on
a fresh install (Installer::installAppLastSteps() guards both the pre- and
post-migration blocks with
if ($previousVersion !== '')).