diff --git a/Gemfile b/Gemfile index eb885674f97..2b4eb3a91cc 100644 --- a/Gemfile +++ b/Gemfile @@ -204,6 +204,9 @@ group :test do gem 'retriable', '~> 3.1.1' gem 'rspec-retry', '~> 0.6.1' + # XML comparison tests + gem 'compare-xml', '~> 0.66', require: false + gem 'rspec-example_disabler', git: 'https://github.com/finnlabs/rspec-example_disabler.git' # brings back testing for 'assigns' and 'assert_template' extracted in rails 5 diff --git a/Gemfile.lock b/Gemfile.lock index b76f6a35a4f..1325583f8d0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -397,6 +397,8 @@ GEM colored2 (3.1.2) commonmarker (0.20.1) ruby-enum (~> 0.5) + compare-xml (0.66) + nokogiri (~> 1.8) concurrent-ruby (1.1.5) cork (0.3.0) colored2 (~> 3.1) @@ -969,6 +971,7 @@ DEPENDENCIES cells-erb (~> 0.1.0) cells-rails (~> 0.0.9) commonmarker (~> 0.20.1) + compare-xml (~> 0.66) cucumber (~> 3.1.0) cucumber-rails (~> 1.8.0) daemons diff --git a/modules/bcf/app/models/bcf/issue.rb b/modules/bcf/app/models/bcf/issue.rb index 0bb8c0b4dd5..4dee1c6f3bc 100644 --- a/modules/bcf/app/models/bcf/issue.rb +++ b/modules/bcf/app/models/bcf/issue.rb @@ -19,6 +19,10 @@ module Bcf end end + def imported_title + markup_doc.xpath('//Topic/Title').text + end + def markup_doc @markup_doc ||= Nokogiri::XML markup, nil, 'UTF-8' end diff --git a/modules/bcf/app/views/bcf/issues/_render_issues.html.erb b/modules/bcf/app/views/bcf/issues/_render_issues.html.erb index 4152966f476..02eae392eb8 100644 --- a/modules/bcf/app/views/bcf/issues/_render_issues.html.erb +++ b/modules/bcf/app/views/bcf/issues/_render_issues.html.erb @@ -6,11 +6,10 @@ <% highlighting_class << ' -failed' if issue.errors.present? %>

- <%= issue.title %> -
<% if issue.work_package %> <%= link_to_work_package(issue.work_package) %> -
+ <% else %> + <%= issue.imported_title %> <% end %>

