Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split api_controller actions into their own controllers #2160

Merged
merged 6 commits into from
Feb 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion app/abilities/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ class Ability
include CanCan::Ability

def initialize(user)
can [:trackpoints, :map, :changes, :capabilities, :permissions], :api
can [:relation, :relation_history, :way, :way_history, :node, :node_history,
:changeset, :note, :new_note, :query], :browse
can :show, :capability
can :index, :change
can [:index, :feed, :show, :download, :query], Changeset
can :index, ChangesetComment
can :search, :direction
Expand All @@ -15,12 +16,15 @@ def initialize(user)
can [:finish, :embed], :export
can [:search, :search_latlon, :search_ca_postcode, :search_osm_nominatim,
:search_geonames, :search_osm_nominatim_reverse, :search_geonames_reverse], :geocoder
can :index, :map
can [:index, :create, :comment, :feed, :show, :search, :mine], Note
can [:token, :request_token, :access_token, :test_request], :oauth
can :show, :permission
can [:index, :show], Redaction
can [:search_all, :search_nodes, :search_ways, :search_relations], :search
can [:trackpoints], :swf
can [:index, :show, :data, :georss, :picture, :icon], Trace
can :index, Tracepoint
can [:terms, :api_users, :login, :logout, :new, :create, :save, :confirm, :confirm_resend, :confirm_email, :lost_password, :reset_password, :show, :api_read, :auth_success, :auth_failure], User
can [:index, :show, :blocks_on, :blocks_by], UserBlock
can [:index, :show], Node
Expand Down
21 changes: 21 additions & 0 deletions app/controllers/api/capabilities_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Api
class CapabilitiesController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :api_deny_access_handler

authorize_resource :class => false

around_action :api_call_handle_error, :api_call_timeout

# External apps that use the api are able to query the api to find out some
# parameters of the API. It currently returns:
# * minimum and maximum API versions that can be used.
# * maximum area that can be requested in a bbox request in square degrees
# * number of tracepoints that are returned in each tracepoints page
def show
@database_status = database_status
@api_status = api_status
@gpx_status = gpx_status
end
end
end
57 changes: 57 additions & 0 deletions app/controllers/api/changes_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
module Api
class ChangesController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :api_deny_access_handler

authorize_resource :class => false

before_action :check_api_readable
around_action :api_call_handle_error, :api_call_timeout

# Get a list of the tiles that have changed within a specified time
# period
def index
zoom = (params[:zoom] || "12").to_i

if params.include?(:start) && params.include?(:end)
starttime = Time.parse(params[:start])
endtime = Time.parse(params[:end])
else
hours = (params[:hours] || "1").to_i.hours
endtime = Time.now.getutc
starttime = endtime - hours
end

if zoom >= 1 && zoom <= 16 &&
endtime > starttime && endtime - starttime <= 24.hours
mask = (1 << zoom) - 1

tiles = Node.where(:timestamp => starttime..endtime).group("maptile_for_point(latitude, longitude, #{zoom})").count

doc = OSM::API.new.get_xml_doc
changes = XML::Node.new "changes"
changes["starttime"] = starttime.xmlschema
changes["endtime"] = endtime.xmlschema

tiles.each do |tile, count|
x = (tile.to_i >> zoom) & mask
y = tile.to_i & mask

t = XML::Node.new "tile"
t["x"] = x.to_s
t["y"] = y.to_s
t["z"] = zoom.to_s
t["changes"] = count.to_s

changes << t
end

doc.root << changes

render :xml => doc.to_s
else
render :plain => "Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours", :status => :bad_request
end
end
end
end
107 changes: 107 additions & 0 deletions app/controllers/api/map_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
module Api
class MapController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :api_deny_access_handler

authorize_resource :class => false

before_action :check_api_readable
around_action :api_call_handle_error, :api_call_timeout

# 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
# are searched, then all the ways that reference those nodes are found.
# All Nodes that are referenced by those ways are fetched and added to the list
# of nodes.
# Then all the relations that reference the already found nodes and ways are
# fetched. All the nodes and ways that are referenced by those ways are then
# fetched. Finally all the xml is returned.
def index
# Figure out the bbox
# check boundary is sane and area within defined
# see /config/application.yml
begin
bbox = BoundingBox.from_bbox_params(params)
bbox.check_boundaries
bbox.check_size
rescue StandardError => err
report_error(err.message)
return
end

nodes = Node.bbox(bbox).where(:visible => true).includes(:node_tags).limit(MAX_NUMBER_OF_NODES + 1)

node_ids = nodes.collect(&:id)
if node_ids.length > MAX_NUMBER_OF_NODES
report_error("You requested too many nodes (limit is #{MAX_NUMBER_OF_NODES}). Either request a smaller area, or use planet.osm")
return
end

doc = OSM::API.new.get_xml_doc

# add bounds
doc.root << bbox.add_bounds_to(XML::Node.new("bounds"))

# get ways
# find which ways are needed
ways = []
if node_ids.empty?
list_of_way_nodes = []
else
way_nodes = WayNode.where(:node_id => node_ids)
way_ids = way_nodes.collect { |way_node| way_node.id[0] }
ways = Way.preload(:way_nodes, :way_tags).find(way_ids)

list_of_way_nodes = ways.collect do |way|
way.way_nodes.collect(&:node_id)
end
list_of_way_nodes.flatten!
end

