[STC-730] Adapt BCF functionality to semantic identifiers

This commit is contained in:
Tomas Hykel
2026-06-11 19:53:24 +02:00
parent 24a5b24bcd
commit 4563d09ef7
8 changed files with 196 additions and 13 deletions
@@ -59,7 +59,7 @@ module Bim::Bcf::API::V2_1
property :reference_links,
getter: ->(decorator:, **) {
[decorator.api_v3_paths.work_package(work_package.id)]
[decorator.api_v3_paths.work_package(work_package.display_id)]
}
property :title,
@@ -57,7 +57,7 @@ module Bim::Bcf
end
def use_work_package(links:, params:)
work_package = WorkPackage.visible(user).find_by(id: work_package_id_from_links(links))
work_package = WorkPackage.visible(user).find_by_display_id(work_package_identifier_from_links(links))
return work_package_not_found_result if work_package.nil?
::WorkPackages::UpdateService
@@ -71,10 +71,10 @@ module Bim::Bcf
.call(**params)
end
def work_package_id_from_links(links)
def work_package_identifier_from_links(links)
links
.take(1)
.map { |link| link.split("/").last.to_i }
.map { |link| link.split("/").last }
.first
end
+1 -1
View File
@@ -163,7 +163,7 @@ module OpenProject::Bim
{
href: bcf_v2_1_paths.topics(represented.project.identifier),
title: "Convert to BCF",
payload: { reference_links: [api_v3_paths.work_package(represented.id)] },
payload: { reference_links: [api_v3_paths.work_package(represented.display_id)] },
method: :post
}
end
@@ -150,6 +150,19 @@ RSpec.describe API::V3::WorkPackages::WorkPackageRepresenter do
.to be_json_eql({ reference_links: ["/api/v3/work_packages/#{work_package.id}"] }.to_json)
.at_path("_links/convertBCF/payload")
end
context "with semantic work package identifiers",
with_settings: { work_packages_identifier: "semantic" } do
let(:work_package) do
build_stubbed(:work_package, bcf_issue: bcf_topic, identifier: "PROJ-7", sequence_number: 7)
end
it "uses the semantic identifier in the payload" do
expect(subject)
.to be_json_eql({ reference_links: ["/api/v3/work_packages/PROJ-7"] }.to_json)
.at_path("_links/convertBCF/payload")
end
end
end
context "if a bcf issue is assigned" do
@@ -190,6 +190,19 @@ RSpec.describe OpenProject::Bim::BcfXml::IssueWriter do
end
end
context "with semantic work package identifiers",
with_settings: { work_packages_identifier: "semantic" } do
let(:project) { create(:project, identifier: "BIMPROJ") }
it "writes the semantic identifier into the ReferenceLink" do
work_package.reload
expect(work_package.display_id).to start_with "BIMPROJ-"
expect(subject.at("Topic/ReferenceLink").content)
.to end_with "/work_packages/#{work_package.display_id}"
end
end
context "when bcf_issue snapshot is false" do
let(:vp_snapshot) { false }
@@ -124,6 +124,29 @@ RSpec.describe Bim::Bcf::API::V2_1::Topics::SingleRepresenter, "rendering" do
let(:value) { [api_v3_paths.work_package(work_package.id)] }
let(:path) { "reference_links" }
end
context "with semantic work package identifiers",
with_settings: { work_packages_identifier: "semantic" } do
let(:work_package) do
build_stubbed(:work_package,
identifier: "PROJ-42",
sequence_number: 42,
assigned_to: assignee,
due_date: Time.zone.today,
status:,
priority:,
type:).tap do |wp|
allow(wp)
.to receive(:journals)
.and_return(journals)
end
end
it_behaves_like "attribute" do
let(:value) { [api_v3_paths.work_package("PROJ-42")] }
let(:path) { "reference_links" }
end
end
end
context "title" do
@@ -127,7 +127,7 @@ RSpec.describe "BCF 2.1 topics resource", content_type: :json do
modified_author: current_user.mail,
modified_date: work_package.updated_at.iso8601(3),
reference_links: [
api_v3_paths.work_package(work_package.id)
api_v3_paths.work_package(work_package.display_id)
],
stage: bcf_issue.stage,
title: work_package.subject,
@@ -175,7 +175,7 @@ RSpec.describe "BCF 2.1 topics resource", content_type: :json do
modified_author: current_user.mail,
modified_date: work_package.updated_at.iso8601(3),
reference_links: [
api_v3_paths.work_package(work_package.id)
api_v3_paths.work_package(work_package.display_id)
],
stage: bcf_issue.stage,
title: work_package.subject,
@@ -220,7 +220,7 @@ RSpec.describe "BCF 2.1 topics resource", content_type: :json do
modified_author: current_user.mail,
modified_date: work_package.updated_at.iso8601(3),
reference_links: [
api_v3_paths.work_package(work_package.id)
api_v3_paths.work_package(work_package.display_id)
],
stage: bcf_issue.stage,
title: work_package.subject,
@@ -234,6 +234,19 @@ RSpec.describe "BCF 2.1 topics resource", content_type: :json do
end
end
context "with semantic work package identifiers",
with_settings: { work_packages_identifier: "semantic" } do
it "renders the semantic identifier in the reference_links" do
work_package.reload
expect(response).to have_http_status :ok
expect(work_package.display_id).to match(/\A#{project.identifier}-\d+\z/)
expect(response.body)
.to be_json_eql([api_v3_paths.work_package(work_package.display_id)].to_json)
.at_path("reference_links")
end
end
context "lacking permission to see project" do
let(:current_user) { non_member_user }
@@ -272,7 +285,7 @@ RSpec.describe "BCF 2.1 topics resource", content_type: :json do
modified_author: current_user.mail,
modified_date: work_package.updated_at.iso8601(3),
reference_links: [
api_v3_paths.work_package(work_package.id)
api_v3_paths.work_package(work_package.display_id)
],
stage: bcf_issue.stage,
title: work_package.subject,
@@ -558,7 +571,7 @@ RSpec.describe "BCF 2.1 topics resource", content_type: :json do
labels:,
index:,
reference_links: [
api_v3_paths.work_package(work_package&.id)
api_v3_paths.work_package(work_package&.display_id)
],
assigned_to: view_only_user.mail,
due_date: Date.today.iso8601,
@@ -597,7 +610,7 @@ RSpec.describe "BCF 2.1 topics resource", content_type: :json do
labels: [],
index: nil,
reference_links: [
api_v3_paths.work_package(work_package&.id)
api_v3_paths.work_package(work_package&.display_id)
],
assigned_to: assigned_to || base&.assigned_to&.mail,
due_date: due_date || base&.due_date,
@@ -661,6 +674,68 @@ RSpec.describe "BCF 2.1 topics resource", content_type: :json do
end
end
context "with semantic work package identifiers",
with_settings: { work_packages_identifier: "semantic" } do
let(:params) do
{
title: "BCF topic #{existing_work_package.display_id}",
reference_links: [
api_v3_paths.work_package(existing_work_package.display_id)
]
}
end
it_behaves_like "bcf api successful response" do
let(:expected_status) { 201 }
let(:expected_body) do
expected_body_for_bcf_issue(
Bim::Bcf::Issue.last.reload,
title: params[:title],
creation_author_mail: existing_work_package.author.mail,
modified_author_mail: edit_member_user.mail,
base: existing_work_package
)
end
end
context "with a link using the numeric id" do
let(:params) do
{
title: "BCF topic ##{existing_work_package.id}",
reference_links: [
api_v3_paths.work_package(existing_work_package.id)
]
}
end
it_behaves_like "bcf api successful response" do
let(:expected_status) { 201 }
let(:expected_body) do
expected_body_for_bcf_issue(
Bim::Bcf::Issue.last.reload,
title: params[:title],
creation_author_mail: existing_work_package.author.mail,
modified_author_mail: edit_member_user.mail,
base: existing_work_package
)
end
end
end
context "with a link to a non-existing semantic identifier" do
let(:params) do
{
title: "A new BCF topic",
reference_links: [
api_v3_paths.work_package("#{project.identifier}-99999")
]
}
end
it_behaves_like "bcf api not found response"
end
end
context "with a non-existing work package" do
let(:params) do
{
@@ -807,7 +882,7 @@ RSpec.describe "BCF 2.1 topics resource", content_type: :json do
labels: [],
index:,
reference_links: [
api_v3_paths.work_package(work_package&.id)
api_v3_paths.work_package(work_package&.display_id)
],
assigned_to: view_only_user.mail,
due_date: Date.today.iso8601,
@@ -846,7 +921,7 @@ RSpec.describe "BCF 2.1 topics resource", content_type: :json do
labels: [],
index: nil,
reference_links: [
api_v3_paths.work_package(work_package&.id)
api_v3_paths.work_package(work_package&.display_id)
],
assigned_to: nil,
due_date: nil,
@@ -50,4 +50,63 @@ RSpec.describe Bim::Bcf::Issues::CreateService, type: :model do
end
end
end
describe "resolving work packages from reference_links" do
include API::V3::Utilities::PathHelper
let(:project) { create(:project, enabled_module_names: %i[bim work_package_tracking]) }
let(:user) do
create(:user,
member_with_permissions: { project => %i[manage_bcf
view_linked_issues
view_work_packages
edit_work_packages] })
end
let(:work_package) { create(:work_package, project:) }
let(:instance) { described_class.new(user:) }
subject(:service_call) { instance.call(reference_links:) }
context "with a link containing the numeric id" do
let(:reference_links) { [api_v3_paths.work_package(work_package.id)] }
it "links the topic to the referenced work package" do
expect(service_call).to be_success
expect(service_call.result.work_package).to eq work_package
end
end
context "with semantic work package identifiers",
with_settings: { work_packages_identifier: "semantic" } do
let(:project) { create(:project, identifier: "SEMID", enabled_module_names: %i[bim work_package_tracking]) }
context "with a link containing the semantic identifier" do
let(:reference_links) { [api_v3_paths.work_package(work_package.display_id)] }
it "links the topic to the referenced work package" do
expect(work_package.display_id).to start_with "SEMID-"
expect(service_call).to be_success
expect(service_call.result.work_package).to eq work_package
end
end
context "with a link containing the numeric id" do
let(:reference_links) { [api_v3_paths.work_package(work_package.id)] }
it "links the topic to the referenced work package" do
expect(service_call).to be_success
expect(service_call.result.work_package).to eq work_package
end
end
context "with a link to an unknown semantic identifier" do
let(:reference_links) { [api_v3_paths.work_package("SEMID-9999")] }
it "fails with a not found error" do
expect(service_call).to be_failure
expect(service_call.errors.symbols_for(:base)).to include :error_not_found
end
end
end
end
end