Skip to content

filterDashboards() would blank every result — extractUuid() probes a magic accessor on launchpad's own Dashboard entity (latent: no callers yet) #103

Description

@rubenvdlinde

Verified against origin/development with git show.

Summary

MetadataService::extractUuid() (lib/Service/MetadataService.php:564) probes:

if (is_object($dashboard) === true && method_exists($dashboard, 'getUuid') === true) {

$dashboard is launchpad's own OCA\Launchpad\Db\Dashboard, which extends OCP\AppFramework\Db\Entity and declares getUuid() as an @method docblock (lib/Db/Dashboard.php:30). Nextcloud's Entity serves it through __call(), so method_exists() is false and the method returns null.

Its caller filterDashboards() then continues every dashboard and returns [].

Measured

Live, against the server's own lib/public/AppFramework/Db/Entity.php:

subject method_exists is_callable
an Entity subclass's docblock-only accessor false true
a genuinely concrete method — control true true
🔴 Entity::getId false true

Consequence: latent, not live

filterDashboards() has zero callers anywhere in launchpad — no PHP, JS, Vue or test reference. So nothing is broken for users today.

Filing it anyway because it is a loaded gun: the first caller wired to it gets a total metadata-filter blackout, and the failure is silent — an empty list is indistinguishable from "nothing matched". Wiring it up would look like the new caller's bug.

Why this one matters beyond launchpad

This is the fleet's clearest proof that the pattern is not an OpenRegister problem. It is a Nextcloud Entity problem, and it bites an app against its own entity.

launchpad has 21 entity files declaring @method accessors. Any method_exists() probe against any of them is false.

Related in this repo:

  • lib/Service/DashboardVersionService.php:764method_exists($entity, $setter) with a variable name, which a literal-name grep cannot see.
  • lib/Service/DashboardVersionService.php:560 — probes getContentBackend, a method I could not find defined anywhere in the fleet. Possibly a phantom; worth confirming against whatever supplies that object.

Fix notes

is_callable() is not a membership test on a __call class — measured, it is true for any name, and the call then raises BadFunctionCallException. Swapping the probe yields an always-true guard, so make the call exception-safe in the same edit.

For an entity you own, property_exists($entity, 'uuid') is the better probe: it is a genuine membership test. openregister's MultiTenancyTrait uses exactly that, twice, for this reason.

Do not fall back to getId() — it is magic too.

Fleet context and the full 18-repo table: fleet-board/findings/method-exists-sweep.md.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions