From: mmd-osm Date: Sat, 28 Dec 2019 08:54:37 +0000 (+0100) Subject: API JSON output X-Git-Tag: live~2805^2~9 X-Git-Url: https://git.openstreetmap.org./rails.git/commitdiff_plain/75a49786f8bcb76715cf4009ba807a844baae282?hp=0db30c611a99d62381217d86c5a0bd9d104efb42 API JSON output --- diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 06a9c76f0..080b7b3a5 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -199,6 +199,9 @@ Style/FormatStringToken: - 'test/controllers/api/relations_controller_test.rb' - 'test/controllers/api/ways_controller_test.rb' - 'test/lib/bounding_box_test.rb' + - 'app/views/api/map/_bounds.json.jbuilder' + - 'app/views/api/nodes/_node.json.jbuilder' + - 'app/views/api/old_nodes/_old_node.json.jbuilder' # Offense count: 539 # Cop supports --auto-correct. diff --git a/app/controllers/api/map_controller.rb b/app/controllers/api/map_controller.rb index b0998f7eb..34257fc99 100644 --- a/app/controllers/api/map_controller.rb +++ b/app/controllers/api/map_controller.rb @@ -5,6 +5,13 @@ module Api before_action :check_api_readable around_action :api_call_handle_error, :api_call_timeout + before_action :default_format_xml + + # Set format to xml unless client requires a specific format + def default_format_xml + request.format = "xml" unless params[:format] + end + # This is probably the most common call of all. It is used for getting the # OSM data for a specified bounding box, usually for editing. First the # bounding box (bbox) is checked to make sure that it is sane. All nodes @@ -90,7 +97,10 @@ module Api response.headers["Content-Disposition"] = "attachment; filename=\"map.osm\"" # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end end end end diff --git a/app/controllers/api/nodes_controller.rb b/app/controllers/api/nodes_controller.rb index 2962ce070..1827603a7 100644 --- a/app/controllers/api/nodes_controller.rb +++ b/app/controllers/api/nodes_controller.rb @@ -13,6 +13,13 @@ module Api before_action :check_api_readable, :except => [:create, :update, :delete] around_action :api_call_handle_error, :api_call_timeout + before_action :default_format_xml + + # Set format to xml unless client requires a specific format + def default_format_xml + request.format = "xml" unless params[:format] + end + # Create a node from XML. def create assert_method :put @@ -32,7 +39,10 @@ module Api if @node.visible # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end else head :gone end @@ -73,7 +83,10 @@ module Api @nodes = Node.find(ids) # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end end end end diff --git a/app/controllers/api/old_controller.rb b/app/controllers/api/old_controller.rb index 862d14716..92c6d9295 100644 --- a/app/controllers/api/old_controller.rb +++ b/app/controllers/api/old_controller.rb @@ -16,6 +16,13 @@ module Api before_action :lookup_old_element, :except => [:history] before_action :lookup_old_element_versions, :only => [:history] + before_action :default_format_xml + + # Set format to xml unless client requires a specific format + def default_format_xml + request.format = "xml" unless params[:format] + end + def history # the .where() method used in the lookup_old_element_versions # call won't throw an error if no records are found, so we have @@ -30,7 +37,10 @@ module Api end # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end end def version @@ -41,7 +51,10 @@ module Api response.last_modified = @old_element.timestamp # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end end end diff --git a/app/controllers/api/relations_controller.rb b/app/controllers/api/relations_controller.rb index ba0dd0c6b..c33840978 100644 --- a/app/controllers/api/relations_controller.rb +++ b/app/controllers/api/relations_controller.rb @@ -11,6 +11,13 @@ module Api before_action :check_api_readable, :except => [:create, :update, :delete] around_action :api_call_handle_error, :api_call_timeout + before_action :default_format_xml + + # Set format to xml unless client requires a specific format + def default_format_xml + request.format = "xml" unless params[:format] + end + def create assert_method :put @@ -26,7 +33,10 @@ module Api response.last_modified = @relation.timestamp if @relation.visible # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end else head :gone end @@ -117,7 +127,10 @@ module Api @relations << relation # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end else head :gone end @@ -133,7 +146,10 @@ module Api @relations = Relation.find(ids) # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end end def relations_for_way @@ -160,7 +176,10 @@ module Api end # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end end end end diff --git a/app/controllers/api/ways_controller.rb b/app/controllers/api/ways_controller.rb index 9af087d83..a721f82f7 100644 --- a/app/controllers/api/ways_controller.rb +++ b/app/controllers/api/ways_controller.rb @@ -11,6 +11,13 @@ module Api before_action :check_api_readable, :except => [:create, :update, :delete] around_action :api_call_handle_error, :api_call_timeout + before_action :default_format_xml + + # Set format to xml unless client requires a specific format + def default_format_xml + request.format = "xml" unless params[:format] + end + def create assert_method :put @@ -28,7 +35,10 @@ module Api if @way.visible # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end else head :gone end @@ -75,7 +85,10 @@ module Api end # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end else head :gone end @@ -93,7 +106,10 @@ module Api @ways = Way.find(ids) # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end end ## @@ -106,7 +122,10 @@ module Api @ways = Way.where(:id => wayids, :visible => true) # Render the result - render :formats => [:xml] + respond_to do |format| + format.xml + format.json + end end end end diff --git a/app/views/api/map/_bounds.json.jbuilder b/app/views/api/map/_bounds.json.jbuilder new file mode 100644 index 000000000..98755900e --- /dev/null +++ b/app/views/api/map/_bounds.json.jbuilder @@ -0,0 +1,6 @@ +json.bounds do + json.minlat format("%.7f", @bounds.min_lat) + json.minlon format("%.7f", @bounds.min_lon) + json.maxlat format("%.7f", @bounds.max_lat) + json.maxlon format("%.7f", @bounds.max_lon) +end diff --git a/app/views/api/map/_root_attributes.json.jbuilder b/app/views/api/map/_root_attributes.json.jbuilder new file mode 100644 index 000000000..d8fbef9fd --- /dev/null +++ b/app/views/api/map/_root_attributes.json.jbuilder @@ -0,0 +1,5 @@ +json.version Settings.api_version +json.generator Settings.generator +json.copyright Settings.copyright_owner +json.attribution Settings.attribution_url +json.license Settings.license_url diff --git a/app/views/api/map/index.json.jbuilder b/app/views/api/map/index.json.jbuilder new file mode 100644 index 000000000..7cc983aef --- /dev/null +++ b/app/views/api/map/index.json.jbuilder @@ -0,0 +1,9 @@ +json.partial! "root_attributes" + +json.partial! "bounds" + +all = @nodes + @ways + @relations + +json.elements(all) do |obj| + json.partial! obj +end diff --git a/app/views/api/nodes/_node.json.jbuilder b/app/views/api/nodes/_node.json.jbuilder new file mode 100644 index 000000000..12cf2fb95 --- /dev/null +++ b/app/views/api/nodes/_node.json.jbuilder @@ -0,0 +1,15 @@ +json.type "node" +json.id node.id +if node.visible + json.lat format("%.7f", node.lat.to_f) + json.lon format("%.7f", node.lon.to_f) +end +json.timestamp node.timestamp.xmlschema +json.version node.version +json.changeset node.changeset_id +json.user node.changeset.user.display_name +json.uid node.changeset.user_id + +json.visible node.visible unless node.visible + +json.tags node.tags unless node.tags.empty? diff --git a/app/views/api/nodes/index.json.jbuilder b/app/views/api/nodes/index.json.jbuilder new file mode 100644 index 000000000..f42f7563b --- /dev/null +++ b/app/views/api/nodes/index.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements(@nodes) do |node| + json.partial! node +end diff --git a/app/views/api/nodes/show.json.jbuilder b/app/views/api/nodes/show.json.jbuilder new file mode 100644 index 000000000..c87777a87 --- /dev/null +++ b/app/views/api/nodes/show.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements([@node]) do |node| + json.partial! node +end diff --git a/app/views/api/old_nodes/_old_node.json.jbuilder b/app/views/api/old_nodes/_old_node.json.jbuilder new file mode 100644 index 000000000..5042c84a7 --- /dev/null +++ b/app/views/api/old_nodes/_old_node.json.jbuilder @@ -0,0 +1,15 @@ +json.type "node" +json.id old_node.node_id +if old_node.visible + json.lat format("%.7f", old_node.lat.to_f) + json.lon format("%.7f", old_node.lon.to_f) +end +json.timestamp old_node.timestamp.xmlschema +json.version old_node.version +json.changeset old_node.changeset_id +json.user old_node.changeset.user.display_name +json.uid old_node.changeset.user_id + +json.visible old_node.visible unless old_node.visible + +json.tags old_node.tags unless old_node.tags.empty? diff --git a/app/views/api/old_nodes/history.json.jbuilder b/app/views/api/old_nodes/history.json.jbuilder new file mode 100644 index 000000000..b0e97e832 --- /dev/null +++ b/app/views/api/old_nodes/history.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements(@elems) do |old_node| + json.partial! old_node +end diff --git a/app/views/api/old_nodes/version.json.jbuilder b/app/views/api/old_nodes/version.json.jbuilder new file mode 100644 index 000000000..992230bc0 --- /dev/null +++ b/app/views/api/old_nodes/version.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements([@old_element]) do |old_node| + json.partial! old_node +end diff --git a/app/views/api/old_relations/_old_relation.json.jbuilder b/app/views/api/old_relations/_old_relation.json.jbuilder new file mode 100644 index 000000000..c52ca20de --- /dev/null +++ b/app/views/api/old_relations/_old_relation.json.jbuilder @@ -0,0 +1,19 @@ +json.type "relation" +json.id old_relation.relation_id +json.timestamp old_relation.timestamp.xmlschema +json.version old_relation.version +json.changeset old_relation.changeset_id +json.user old_relation.changeset.user.display_name +json.uid old_relation.changeset.user_id + +json.visible old_relation.visible unless old_relation.visible + +unless old_relation.relation_members.empty? + json.members(old_relation.relation_members) do |m| + json.type m.member_type.downcase + json.ref m.member_id + json.role m.member_role + end +end + +json.tags old_relation.tags unless old_relation.tags.empty? diff --git a/app/views/api/old_relations/history.json.jbuilder b/app/views/api/old_relations/history.json.jbuilder new file mode 100644 index 000000000..c5dacae0a --- /dev/null +++ b/app/views/api/old_relations/history.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements(@elems) do |old_relation| + json.partial! old_relation +end diff --git a/app/views/api/old_relations/version.json.jbuilder b/app/views/api/old_relations/version.json.jbuilder new file mode 100644 index 000000000..b4018429b --- /dev/null +++ b/app/views/api/old_relations/version.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements([@old_element]) do |old_relation| + json.partial! old_relation +end diff --git a/app/views/api/old_ways/_old_way.json.jbuilder b/app/views/api/old_ways/_old_way.json.jbuilder new file mode 100644 index 000000000..b2e79f879 --- /dev/null +++ b/app/views/api/old_ways/_old_way.json.jbuilder @@ -0,0 +1,13 @@ +json.type "way" +json.id old_way.way_id +json.timestamp old_way.timestamp.xmlschema +json.version old_way.version +json.changeset old_way.changeset_id +json.user old_way.changeset.user.display_name +json.uid old_way.changeset.user_id + +json.visible old_way.visible unless old_way.visible + +json.nodes old_way.nds unless old_way.nds.empty? + +json.tags old_way.tags unless old_way.tags.empty? diff --git a/app/views/api/old_ways/history.json.jbuilder b/app/views/api/old_ways/history.json.jbuilder new file mode 100644 index 000000000..b231d7de1 --- /dev/null +++ b/app/views/api/old_ways/history.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements(@elems) do |old_way| + json.partial! old_way +end diff --git a/app/views/api/old_ways/version.json.jbuilder b/app/views/api/old_ways/version.json.jbuilder new file mode 100644 index 000000000..bb261c1c5 --- /dev/null +++ b/app/views/api/old_ways/version.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements([@old_element]) do |old_way| + json.partial! old_way +end diff --git a/app/views/api/relations/_relation.json.jbuilder b/app/views/api/relations/_relation.json.jbuilder new file mode 100644 index 000000000..52263ce37 --- /dev/null +++ b/app/views/api/relations/_relation.json.jbuilder @@ -0,0 +1,19 @@ +json.type "relation" +json.id relation.id +json.timestamp relation.timestamp.xmlschema +json.version relation.version +json.changeset relation.changeset_id +json.user relation.changeset.user.display_name +json.uid relation.changeset.user_id + +json.visible relation.visible unless relation.visible + +unless relation.relation_members.empty? + json.members(relation.relation_members) do |m| + json.type m.member_type.downcase + json.ref m.member_id + json.role m.member_role + end +end + +json.tags relation.tags unless relation.tags.empty? diff --git a/app/views/api/relations/full.json.jbuilder b/app/views/api/relations/full.json.jbuilder new file mode 100644 index 000000000..268de01ca --- /dev/null +++ b/app/views/api/relations/full.json.jbuilder @@ -0,0 +1,7 @@ +json.partial! "api/map/root_attributes" + +all = @nodes + @ways + @relations + +json.elements(all) do |obj| + json.partial! obj +end diff --git a/app/views/api/relations/index.json.jbuilder b/app/views/api/relations/index.json.jbuilder new file mode 100644 index 000000000..25c54fc6f --- /dev/null +++ b/app/views/api/relations/index.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements(@relations) do |relation| + json.partial! relation +end diff --git a/app/views/api/relations/relations_for_node.json.jbuilder b/app/views/api/relations/relations_for_node.json.jbuilder new file mode 100644 index 000000000..25c54fc6f --- /dev/null +++ b/app/views/api/relations/relations_for_node.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements(@relations) do |relation| + json.partial! relation +end diff --git a/app/views/api/relations/relations_for_relation.json.jbuilder b/app/views/api/relations/relations_for_relation.json.jbuilder new file mode 100644 index 000000000..25c54fc6f --- /dev/null +++ b/app/views/api/relations/relations_for_relation.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements(@relations) do |relation| + json.partial! relation +end diff --git a/app/views/api/relations/relations_for_way.json.jbuilder b/app/views/api/relations/relations_for_way.json.jbuilder new file mode 100644 index 000000000..25c54fc6f --- /dev/null +++ b/app/views/api/relations/relations_for_way.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements(@relations) do |relation| + json.partial! relation +end diff --git a/app/views/api/relations/show.json.jbuilder b/app/views/api/relations/show.json.jbuilder new file mode 100644 index 000000000..682ef0a24 --- /dev/null +++ b/app/views/api/relations/show.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements([@relation]) do |relation| + json.partial! relation +end diff --git a/app/views/api/ways/_way.json.jbuilder b/app/views/api/ways/_way.json.jbuilder new file mode 100644 index 000000000..11e796bc8 --- /dev/null +++ b/app/views/api/ways/_way.json.jbuilder @@ -0,0 +1,13 @@ +json.type "way" +json.id way.id +json.timestamp way.timestamp.xmlschema +json.version way.version +json.changeset way.changeset_id +json.user way.changeset.user.display_name +json.uid way.changeset.user_id + +json.visible way.visible unless way.visible + +json.nodes way.nodes.ids unless way.nodes.ids.empty? + +json.tags way.tags unless way.tags.empty? diff --git a/app/views/api/ways/full.json.jbuilder b/app/views/api/ways/full.json.jbuilder new file mode 100644 index 000000000..9bd234fbe --- /dev/null +++ b/app/views/api/ways/full.json.jbuilder @@ -0,0 +1,7 @@ +json.partial! "api/map/root_attributes" + +all = @nodes + [@way] + +json.elements(all) do |obj| + json.partial! obj +end diff --git a/app/views/api/ways/index.json.jbuilder b/app/views/api/ways/index.json.jbuilder new file mode 100644 index 000000000..e1dbd4347 --- /dev/null +++ b/app/views/api/ways/index.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements(@ways) do |way| + json.partial! way +end diff --git a/app/views/api/ways/show.json.jbuilder b/app/views/api/ways/show.json.jbuilder new file mode 100644 index 000000000..e5cd31fed --- /dev/null +++ b/app/views/api/ways/show.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements([@way]) do |way| + json.partial! way +end diff --git a/app/views/api/ways/ways_for_node.json.jbuilder b/app/views/api/ways/ways_for_node.json.jbuilder new file mode 100644 index 000000000..e1dbd4347 --- /dev/null +++ b/app/views/api/ways/ways_for_node.json.jbuilder @@ -0,0 +1,5 @@ +json.partial! "api/map/root_attributes" + +json.elements(@ways) do |way| + json.partial! way +end diff --git a/test/controllers/api/map_controller_test.rb b/test/controllers/api/map_controller_test.rb index db83aaa9f..5df2dc6a3 100644 --- a/test/controllers/api/map_controller_test.rb +++ b/test/controllers/api/map_controller_test.rb @@ -23,6 +23,10 @@ module Api { :path => "/api/0.6/map", :method => :get }, { :controller => "api/map", :action => "index" } ) + assert_routing( + { :path => "/api/0.6/map.json", :method => :get }, + { :controller => "api/map", :action => "index", :format => "json" } + ) end # ------------------------------------- @@ -63,6 +67,54 @@ module Api end end + def test_map_json + node = create(:node, :lat => 7, :lon => 7) + tag = create(:node_tag, :node => node) + way1 = create(:way_node, :node => node).way + way2 = create(:way_node, :node => node).way + relation = create(:relation_member, :member => node).relation + + # Need to split the min/max lat/lon out into their own variables here + # so that we can test they are returned later. + minlon = node.lon - 0.1 + minlat = node.lat - 0.1 + maxlon = node.lon + 0.1 + maxlat = node.lat + 0.1 + bbox = "#{minlon},#{minlat},#{maxlon},#{maxlat}" + get :index, :params => { :bbox => bbox, :format => "json" } + if $VERBOSE + print @request.to_yaml + print @response.body + end + assert_response :success, "Expected success with the map call" + js = ActiveSupport::JSON.decode(@response.body) + assert_not_nil js + + assert_equal Settings.api_version, js["version"] + assert_equal Settings.generator, js["generator"] + assert_equal "#{format('%.7f', minlon)}", js["bounds"]["minlon"] + assert_equal "#{format('%.7f', minlat)}", js["bounds"]["minlat"] + assert_equal "#{format('%.7f', maxlon)}", js["bounds"]["maxlon"] + assert_equal "#{format('%.7f', maxlat)}", js["bounds"]["maxlat"] + + result_nodes = js["elements"].select { |a| a["type"] == "node" } + .select { |a| a["id"] == node.id } + .select { |a| a["lat"] == "#{format('%.7f', node.lat)}" } + .select { |a| a["lon"] == "#{format('%.7f', node.lon)}" } + .select { |a| a["version"] == node.version } + .select { |a| a["changeset"] == node.changeset_id } + .select { |a| a["timestamp"] == node.timestamp.xmlschema } + assert_equal result_nodes.count, 1 + result_node = result_nodes.first + + assert_equal result_node["tags"], tag.k => tag.v + assert_equal 2, (js["elements"].count { |a| a["type"] == "way" }) + assert_equal 1, (js["elements"].count { |a| a["type"] == "way" && a["id"] == way1.id }) + assert_equal 1, (js["elements"].count { |a| a["type"] == "way" && a["id"] == way2.id }) + assert_equal 1, (js["elements"].count { |a| a["type"] == "relation" }) + assert_equal 1, (js["elements"].count { |a| a["type"] == "relation" && a["id"] == relation.id }) + end + # This differs from the above test in that we are making the bbox exactly # the same as the node we are looking at def test_map_inclusive