Skip to content

Feature request: a public, stable PHP API so other apps can create and update cards #8410

Description

@fds2610

Who is asking. NC-Gantt (https://codeberg.org/fds2610/NC-Gantt) is a project planning
app (Gantt chart) for Nextcloud. Projects, subprojects and tasks can be linked to Deck
boards, lists and cards, so that planning and day-to-day work stay in one place: the
plan hands work over to Deck, and Deck stays the single place where that work is done.

What we do today. Deck has an excellent read channel — since Deck 1.18 the
webhook-compatible CardCreatedEvent / CardUpdatedEvent / CardDeletedEvent (#7910) give
us everything we need to follow changes made in Deck. Thank you for those; this
request is only about the other direction.

For writing we have no supported option, so we call Deck's internal services from a
single adapter class (DeckGateway), fetched from the container at call time so that
our app still loads without Deck: CardService (find, create, update, archive),
AssignmentService::assignUser(), BoardService::find(), StackMapper (find, findAll).

These are not meant as an API, and we treat them accordingly: one adapter class, a
contract test that checks every class, method parameter and entity field we touch
against the installed Deck, and a documented review of stable31…stable35 plus master
before every release. That keeps the risk visible, but it does not remove it — and it
puts work on you too, because any refactoring in these services can silently break
third-party apps you never hear about.

Why the internal services are hard to use correctly

  1. Signatures move, by design. Between Deck 1.15 and 1.19, CardService::update()
    gained $done, $startdate and $color, partly in the middle of the parameter list. We
    now call create() and update() with named arguments resolved through reflection,
    because a positional call would break on the next release. That is a workaround, not
    an integration.
  2. The acting user comes from the session. CardService and PermissionService receive
    the user as private ?string $userId at construction time. In a background job or an
    occ command there is no session, so we have to set the "volatile active user" before
    calling Deck — and hope that is enough for every code path inside Deck. An API that
    takes the acting user as a parameter would make this correct by construction.
  3. No partial updates. update() requires title, stackId, type and owner even when we
    only want to move a due date. So every write is a read-modify-write over the whole
    card, which loses concurrent edits made in Deck between our read and our write —
    exactly the thing a planning app that writes dates must not do. (Same pain as API: Updating items should not require all parameters to be set #1578 on
    the REST side.) Deck already has OptionalNullableValue for done and color, so the
    idea of "field not supplied ≠ field set to null" exists in the code base; we would
    like it for all fields.

Why not the REST/OCS API? An app running inside the same server would have to call its
own server over HTTP, with credentials it does not have, losing the acting user's
session and the transaction context. Talk's "create card" goes that way today (#8116);
we would rather not add another one.

What we need

| Operation | Data
|
|--------------------------------|----------------------------------------------------
-------------------------------------------|
| Create a card | title, description, target list (stack), due date,
start date |
| Read a card | id, title, description, stack/board, due date,
start date, done, archived, deleted, assignees |
| Set dates | due date and start date, each independently, "leave
unchanged" distinguishable from "clear" |
| Mark done / undone | —
|
| Assign / unassign a user | card, user id; a clear, catchable error when the
account has no access to the board |
| Archive a card | (we never delete a card that a user might still
need) |
| List the stacks of a board | id, title, order — to offer a target list
|
| Permissions of the acting user | read / edit / manage on a board, so the UI can hide
what the user may not do |

Permissions stay with Deck. We are explicitly not asking for a way around Deck's
permission checks. Deck should keep checking the rights of the acting person on every
call, exactly as it does for its own controllers, and refuse otherwise. Our app also
never shares a board on the user's behalf: if an assignment fails because the account
cannot see the board, we want to catch that and tell the user, not fix it silently.

Suggested shape

  • Interfaces in a stable namespace of Deck (for example OCA\Deck\Api*), registered in
    the container, versioned with @SInCE tags — the same contract style Nextcloud uses
    for OCP, and the same promise the webhook-compatible events already made for reading.
  • The acting user as an explicit parameter of every call, not read from the session at
    construction time, so the API also works in background jobs and occ commands.
  • Partial updates: a change object or "not supplied" marker per field (Deck's existing
    OptionalNullableValue is a good starting point).
  • Documented exceptions for "not found", "no permission" and "invalid input", so
    callers can map them to their own states instead of catching Throwable.
  • A small surface is fine. The table above would already remove our entire dependency
    risk; we would rather have seven stable operations than everything Deck can do.

What this would change for us

Once such an API exists, our adapter would use it from that Deck version on and keep
the current internal calls only as a fallback for older versions — and we would drop
them entirely once those versions are out of support. We are happy to help: reviewing
a draft interface, testing a branch against our integration, or contributing the
implementation if you tell us the shape you would accept.

Activity

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

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