Adapt work package show view for semantic identifiers#22704
Conversation
9bce150 to
206d502
Compare
7756686 to
1f8bf4c
Compare
3 tasks
7c38287 to
a3b5e32
Compare
1f8bf4c to
a88120e
Compare
a3b5e32 to
bb04400
Compare
a88120e to
d517a42
Compare
bb04400 to
c466ac6
Compare
7d160d6 to
0c3e8bd
Compare
128f33b to
be79dc3
Compare
257e36d to
abe86e8
Compare
73bae1e to
48946fe
Compare
abe86e8 to
95be852
Compare
judithroth
reviewed
Apr 15, 2026
Contributor
judithroth
left a comment
There was a problem hiding this comment.
I can't properly finish this review, because I have to leave now. So just some quick first feedback, but looks promising!
95be852 to
2fb1cff
Compare
36d081a to
3b6ba7d
Compare
2fb1cff to
4c1b1a6
Compare
Override WorkPackageBaseResource.id to read the API's displayId field,
which is always present and returns the appropriate identifier for the
current mode ("PROJ-42" in semantic, "123" in classic). This single
change propagates to all WP views without per-view conditional logic.
Also widen SPA route patterns from numeric-only to alphanumeric so
semantic IDs like "PROJ-42" can be used in split view and BIM URLs,
and remove parseInt coercion in hover sync since WP IDs may now be
non-numeric strings.
Filters like resourceId, entityId, and parent map to integer database
columns. Now that wp.id returns the display identifier ("KSTP-7"), these
must use $source.id (the numeric PK) instead.
Overriding wp.id to return the semantic identifier (e.g. "PROJ-42")
broke cache keys, API filters, row rendering, and CSS selectors that
all depend on the numeric PK.
Instead, keep wp.id as the numeric PK and add two new properties:
- displayId: returns the user-facing identifier ("PROJ-42" or "123")
- displayIdWithHash: returns "#PROJ-42" or "#123" for UI display
Also adds a COALESCE fallback in the SQL representer so work packages
created before semantic mode was enabled still get a valid displayId.
Centralise the URL segment pattern that matches work package identifiers into a named constant so route definitions reference a single source of truth instead of duplicating the regex.
parent_id is consumed by parseInt() in wp-create.component.ts, which returns NaN for semantic identifiers like "PROJ-42". This silently breaks parent assignment when creating child work packages. Revert the route constraint back to [0-9]+ since parent_id is a machine-level concern used in API calls, not a display identifier. Add a feature spec with semantic mode enabled to verify the URL uses the numeric PK and the parent relationship is set correctly.
The frontend pattern was [A-Za-z0-9_-]+ which accepted invalid identifiers like "---", "abc", or "42abc". Tighten it to match the backend ID_ROUTE_CONSTRAINT: either a pure numeric ID or a semantic identifier starting with a letter and ending with a hyphen followed by digits (e.g. "PROJ-42").
Cover the changed behavior where subjectWithId now delegates to displayIdWithHash instead of inlining the numeric id. Tests verify semantic mode output (#PROJ-7), classic mode (#42), and no suffix for new resources.
In semantic mode, work packages created before the feature was enabled have a nil identifier column. Previously display_id returned nil for these, causing the Ruby representer to serialize displayId as null while the SQL representer used COALESCE to return the numeric id. Use identifier.presence to fall back to the numeric id, aligning both representer paths.
Semantic identifiers (e.g. PROJ-42) are self-describing and should not be prefixed with #. Only classic numeric IDs retain the hash prefix (e.g. #42). This matches Jira's convention and PR review feedback. The detection uses a simple regex check: if the displayId contains any letter, it's semantic (no hash); otherwise it's classic (hash prefix).
PR #22710 made displayId a required field in all API v3 work package responses. The if/else fallback to this.id and corresponding legacy specs are now dead code. Collapse to a null-coalescing one-liner (still safe against stale cache during rolling deploys) and remove specs that tested the absent-field scenario.
4c1b1a6 to
26f87c6
Compare
The idLabel getter was bypassing formattedId entirely, always rendering the numeric PK with a # prefix. In semantic mode this showed #9720 instead of KSTP-1.
HDinger
reviewed
Apr 16, 2026
| */ | ||
| public get formattedId():string { | ||
| const wpId = this.displayId; | ||
| return /[A-Za-z]/.test(wpId) ? wpId : `#${wpId}`; |
Contributor
There was a problem hiding this comment.
I'd prefer a more clean way of detecting the semantic mode in the frontend but since we don't have that, the regex is probably fine. However, please add that as a const to the work-package-id-pattern.ts file that you created. Thus it is at least reusable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ticket
https://community.openproject.org/work_packages/73716
What are you trying to accomplish?
Propagate the
displayIdAPI field to the frontend work package show view. After PRs #22710 and #22718 made the API always return adisplayIdand the backend finders resolve semantic identifiers, this PR addsdisplayIdandformattedIdproperties to the frontend resource so the correct identifier can be shown in the show view.What approach did you choose and why?
Keeps machine and display identifiers separate. Instead of overriding
wp.id(which is a machine identifier used for cache keys, API paths, filter values, row IDs, and CSS selectors), two new read-only properties are added toWorkPackageBaseResource:displayId- returns the user-facing identifier from the API ("PROJ-42"in semantic mode,"42"in classic mode). Uses null-coalescing fallback to numeric PK as a safety net for stale cache during rolling deploys, but the field is now mandatory in API responses (per PR Expose displayId in API v3 work package endpoints #22710).formattedId- returns the display-ready form:PROJ-42(no prefix) for semantic identifiers,#42(hash-prefixed) for classic numeric IDs. Semantic identifiers are self-describing so they don't need the#prefix, matching Jira's convention.subjectWithIdandsubjectWithTypenow useformattedId, so tooltip-style labels render as"Fix the bug (PROJ-42)"in semantic mode and"Fix the bug (#42)"in classic mode.Also widens SPA route patterns from
[0-9]+to a sharedWP_ID_URL_PATTERNconstant so semantic IDs likePROJ-42can be used in split view and BIM URLs.On the backend, the SQL representer now uses
COALESCE(identifier, id::text)so work packages created before semantic mode was enabled still get a validdisplayIdinstead of NULL.Merge checklist