Skip to content

fix(concept-sets): handle duplicate names and disable Update for non-owned sets [v0.18] - #3208

Merged
khairul-syazwan merged 7 commits into
release/v0.18.0-betafrom
khairul-syazwan/conceptset-duplicate-name-v0.18
Aug 28, 2026
Merged

fix(concept-sets): handle duplicate names and disable Update for non-owned sets [v0.18]#3208
khairul-syazwan merged 7 commits into
release/v0.18.0-betafrom
khairul-syazwan/conceptset-duplicate-name-v0.18

Conversation

@khairul-syazwan

Copy link
Copy Markdown
Collaborator

Summary

Backport of #3207 to release/v0.18.0-beta.

A researcher-only user could not save a concept set. The Concept Sets UI called GET /conceptset/{id}/exists before saving, and that route is guarded by read:conceptset or write:conceptset. The concept set creator role holds neither, so the pre-check returned 403 and the save never ran. Atlas3 never makes this call, which is why it worked there.

The fix removes the WebAPI half of the pre-check and lets the database uq_cs_name constraint reject a duplicate as HTTP 409. The 409 is mapped to a typed conflict and shown to the user as the duplicate-name message instead of a generic error.

Separately, the Concept Sets drawer showed an active Update button for a set the user does not own. The list already marks these sets read-only with an eye icon. The fix passes the list row's writability into the drawer and disables Update for non-owned sets. It also makes legacy concept sets report ownership, so a shared legacy set owned by another user is read-only too.

Affected modules

  • plugins/functions/d2e-webapi — facade, conceptset routes/service/errors
  • plugins/ui/apps/concept-sets — Terminology, ConceptSets, ConceptSetsTable

Validation performed

The changed files are byte-identical between develop and this release branch, so the validation from #3207 applies. On develop:

  • Deno facade unit tests: 26 passed, 0 failed (denoland/deno:2.5.6).
  • Concept-sets app tsc --noEmit: clean.
  • Browser as researcher18 (create:conceptset only): create set succeeds; duplicate name shows the "already exists" message (409); non-owned set shows Update disabled; owned set shows Update enabled.
  • Browser as admin (owner): Update enabled on an owned legacy set; a mock shared legacy set owned by another user shows Update disabled.
  • Evidence screenshots in docs/projects/conceptset-webapi-researcher-403/screens/.

This run did not rebuild or re-run tests on the release image; the release branch shares the affected source with develop.

Merge Checklist

  • Automated Tests (Jasmine integration tests, Unit tests, and/or Performance tests)
  • Updated Manual tests / Demo Config
  • Documentation (Application guide, Admin guide, Markdown, Readme and/or Wiki)
  • Verified that local development environment is working with latest changes (integrated with latest release/v0.18.0-beta branch)
  • Following best practices in code review doc

Refs: #3207

…permission

A researcher-only user could not save a concept set. The save runs a
duplicate-name pre-check first, and that check asked WebAPI through
GET /conceptset/{id}/exists, guarded by

  isAnyPermitted(anyOf('read:conceptset','write:conceptset'))

with no isOwner fallback. The 'concept set creator' role holds neither
permission, so the check returned 403 and the save stopped before the create
was ever attempted. The create itself was always permitted, because it is
guarded by isPermitted('create:conceptset'). Atlas3 never asks this question
and works for the same user.

The facade merges two stores in that check. Only the WebAPI half fails, and
the WebAPI store already enforces the same rule: webapi.concept_set carries
the uq_cs_name unique constraint, which rejects a duplicate on write and
reports it as HTTP 409.

Keep the legacy half, which needs no permission, and let the constraint answer
for the WebAPI store. Map the 409 on create and on update to a typed error, so
that a duplicate name reaches the browser as a 409 with a readable message
rather than a 500. Remove the now-unreachable WebAPI probe.

No permission grant is required for a researcher to save a concept set.
…rns 409

The save flow discarded the error and always rendered the generic
'Error creating/updating' text. A duplicate name in the WebAPI store is now
reported as a 409 by the facade, so read it and show the same message the
pre-check already shows for the legacy store. Every other failure keeps the
generic text.
The Concept Sets list shows read-only rows (eye icon) for sets the user
does not own, but opening one still showed an active Update button. The
drawer re-fetches the set by id, and that get-by-id response carries an
unreliable hasWriteAccess flag (false for owned sets, and 500 for a
non-owned researcher set). The list row is the reliable source: it already
marks non-owned sets with an eye.

Pass the list row's writability into the drawer and use it as the
authoritative ownership signal. Reset the ownership state when a set opens,
so a failed get-by-id cannot leave a stale value. Keep the Update button
visible but disabled for sets the user does not own, and enabled for owned
sets, matching the read-only affordance already shown in the list.
The legacy mapper always reported hasWriteAccess true, so a shared legacy
concept set owned by another user showed an active Update button even though
the list marked it read-only. Decode the current user from the token and set
legacy hasWriteAccess by ownership (createdBy === current user). When no user
can be determined, keep the historical writable default. Covers the legacy
half of the ownership fix already applied to the WebAPI store.
… change

The ConceptSetsTable test asserted onAddEdit was called with only the id.
It is now called with the writability flag, so update the three expectations
to include the boolean. The /:id/exists route description still said the
check used the WebAPI database; it now probes the legacy store only, so
reflect that in the schema description.
@khairul-syazwan
khairul-syazwan merged commit 63e71d8 into release/v0.18.0-beta Aug 28, 2026
51 of 52 checks passed
@khairul-syazwan
khairul-syazwan deleted the khairul-syazwan/conceptset-duplicate-name-v0.18 branch August 28, 2026 06:34
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