# - [0] in case some thing links to node 0 which doesn't exist. Shouldn't actually ever happen but it does. FIXME: file a ticket for this
nodes_to_fetch = (list_of_way_nodes.uniq - node_ids) - [0]

nodes += Node.includes(:node_tags).find(nodes_to_fetch) unless nodes_to_fetch.empty?

visible_nodes = {}
changeset_cache = {}
user_display_name_cache = {}

nodes.each do |node|
if node.visible?
doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
visible_nodes[node.id] = node
end
end

way_ids = []
ways.each do |way|
if way.visible?
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
way_ids << way.id
end
end

relations = Relation.nodes(visible_nodes.keys).visible +
Relation.ways(way_ids).visible

# we do not normally return the "other" partners referenced by an relation,
# e.g. if we return a way A that is referenced by relation X, and there's
# another way B also referenced, that is not returned. But we do make
# an exception for cases where an relation references another *relation*;
# in that case we return that as well (but we don't go recursive here)
relations += Relation.relations(relations.collect(&:id)).visible

# this "uniq" may be slightly inefficient; it may be better to first collect and output
# all node-related relations, then find the *not yet covered* way-related ones etc.
relations.uniq.each do |relation|
doc.root << relation.to_xml_node(changeset_cache, user_display_name_cache)
end

response.headers["Content-Disposition"] = "attachment; filename=\"map.osm\""

render :xml => doc.to_s
end
end
end
27 changes: 27 additions & 0 deletions app/controllers/api/permissions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module Api
class PermissionsController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :api_deny_access_handler

authorize_resource :class => false

before_action :check_api_readable
before_action :setup_user_auth
around_action :api_call_handle_error, :api_call_timeout

# External apps that use the api are able to query which permissions
# they have. This currently returns a list of permissions granted to the current user:
# * if authenticated via OAuth, this list will contain all permissions granted by the user to the access_token.
# * if authenticated via basic auth all permissions are granted, so the list will contain all permissions.
# * unauthenticated users have no permissions, so the list will be empty.
def show
@permissions = if current_token.present?
ClientApplication.all_permissions.select { |p| current_token.read_attribute(p) }
elsif current_user
ClientApplication.all_permissions
else
[]
end
end
end
end
112 changes: 112 additions & 0 deletions app/controllers/api/tracepoints_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
module Api
class TracepointsController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :api_deny_access_handler

authorize_resource

before_action :check_api_readable
around_action :api_call_handle_error, :api_call_timeout

# Get an XML response containing a list of tracepoints that have been uploaded
# within the specified bounding box, and in the specified page.
def index
# retrieve the page number
page = params["page"].to_s.to_i

unless page >= 0
report_error("Page number must be greater than or equal to 0")
return
end

offset = page * TRACEPOINTS_PER_PAGE

# Figure out the bbox
# check boundary is sane and area within defined
# see /config/application.yml
begin
bbox = BoundingBox.from_bbox_params(params)
bbox.check_boundaries
bbox.check_size
rescue StandardError => err
report_error(err.message)
return
end

# get all the points
ordered_points = Tracepoint.bbox(bbox).joins(:trace).where(:gpx_files => { :visibility => %w[trackable identifiable] }).order("gpx_id DESC, trackid ASC, timestamp ASC")
unordered_points = Tracepoint.bbox(bbox).joins(:trace).where(:gpx_files => { :visibility => %w[public private] }).order("gps_points.latitude", "gps_points.longitude", "gps_points.timestamp")
points = ordered_points.union_all(unordered_points).offset(offset).limit(TRACEPOINTS_PER_PAGE)

doc = XML::Document.new
doc.encoding = XML::Encoding::UTF_8
root = XML::Node.new "gpx"
root["version"] = "1.0"
root["creator"] = "OpenStreetMap.org"
root["xmlns"] = "http://www.topografix.com/GPX/1/0"

doc.root = root

# initialise these variables outside of the loop so that they
# stay in scope and don't get free'd up by the GC during the
# loop.
gpx_id = -1
trackid = -1
track = nil
trkseg = nil
anon_track = nil
anon_trkseg = nil
gpx_file = nil
timestamps = false

points.each do |point|
if gpx_id != point.gpx_id
gpx_id = point.gpx_id
trackid = -1
gpx_file = Trace.find(gpx_id)

if gpx_file.trackable?
track = XML::Node.new "trk"
doc.root << track
timestamps = true

if gpx_file.identifiable?
track << (XML::Node.new("name") << gpx_file.name)
track << (XML::Node.new("desc") << gpx_file.description)
track << (XML::Node.new("url") << url_for(:controller => "/traces", :action => "show", :display_name => gpx_file.user.display_name, :id => gpx_file.id))
end
else
# use the anonymous track segment if the user hasn't allowed
# their GPX points to be tracked.
timestamps = false
if anon_track.nil?
anon_track = XML::Node.new "trk"
doc.root << anon_track
end
track = anon_track
end
end

if trackid != point.trackid
if gpx_file.trackable?
trkseg = XML::Node.new "trkseg"
track << trkseg
trackid = point.trackid
else
if anon_trkseg.nil?
anon_trkseg = XML::Node.new "trkseg"
anon_track << anon_trkseg
end
trkseg = anon_trkseg
end
end

trkseg << point.to_xml_node(timestamps)
end

response.headers["Content-Disposition"] = "attachment; filename=\"tracks.gpx\""

render :xml => doc.to_s
end
end
end
Loading