<% if issue.errors.present? %> diff --git a/modules/bcf/db/migrate/20191121140202_migrate_xml_viewpoint_to_json.rb b/modules/bcf/db/migrate/20191121140202_migrate_xml_viewpoint_to_json.rb index 296547b5713..8f8fc3e00a7 100644 --- a/modules/bcf/db/migrate/20191121140202_migrate_xml_viewpoint_to_json.rb +++ b/modules/bcf/db/migrate/20191121140202_migrate_xml_viewpoint_to_json.rb @@ -8,7 +8,7 @@ class MigrateXmlViewpointToJson < ActiveRecord::Migration[6.0] # Convert viewpoints ::Bcf::Viewpoint.reset_column_information ::Bcf::Viewpoint.find_each do |resource| - mapper = ::OpenProject::Bcf::BcfJson::ViewpointMapper + mapper = ::OpenProject::Bcf::BcfJson::ViewpointReader .new(resource.uuid, resource.viewpoint) resource.update_column(:json_viewpoint, mapper.result) diff --git a/modules/bcf/lib/open_project/bcf/bcf_json/viewpoint_mapper.rb b/modules/bcf/lib/open_project/bcf/bcf_json/viewpoint_reader.rb similarity index 99% rename from modules/bcf/lib/open_project/bcf/bcf_json/viewpoint_mapper.rb rename to modules/bcf/lib/open_project/bcf/bcf_json/viewpoint_reader.rb index 0c809c7bd29..4603b61277b 100644 --- a/modules/bcf/lib/open_project/bcf/bcf_json/viewpoint_mapper.rb +++ b/modules/bcf/lib/open_project/bcf/bcf_json/viewpoint_reader.rb @@ -1,6 +1,6 @@ module OpenProject::Bcf module BcfJson - class ViewpointMapper + class ViewpointReader ROOT_NODE ||= 'VisualizationInfo'.freeze attr_reader :uuid, :xml diff --git a/modules/bcf/lib/open_project/bcf/bcf_xml/base_writer.rb b/modules/bcf/lib/open_project/bcf/bcf_xml/base_writer.rb new file mode 100644 index 00000000000..ad86e1885ab --- /dev/null +++ b/modules/bcf/lib/open_project/bcf/bcf_xml/base_writer.rb @@ -0,0 +1,67 @@ +## +# Creates or updates a BCF issue and markup from a work package +module OpenProject::Bcf::BcfXml + class BaseWriter + attr_reader :markup_doc + + def initialize + @markup_doc = build_markup_document + end + + protected + + def root_node + raise NotImplementedError + end + + def root_node_attributes + {} + end + + ## + # Initial markup file as basic BCF compliant xml + def build_markup_document + Nokogiri::XML::Builder + .new(:encoding => 'UTF-8') do |xml| + xml.comment created_by_comment + xml.send(root_node, + "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", + "xmlns:xsd" => "http://www.w3.org/2001/XMLSchema", + **root_node_attributes) + end + .doc + end + + def prepend_into_or_insert(parent_node, node) + if first_child = parent_node.children.select(&:element?)&.first + first_child.previous = node + else + node.parent = parent_node + end + end + + def fetch(parent_node, name) + node = parent_node.at(name) || Nokogiri::XML::Node.new(name, markup_doc) + node.parent = parent_node unless node.parent.present? + node + end + + ## + # + def created_by_comment + " Created by #{Setting.app_title} #{OpenProject::VERSION} at #{Time.now} " + end + + def to_bcf_datetime(date_time) + date_time.utc.iso8601 + end + + def to_bcf_date(date) + date.iso8601 + end + + def url_helpers + @url_helpers ||= OpenProject::StaticRouting::StaticUrlHelpers.new + end + end +end diff --git a/modules/bcf/lib/open_project/bcf/bcf_xml/exporter.rb b/modules/bcf/lib/open_project/bcf/bcf_xml/exporter.rb index 87ab075b914..630ce25ae4d 100644 --- a/modules/bcf/lib/open_project/bcf/bcf_xml/exporter.rb +++ b/modules/bcf/lib/open_project/bcf/bcf_xml/exporter.rb @@ -131,7 +131,7 @@ module OpenProject::Bcf::BcfXml snapshot_file = File.join(issue_dir, vp.snapshot.filename) # Copy the files - dump_file vp_file, vp.viewpoint + dump_file vp_file, ViewpointWriter.new(vp).to_xml FileUtils.cp vp.snapshot.local_path, snapshot_file files << vp_file << snapshot_file diff --git a/modules/bcf/lib/open_project/bcf/bcf_xml/issue_reader.rb b/modules/bcf/lib/open_project/bcf/bcf_xml/issue_reader.rb index 3090d3112b8..e77d97bfb16 100644 --- a/modules/bcf/lib/open_project/bcf/bcf_xml/issue_reader.rb +++ b/modules/bcf/lib/open_project/bcf/bcf_xml/issue_reader.rb @@ -193,7 +193,7 @@ module OpenProject::Bcf::BcfXml uuid: vp[:uuid], # Save the viewpoint as json - viewpoint: viewpoint_as_json(vp[:uuid], vp[:viewpoint]), + json_viewpoint: viewpoint_as_json(vp[:uuid], read_entry(vp[:viewpoint])), viewpoint_name: vp[:viewpoint], # Save the snapshot as file attachment @@ -238,9 +238,9 @@ module OpenProject::Bcf::BcfXml ## # Map the xml viewpoint as json def viewpoint_as_json(uuid, xml) - ::OpenProject::Bcf::BcfJson::ViewpointMapper + ::OpenProject::Bcf::BcfJson::ViewpointReader .new(uuid, xml) - .to_json + .result end def new_comment(comment_data) diff --git a/modules/bcf/lib/open_project/bcf/bcf_xml/issue_writer.rb b/modules/bcf/lib/open_project/bcf/bcf_xml/issue_writer.rb index 1304f642677..0da31e0bf5c 100644 --- a/modules/bcf/lib/open_project/bcf/bcf_xml/issue_writer.rb +++ b/modules/bcf/lib/open_project/bcf/bcf_xml/issue_writer.rb @@ -1,7 +1,7 @@ ## # Creates or updates a BCF issue and markup from a work package module OpenProject::Bcf::BcfXml - class IssueWriter + class IssueWriter < BaseWriter attr_reader :work_package, :issue, :markup_doc, :markup_node TOPIC_SEQUENCE = [ @@ -34,11 +34,11 @@ module OpenProject::Bcf::BcfXml @work_package = work_package @issue = find_or_initialize_issue - # Read the existing markup XML or build an empty one - @markup_doc = build_markup_document + # Create markup document + super() # Remember root markup node for easier access - @markup_node = @markup_doc.at_xpath('/Markup') + @markup_node = markup_doc.at_xpath('/Markup') end def update @@ -58,7 +58,11 @@ module OpenProject::Bcf::BcfXml issue.save! end - private + protected + + def root_node + :Markup + end ## # Get the nokogiri document from the markup xml @@ -66,16 +70,7 @@ module OpenProject::Bcf::BcfXml if issue.markup Nokogiri::XML issue.markup, &:noblanks else - build_initial_markup_xml.doc - end - end - - ## - # Initial markup file as basic BCF compliant xml - def build_initial_markup_xml - Nokogiri::XML::Builder.new do |xml| - xml.comment created_by_comment - xml.Markup "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", "xmlns:xsd" => "http://www.w3.org/2001/XMLSchema" + super end end @@ -124,12 +119,6 @@ module OpenProject::Bcf::BcfXml end end - def fetch(parent_node, name) - node = parent_node.at(name) || Nokogiri::XML::Node.new(name, markup_doc) - node.parent = parent_node unless node.parent.present? - node - end - def topic_attributes(topic_node) topic_node['Guid'] = issue.uuid topic_node['TopicType'] = work_package.type.name # TODO: Looks wrong to me. Probably better to use original TopicType? @@ -239,28 +228,10 @@ module OpenProject::Bcf::BcfXml end end - ## - # - def created_by_comment - " Created by #{Setting.app_title} #{OpenProject::VERSION} at #{Time.now} " - end - ## # Find existing issue or create new def find_or_initialize_issue ::Bcf::Issue.find_or_initialize_by(work_package: work_package) end - - def to_bcf_datetime(date_time) - date_time.utc.iso8601 - end - - def to_bcf_date(date) - date.iso8601 - end - - def url_helpers - @url_helpers ||= OpenProject::StaticRouting::StaticUrlHelpers.new - end end end diff --git a/modules/bcf/lib/open_project/bcf/bcf_xml/viewpoint_writer.rb b/modules/bcf/lib/open_project/bcf/bcf_xml/viewpoint_writer.rb new file mode 100644 index 00000000000..fd089b6952f --- /dev/null +++ b/modules/bcf/lib/open_project/bcf/bcf_xml/viewpoint_writer.rb @@ -0,0 +1,182 @@ +## +# Creates or updates a BCF issue and markup from a work package +module OpenProject::Bcf::BcfXml + class ViewpointWriter < BaseWriter + attr_reader :viewpoint + + def initialize(viewpoint) + @viewpoint = viewpoint + super() + end + + def to_xml + doc.to_xml(indent: 2) + end + + def doc + @doc ||= begin + viewpoint_node = fetch(markup_doc, root_node.to_s) + + Nokogiri::XML::Builder.with(viewpoint_node) do |xml| + components xml + + camera('orthogonal_camera', xml) + camera('perspective_camera', xml) + + lines xml + clipping_planes xml + bitmaps xml + end + + markup_doc + end + end + + protected + + def root_node + :VisualizationInfo + end + + def root_node_attributes + { Guid: viewpoint.uuid } + end + + def dig_json(*args) + viewpoint.json_viewpoint.dig(*args) + end + + def components(xml) + return unless dig_json('components') + + xml.Components do + view_setup_hints xml + selected_components xml + visibility xml + coloring xml + end + end + + def view_setup_hints(xml) + return unless (setup_hash = dig_json('components', 'visibility', 'view_setup_hints')) + + xml.ViewSetupHints(camelized(setup_hash)) + end + + def selected_components(xml) + return unless (selected = dig_json('components', 'selection')) + + xml.Selection do + selected.each do |comp_hash| + xml.Component camelized(comp_hash) + end + end + end + + def visibility(xml) + return unless (visibility_hash = dig_json 'components', 'visibility') + + xml.Visibility(DefaultVisibility: visibility_hash['default_visibility']) do + exceptions = visibility_hash['exceptions'] + next unless exceptions + + xml.Exceptions do + Array.wrap(exceptions).each do |comp_hash| + xml.Component camelized(comp_hash) + end + end + end + end + + def coloring(xml) + return unless (colors = dig_json 'components', 'coloring') + + xml.Coloring do + Array.wrap(colors).each do |color| + xml.Color Color: color['color'].delete_prefix('#') do + Array.wrap(color['components']).each do |comp_hash| + xml.Component camelized(comp_hash) + end + end + end + end + end + + def camera(type, xml) + return unless (camera = dig_json(type)) + + xml.send(type.camelize) do + %w[CameraViewPoint CameraDirection CameraUpVector].each do |entry| + xml.send(entry) do + coords = camera[entry.underscore] + to_xml_coords(coords, xml) + end + end + xml.FieldOfView convert_float(camera['field_of_view']) + end + end + + def lines(xml) + return unless (lines = dig_json 'lines') + + xml.Lines do + Array.wrap(lines).each do |line| + xml.Line do + xml.StartPoint { to_xml_coords(line['start_point'], xml) } + xml.EndPoint { to_xml_coords(line['end_point'], xml) } + end + end + end + end + + def clipping_planes(xml) + return unless (planes = dig_json 'clipping_planes') + + xml.ClippingPlanes do + Array.wrap(planes).each do |plane| + xml.ClippingPlane do + xml.Location { to_xml_coords(plane['location'], xml) } + xml.Direction { to_xml_coords(plane['direction'], xml) } + end + end + end + end + + def bitmaps(xml) + return unless (entries = dig_json 'bitmaps') + + # Bitmaps are rendered flat, whyever that is + entries.each do |bitmap| + xml.Bitmaps do + xml.Bitmap bitmap['bitmap_type'].upcase + xml.Reference bitmap['bitmap_data'] + xml.Location { to_xml_coords bitmap['location'], xml } + xml.Normal { to_xml_coords bitmap['normal'], xml } + xml.Up { to_xml_coords bitmap['up'], xml } + xml.Height convert_float(bitmap['height']) + end + end + end + + ## + # Helper to transform a hash into camelized keys + def camelized(hash) + hash.transform_keys(&:camelize) + end + + ## + # Convert a float to BCF format that strips + # insignificant zeros + def convert_float(val) + val.to_s.gsub(/(\.)0+$/, '') + end + + ## + # Helper to render X,Y,Z hash as set of nodes + def to_xml_coords(hash, xml) + hash.each do |key, val| + xml.send(key.to_s.upcase, convert_float(val)) + end + end + end +end diff --git a/modules/bcf/spec/factories/bcf_viewpoint_factory.rb b/modules/bcf/spec/factories/bcf_viewpoint_factory.rb index 5c08dbf9900..e9952a93c65 100644 --- a/modules/bcf/spec/factories/bcf_viewpoint_factory.rb +++ b/modules/bcf/spec/factories/bcf_viewpoint_factory.rb @@ -38,10 +38,10 @@ FactoryBot.define do new_uuid = SecureRandom.uuid uuid { new_uuid } viewpoint_name { "full_viewpoint.bcfv" } - viewpoint do - file = OpenProject::Bcf::Engine.root.join("spec/fixtures/viewpoints/#{viewpoint_name}.xml") + json_viewpoint do + file = OpenProject::Bcf::Engine.root.join("spec/fixtures/viewpoints/#{viewpoint_name}.json") if file.readable? - file.read + JSON.parse(file.read) else warn "Viewpoint name #{viewpoint_name} doesnt map to a viewpoint fixture" end diff --git a/modules/bcf/spec/fixtures/viewpoints/full_viewpoint.bcfv.xml b/modules/bcf/spec/fixtures/viewpoints/full_viewpoint.bcfv.xml index af60fd30ae3..db0b1da07b0 100644 --- a/modules/bcf/spec/fixtures/viewpoints/full_viewpoint.bcfv.xml +++ b/modules/bcf/spec/fixtures/viewpoints/full_viewpoint.bcfv.xml @@ -1,6 +1,6 @@ - + @@ -120,13 +120,13 @@ -0.9999999999999999 - 1.253656364893038E-16 - 0.0 + 1.253656364893038e-16 + 0 - -5.43903050550883E-34 - -4.338533794284917E-18 - 1.0 + -5.43903050550883e-34 + -4.338533794284917e-18 + 1 1666.1814563907683 @@ -140,13 +140,13 @@ -3 - 1.253656364893038E-16 - 0.0 + 1.253656364893038e-16 + 0 30.1234 -9000 - 1.0 + 1 10 diff --git a/modules/bcf/spec/fixtures/viewpoints/minimal.bcfv.xml b/modules/bcf/spec/fixtures/viewpoints/minimal.bcfv.xml index 780ee5877a2..d67ae08e076 100644 --- a/modules/bcf/spec/fixtures/viewpoints/minimal.bcfv.xml +++ b/modules/bcf/spec/fixtures/viewpoints/minimal.bcfv.xml @@ -1,5 +1,5 @@ - + 12.3456789 @@ -7,8 +7,8 @@ -1234.1234 - -1.0 - -2.0 + -1 + -2 -3 diff --git a/modules/bcf/spec/fixtures/viewpoints/neubau_sc_1.bcfv.xml b/modules/bcf/spec/fixtures/viewpoints/neubau_sc_1.bcfv.xml index b9f9669bbb2..8f63e2b96a0 100644 --- a/modules/bcf/spec/fixtures/viewpoints/neubau_sc_1.bcfv.xml +++ b/modules/bcf/spec/fixtures/viewpoints/neubau_sc_1.bcfv.xml @@ -1,5 +1,5 @@ - + diff --git a/modules/bcf/spec/fixtures/viewpoints/neubau_sc_1_fixed.bcfv.xml b/modules/bcf/spec/fixtures/viewpoints/neubau_sc_1_fixed.bcfv.xml new file mode 100644 index 00000000000..d596abe0dd0 --- /dev/null +++ b/modules/bcf/spec/fixtures/viewpoints/neubau_sc_1_fixed.bcfv.xml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 58.1038 + 16.5493 + 24.7329 + + + 1.28974 + 1.2105 + -0.569962 + + + 0.223629 + 0.209889 + 0.951807 + + 60 + + \ No newline at end of file diff --git a/modules/bcf/spec/lib/open_project/bcf/bcf_json/viewpoint_mapper_shared_examples.rb b/modules/bcf/spec/lib/open_project/bcf/bcf_json/viewpoint_reader_shared_examples.rb similarity index 100% rename from modules/bcf/spec/lib/open_project/bcf/bcf_json/viewpoint_mapper_shared_examples.rb rename to modules/bcf/spec/lib/open_project/bcf/bcf_json/viewpoint_reader_shared_examples.rb diff --git a/modules/bcf/spec/lib/open_project/bcf/bcf_json/viewpoint_mapper_spec.rb b/modules/bcf/spec/lib/open_project/bcf/bcf_json/viewpoint_reader_spec.rb similarity index 95% rename from modules/bcf/spec/lib/open_project/bcf/bcf_json/viewpoint_mapper_spec.rb rename to modules/bcf/spec/lib/open_project/bcf/bcf_json/viewpoint_reader_spec.rb index 60fa0e2725f..a07d03a03a8 100644 --- a/modules/bcf/spec/lib/open_project/bcf/bcf_json/viewpoint_mapper_spec.rb +++ b/modules/bcf/spec/lib/open_project/bcf/bcf_json/viewpoint_reader_spec.rb @@ -27,9 +27,9 @@ #++ require 'spec_helper' -require_relative './viewpoint_mapper_shared_examples.rb' +require_relative './viewpoint_reader_shared_examples.rb' -describe OpenProject::Bcf::BcfJson::ViewpointMapper do +describe OpenProject::Bcf::BcfJson::ViewpointReader do let(:instance) { described_class.new xml_viewpoint.uuid, xml_viewpoint.viewpoint } subject { instance.result } diff --git a/modules/bcf/spec/lib/open_project/bcf/bcf_xml/viewpoint_writer_spec.rb b/modules/bcf/spec/lib/open_project/bcf/bcf_xml/viewpoint_writer_spec.rb new file mode 100644 index 00000000000..17fca97b2e1 --- /dev/null +++ b/modules/bcf/spec/lib/open_project/bcf/bcf_xml/viewpoint_writer_spec.rb @@ -0,0 +1,96 @@ +#-- copyright +# OpenProject is a project management system. +# Copyright (C) 2012-2019 the OpenProject Foundation (OPF) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require 'spec_helper' +require 'compare-xml' + +describe OpenProject::Bcf::BcfXml::ViewpointWriter do + let(:writer_instance) { described_class.new json_resource } + let(:reader_instance) { ::OpenProject::Bcf::BcfJson::ViewpointReader.new xml_resource.uuid, subject.to_xml } + let(:xml_comparison) { Nokogiri::XML(xml_resource.viewpoint) } + let(:json_comparison) { json_resource.raw_json_viewpoint } + + subject { writer_instance.doc } + + shared_examples 'converts back to xml' do + it 'the output of writer is XML-equal to the provided XML viewpoint' do + results = CompareXML.equivalent?( + subject, + xml_comparison, + collapse_whitespace: false, + verbose: true + ) + + if results.length > 0 + puts subject.to_xml + raise "Expected documents to be equal. Found diffs:\n#{results.join("\n")}" + end + end + + it 'contains the root node' do + expect(writer_instance.doc.at('VisualizationInfo')).to be_present + end + + it 'goes full circle comparing back to JSON' do + expect(reader_instance.to_json).to be_json_eql json_comparison + end + end + + describe 'with minimal example' do + let_it_be(:json_resource) do + FactoryBot.build_stubbed :bcf_viewpoint, uuid: '{{UUID}}', viewpoint_name: 'minimal.bcfv' + end + let_it_be(:xml_resource) do + FactoryBot.build_stubbed :xml_viewpoint, uuid: '{{UUID}}', viewpoint_name: 'minimal.bcfv' + end + + it_behaves_like 'converts back to xml' + end + + describe 'with full viewpoint' do + let_it_be(:json_resource) do + FactoryBot.build_stubbed :bcf_viewpoint, uuid: '{{UUID}}', viewpoint_name: 'full_viewpoint.bcfv' + end + let_it_be(:xml_resource) do + FactoryBot.build_stubbed :xml_viewpoint, uuid: '{{UUID}}', viewpoint_name: 'full_viewpoint.bcfv' + end + + it_behaves_like 'converts back to xml' + end + + describe 'with real-world neuhaus_sc_1 example' do + let_it_be(:json_resource) do + FactoryBot.build_stubbed :bcf_viewpoint, uuid: '{{UUID}}', viewpoint_name: 'neubau_sc_1.bcfv' + end + let_it_be(:xml_resource) do + FactoryBot.build_stubbed :xml_viewpoint, uuid: '{{UUID}}', viewpoint_name: 'neubau_sc_1_fixed.bcfv' + end + + it_behaves_like 'converts back to xml' + end +end diff --git a/modules/bcf/spec/requests/api/bcf/v2_1/viewpoints_api_spec.rb b/modules/bcf/spec/requests/api/bcf/v2_1/viewpoints_api_spec.rb index 1c6ee45f06f..40c45a50ac8 100644 --- a/modules/bcf/spec/requests/api/bcf/v2_1/viewpoints_api_spec.rb +++ b/modules/bcf/spec/requests/api/bcf/v2_1/viewpoints_api_spec.rb @@ -54,7 +54,7 @@ describe 'BCF 2.1 viewpoints resource', type: :request, content_type: :json, wit shared_let(:bcf_issue) { FactoryBot.create(:bcf_issue_with_viewpoint, work_package: work_package) } let(:viewpoint) { bcf_issue.viewpoints.first } - let(:viewpoint_json) { ::OpenProject::Bcf::BcfJson::ViewpointMapper.new(viewpoint).result } + let(:viewpoint_json) { ::OpenProject::Bcf::BcfJson::ViewpointReader.new(viewpoint).result } subject(:response) { last_response } describe 'GET /api/bcf/2.1/projects/:project_id/topics/:topic/viewpoints' do