Skip to content

Resolve semantic work package id when moving a work package#23016

Merged
akabiru merged 9 commits intodevfrom
bugfix/move-work-package-semantic-id-404
May 5, 2026
Merged

Resolve semantic work package id when moving a work package#23016
akabiru merged 9 commits intodevfrom
bugfix/move-work-package-semantic-id-404

Conversation

@akabiru
Copy link
Copy Markdown
Member

@akabiru akabiru commented Apr 30, 2026

https://community.openproject.org/wp/74633

Bug

/work_packages/KSTP-8/move/new returns 404 in semantic mode while the numeric form works, and the post-move-and-follow redirect lands on a numeric URL.

sematic-move-wp-error.mp4

Fix

Two distinct causes that happen to surface together: the bulk-WP controller filter integer-coerces semantic params, and the raw-SQL block allocation leaves the in-memory record holding a stale identifier. Fix each at its layer so move-and-copy honours semantic identifiers end-to-end.

akabiru added 7 commits April 30, 2026 16:45
Single-WP routes pass :work_package_id from the URL, which can be a
semantic identifier (e.g. "PROJ-42") since commit 4dfdd6e dropped
numeric pins on the move and copy HAL action links. The bulk
where(id:) lookup in find_work_packages only matches numeric primary
keys, so /work_packages/PROJ-42/move/new returned 404. Translate
:work_package_id through find_by_display_id first; the bulk :ids
branch is untouched since form submissions already send numeric ids.
Project#reserve_semantic_id_block! rewrites work_packages.identifier
and sequence_number via a raw SQL UPDATE, leaving the in-memory
records with the nil identifier set by SetAttributesService when the
project changed. Callers that read the WP straight out of the
ServiceResult (HAL action links, the move-and-follow redirect in
WorkPackages::BulkJob#redirect_path) then saw an empty display_id and
fell back to numeric URLs. Reload after the bulk allocation so the
in-memory state matches the database.
The plural counterpart to find_by_display_id resolves a mix of
numeric and semantic identifiers in a single composite scope so
callers (notably ApplicationController#find_work_packages, used by
bulk WP routes) do not have to branch on the input shape or partition
into numeric/semantic themselves. Mirrors scope_for_semantic_identifier
in using `.or` to union the primary-key, current-identifier, and
historical-alias arms; unknown values resolve to no match rather
than poisoning the rest of the set.
Both :work_package_id (single-WP routes) and :ids (bulk routes) come
from URLs or HTML forms and may carry semantic identifiers, so the
prior numeric-only assumption on :ids was wrong. Route both inputs
through WorkPackage.where_display_id_in, which returns a chainable
scope that matches numeric, current-identifier, and historical-alias
forms in a single query — no per-id round trip and no controller-side
coupling to WorkPackageSemanticAlias.

Tighten the post-move controller spec to assert the redirect URL
contains the destination project's semantic prefix when follow: 1 is
set, which is the path that exercises the moved WP's display_id.
The bulk SQL UPDATE persists identifier and sequence_number directly,
but callers holding live records have no cheap way to learn what was
written without reloading. Returning the {wp_id => "PROJ-N"} mapping
lets callers (next commit: WorkPackages::UpdateService) refresh
in-memory state without N round trips. Empty input returns an empty
hash for symmetric Hash-typed callers.
Previous fix reloaded each moved work package after the bulk SQL
UPDATE so HAL representers and the move-and-follow redirect saw the
freshly allocated semantic id. That cost one round trip per WP, which
matters when a parent moves with descendants. Now that
reserve_semantic_id_block! returns the {wp_id => identifier}
assignments directly, apply them with assign_attributes +
changes_applied — same observable result, zero extra queries
regardless of bulk size.
The semantic arm reimplemented the union of the identifier column
and the alias-table lookup that scope_for_semantic_identifier already
encapsulates. Both rely on `where(identifier:)` semantics that accept
arrays as IN clauses, so the scalar helper composes correctly for the
plural case. Keeps the SQL shape of "match by display id" defined in
one place — if the correlated EXISTS ever needs to become a semi-join
under bulk pressure, both callers benefit.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes semantic work package identifier handling in the move/copy flow so semantic URLs resolve correctly and redirects stay semantic when semantic mode is enabled.

Changes:

  • Refresh in-memory work package identifier/sequence after bulk semantic ID allocation during move (avoids stale to_param/redirect URLs).
  • Add WorkPackage.where_display_id_in for resolving mixed numeric IDs, semantic IDs, and historical aliases as a chainable relation.
  • Update ApplicationController#find_work_packages to accept semantic identifiers (single and bulk) and add specs covering the end-to-end behavior.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
spec/services/work_packages/semantic_ids/integration_spec.rb Verifies moved WPs have refreshed in-memory identifiers so to_param stays semantic.
spec/models/work_package/semantic_identifier_spec.rb Adds coverage for new where_display_id_in bulk resolver behavior and composability.
spec/models/projects/semantic_identifier_spec.rb Updates expectations for reserve_semantic_id_block! to return assignments (and {} for empty input).
spec/controllers/work_packages/moves_controller_spec.rb Ensures move “new” and “create” work with semantic WP IDs and redirect to semantic URLs.
app/services/work_packages/update_service.rb Applies bulk-allocated semantic ID assignments to in-memory records after raw SQL updates.
app/models/work_package/semantic_identifier/finder_methods.rb Introduces where_display_id_in relation builder for mixed identifier inputs.
app/models/projects/semantic_identifier.rb Makes reserve_semantic_id_block! return { wp_id => semantic_identifier } assignments.
app/controllers/application_controller.rb Switches bulk WP lookup to where_display_id_in to support semantic IDs and aliases.

Comment thread app/services/work_packages/update_service.rb Outdated
clear_changes_information is the established pattern in this codebase
for syncing in-memory dirty state after a side-channel persist (queries,
historic-attribute eager loading, progress/date-picker controllers).
changes_applied has the same effect but no other call site, so prefer
the local convention.
@akabiru akabiru self-assigned this Apr 30, 2026
@akabiru akabiru added the bugfix label Apr 30, 2026
@akabiru akabiru added this to the 17.5.x milestone Apr 30, 2026
Comment thread app/controllers/application_controller.rb
@akabiru akabiru marked this pull request as ready for review April 30, 2026 15:47
@akabiru akabiru requested a review from a team April 30, 2026 15:47
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 30, 2026

Deploying openproject with PullPreview

Field Value
Latest commit 8602aff
Job deploy
Status 🗑️ Preview destroyed
Preview URL Destroyed

View logs

Copy link
Copy Markdown
Contributor

@judithroth judithroth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this! The approach for the custom where is really nice!
I have Just one small remark but I think you'll find a good solution and I don't need to review again.

Comment thread app/services/work_packages/update_service.rb Outdated
Comment thread app/models/work_package/semantic_identifier/finder_methods.rb
clear_changes_information would also clobber any unrelated dirty
attributes the work package happened to be carrying at this point.
The intent is narrower: only the two attributes we just synced from
the raw-SQL UPDATE should be marked clean. Use clear_attribute_changes
with the explicit attribute list instead.
@akabiru akabiru merged commit 8edab11 into dev May 5, 2026
17 of 18 checks passed
@akabiru akabiru deleted the bugfix/move-work-package-semantic-id-404 branch May 5, 2026 13:50
@github-actions github-actions Bot locked and limited conversation to collaborators May 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Development

Successfully merging this pull request may close these issues.

3 participants