can [:read, :feed, :search], Note
can :create, Note unless user
- can [:read, :download], Changeset
+ can :read, Changeset
can :read, ChangesetComment
can :read, Tracepoint
can :read, User
--- /dev/null
+module Api
+ module Changesets
+ class DownloadsController < ApiController
+ authorize_resource :changeset
+
+ before_action :set_request_formats
+
+ ##
+ # download the changeset as an osmChange document.
+ #
+ # to make it easier to revert diffs it would be better if the osmChange
+ # format were reversible, i.e: contained both old and new versions of
+ # modified elements. but it doesn't at the moment...
+ #
+ # this method cannot order the database changes fully (i.e: timestamp and
+ # version number may be too coarse) so the resulting diff may not apply
+ # to a different database. however since changesets are not atomic this
+ # behaviour cannot be guaranteed anyway and is the result of a design
+ # choice.
+ def show
+ changeset = Changeset.find(params[:changeset_id])
+
+ # get all the elements in the changeset which haven't been redacted
+ # and stick them in a big array.
+ elements = [changeset.old_nodes.unredacted,
+ changeset.old_ways.unredacted,
+ changeset.old_relations.unredacted].flatten
+
+ # sort the elements by timestamp and version number, as this is the
+ # almost sensible ordering available. this would be much nicer if
+ # global (SVN-style) versioning were used - then that would be
+ # unambiguous.
+ elements.sort_by! { |e| [e.timestamp, e.version] }
+
+ # generate an output element for each operation. note: we avoid looking
+ # at the history because it is simpler - but it would be more correct to
+ # check these assertions.
+ @created = []
+ @modified = []
+ @deleted = []
+
+ elements.each do |elt|
+ if elt.version == 1
+ # first version, so it must be newly-created.
+ @created << elt
+ elsif elt.visible
+ # must be a modify
+ @modified << elt
+ else
+ # if the element isn't visible then it must have been deleted
+ @deleted << elt
+ end
+ end
+
+ respond_to do |format|
+ format.xml
+ end
+ end
+ end
+ end
+end
end
end
- ##
- # download the changeset as an osmChange document.
- #
- # to make it easier to revert diffs it would be better if the osmChange
- # format were reversible, i.e: contained both old and new versions of
- # modified elements. but it doesn't at the moment...
- #
- # this method cannot order the database changes fully (i.e: timestamp and
- # version number may be too coarse) so the resulting diff may not apply
- # to a different database. however since changesets are not atomic this
- # behaviour cannot be guaranteed anyway and is the result of a design
- # choice.
- def download
- changeset = Changeset.find(params[:id])
-
- # get all the elements in the changeset which haven't been redacted
- # and stick them in a big array.
- elements = [changeset.old_nodes.unredacted,
- changeset.old_ways.unredacted,
- changeset.old_relations.unredacted].flatten
-
- # sort the elements by timestamp and version number, as this is the
- # almost sensible ordering available. this would be much nicer if
- # global (SVN-style) versioning were used - then that would be
- # unambiguous.
- elements.sort_by! { |e| [e.timestamp, e.version] }
-
- # generate an output element for each operation. note: we avoid looking
- # at the history because it is simpler - but it would be more correct to
- # check these assertions.
- @created = []
- @modified = []
- @deleted = []
-
- elements.each do |elt|
- if elt.version == 1
- # first version, so it must be newly-created.
- @created << elt
- elsif elt.visible
- # must be a modify
- @modified << elt
- else
- # if the element isn't visible then it must have been deleted
- @deleted << elt
- end
- end
-
- respond_to do |format|
- format.xml
- end
- end
-
##
# updates a changeset's tags. none of the changeset's attributes are
# user-modifiable, so they will be ignored.
xml.osmChange(OSM::API.new.xml_root_attributes) do |osm|
@created.each do |elt|
osm.create do |create|
- create << render(elt)
+ create << render(:partial => "api/#{elt.to_partial_path}", :object => elt)
end
end
@modified.each do |elt|
osm.modify do |modify|
- modify << render(elt)
+ modify << render(:partial => "api/#{elt.to_partial_path}", :object => elt)
end
end
@deleted.each do |elt|
osm.delete do |delete|
- delete << render(elt)
+ delete << render(:partial => "api/#{elt.to_partial_path}", :object => elt)
end
end
end
:href => api_changeset_url(changeset, :only_path => false),
:type => "application/osm+xml"
entry.link :rel => "alternate",
- :href => changeset_download_url(changeset, :only_path => false),
+ :href => api_changeset_download_url(changeset, :only_path => false),
:type => "application/osmChange+xml"
if !changeset.tags.empty? && changeset.tags.key?("comment")
<div class='secondary-actions'>
<%= link_to t(".changesetxml"), api_changeset_path(@changeset) %>
·
- <%= link_to(t(".osmchangexml"), :controller => "api/changesets", :action => "download") %>
+ <%= link_to t(".osmchangexml"), api_changeset_download_path(@changeset) %>
</div>
<% if @next_by_user || @prev_by_user %>
get "permissions" => "permissions#show"
post "changeset/:id/upload" => "changesets#upload", :as => :changeset_upload, :id => /\d+/
- get "changeset/:id/download" => "changesets#download", :as => :changeset_download, :id => /\d+/
post "changeset/:id/subscribe" => "changesets#subscribe", :as => :api_changeset_subscribe, :id => /\d+/
post "changeset/:id/unsubscribe" => "changesets#unsubscribe", :as => :api_changeset_unsubscribe, :id => /\d+/
put "changeset/:id/close" => "changesets#close", :as => :changeset_close, :id => /\d+/
namespace :api, :path => "api/0.6" do
resources :changesets, :only => [:index, :create]
- resources :changesets, :path => "changeset", :id => /\d+/, :only => [:show, :update]
+ resources :changesets, :path => "changeset", :id => /\d+/, :only => [:show, :update] do
+ resource :download, :module => :changesets, :only => :show
+ end
put "changeset/create" => "changesets#create", :as => nil
resources :changeset_comments, :only => :index
--- /dev/null
+require "test_helper"
+
+module Api
+ module Changesets
+ class DownloadsControllerTest < ActionDispatch::IntegrationTest
+ ##
+ # test all routes which lead to this controller
+ def test_routes
+ assert_routing(
+ { :path => "/api/0.6/changeset/1/download", :method => :get },
+ { :controller => "api/changesets/downloads", :action => "show", :changeset_id => "1" }
+ )
+ end
+
+ def test_show
+ changeset = create(:changeset)
+ node = create(:node, :with_history, :version => 1, :changeset => changeset)
+ tag = create(:old_node_tag, :old_node => node.old_nodes.find_by(:version => 1))
+ node2 = create(:node, :with_history, :version => 1, :changeset => changeset)
+ _node3 = create(:node, :with_history, :deleted, :version => 1, :changeset => changeset)
+ _relation = create(:relation, :with_history, :version => 1, :changeset => changeset)
+ _relation2 = create(:relation, :with_history, :deleted, :version => 1, :changeset => changeset)
+
+ get api_changeset_download_path(changeset)
+
+ assert_response :success
+ # FIXME: needs more assert_select tests
+ assert_select "osmChange[version='#{Settings.api_version}'][generator='#{Settings.generator}']" do
+ assert_select "create", :count => 5
+ assert_select "create>node[id='#{node.id}'][visible='#{node.visible?}'][version='#{node.version}']" do
+ assert_select "tag[k='#{tag.k}'][v='#{tag.v}']"
+ end
+ assert_select "create>node[id='#{node2.id}']"
+ end
+ end
+
+ def test_show_should_sort_by_timestamp
+ changeset = create(:changeset)
+ node1 = create(:old_node, :version => 2, :timestamp => "2020-02-01", :changeset => changeset)
+ node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
+
+ get api_changeset_download_path(changeset)
+
+ assert_response :success
+ assert_dom "modify", :count => 2 do |modify|
+ assert_dom modify[0], ">node", :count => 1 do |node|
+ assert_dom node, ">@id", node0.node_id.to_s
+ end
+ assert_dom modify[1], ">node", :count => 1 do |node|
+ assert_dom node, ">@id", node1.node_id.to_s
+ end
+ end
+ end
+
+ def test_show_should_sort_by_version
+ changeset = create(:changeset)
+ node1 = create(:old_node, :version => 3, :timestamp => "2020-01-01", :changeset => changeset)
+ node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
+
+ get api_changeset_download_path(changeset)
+
+ assert_response :success
+ assert_dom "modify", :count => 2 do |modify|
+ assert_dom modify[0], ">node", :count => 1 do |node|
+ assert_dom node, ">@id", node0.node_id.to_s
+ end
+ assert_dom modify[1], ">node", :count => 1 do |node|
+ assert_dom node, ">@id", node1.node_id.to_s
+ end
+ end
+ end
+
+ ##
+ # check that the changeset download for a changeset with a redacted
+ # element in it doesn't contain that element.
+ def test_show_redacted
+ changeset = create(:changeset)
+ node = create(:node, :with_history, :version => 2, :changeset => changeset)
+ node_v1 = node.old_nodes.find_by(:version => 1)
+ node_v1.redact!(create(:redaction))
+
+ get api_changeset_download_path(changeset)
+ assert_response :success
+
+ assert_select "osmChange", 1
+ # this changeset contains the node in versions 1 & 2, but 1 should
+ # be hidden.
+ assert_select "osmChange node[id='#{node.id}']", 1
+ assert_select "osmChange node[id='#{node.id}'][version='1']", 0
+ end
+ end
+ end
+end
{ :path => "/api/0.6/changeset/1/upload", :method => :post },
{ :controller => "api/changesets", :action => "upload", :id => "1" }
)
- assert_routing(
- { :path => "/api/0.6/changeset/1/download", :method => :get },
- { :controller => "api/changesets", :action => "download", :id => "1" }
- )
assert_routing(
{ :path => "/api/0.6/changeset/1/subscribe", :method => :post },
{ :controller => "api/changesets", :action => "subscribe", :id => "1" }
assert_response :success,
"can't upload multiple versions of an element in a diff: #{@response.body}"
- get changeset_download_path(changeset_id)
+ get api_changeset_download_path(changeset_id)
assert_response :success
assert_select "osmChange", 1
assert_response :success,
"can't upload a diff from JOSM: #{@response.body}"
- get changeset_download_path(changeset_id)
+ get api_changeset_download_path(changeset_id)
assert_response :success
assert_select "osmChange", 1
assert_response :success,
"can't upload multiple versions of an element in a diff: #{@response.body}"
- get changeset_download_path(changeset_id)
+ get api_changeset_download_path(changeset_id)
assert_response :success
assert_select "osmChange", 1
assert_select "osmChange>modify>way", 1
end
- def test_changeset_download
- changeset = create(:changeset)
- node = create(:node, :with_history, :version => 1, :changeset => changeset)
- tag = create(:old_node_tag, :old_node => node.old_nodes.find_by(:version => 1))
- node2 = create(:node, :with_history, :version => 1, :changeset => changeset)
- _node3 = create(:node, :with_history, :deleted, :version => 1, :changeset => changeset)
- _relation = create(:relation, :with_history, :version => 1, :changeset => changeset)
- _relation2 = create(:relation, :with_history, :deleted, :version => 1, :changeset => changeset)
-
- get changeset_download_path(changeset)
-
- assert_response :success
-
- # FIXME: needs more assert_select tests
- assert_select "osmChange[version='#{Settings.api_version}'][generator='#{Settings.generator}']" do
- assert_select "create", :count => 5
- assert_select "create>node[id='#{node.id}'][visible='#{node.visible?}'][version='#{node.version}']" do
- assert_select "tag[k='#{tag.k}'][v='#{tag.v}']"
- end
- assert_select "create>node[id='#{node2.id}']"
- end
- end
-
- test "sorts downloaded elements by timestamp" do
- changeset = create(:changeset)
- node1 = create(:old_node, :version => 2, :timestamp => "2020-02-01", :changeset => changeset)
- node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
-
- get changeset_download_path(changeset)
- assert_response :success
- assert_dom "modify", :count => 2 do |modify|
- assert_dom modify[0], ">node", :count => 1 do |node|
- assert_dom node, ">@id", node0.node_id.to_s
- end
- assert_dom modify[1], ">node", :count => 1 do |node|
- assert_dom node, ">@id", node1.node_id.to_s
- end
- end
- end
-
- test "sorts downloaded elements by version" do
- changeset = create(:changeset)
- node1 = create(:old_node, :version => 3, :timestamp => "2020-01-01", :changeset => changeset)
- node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
-
- get changeset_download_path(changeset)
- assert_response :success
- assert_dom "modify", :count => 2 do |modify|
- assert_dom modify[0], ">node", :count => 1 do |node|
- assert_dom node, ">@id", node0.node_id.to_s
- end
- assert_dom modify[1], ">node", :count => 1 do |node|
- assert_dom node, ">@id", node1.node_id.to_s
- end
- end
- end
-
##
# check that the bounding box of a changeset gets updated correctly
# FIXME: This should really be moded to a integration test due to the with_controller
"element limit.")
end
- ##
- # check that the changeset download for a changeset with a redacted
- # element in it doesn't contain that element.
- def test_diff_download_redacted
- changeset = create(:changeset)
- node = create(:node, :with_history, :version => 2, :changeset => changeset)
- node_v1 = node.old_nodes.find_by(:version => 1)
- node_v1.redact!(create(:redaction))
-
- get changeset_download_path(changeset)
- assert_response :success
-
- assert_select "osmChange", 1
- # this changeset contains the node in versions 1 & 2, but 1 should
- # be hidden.
- assert_select "osmChange node[id='#{node.id}']", 1
- assert_select "osmChange node[id='#{node.id}'][version='1']", 0
- end
-
##
# test subscribe success
def test_subscribe_success