Merge remote-tracking branch 'upstream/pull/2161'
This commit is contained in:
commit
94b59f4403
88 changed files with 12288 additions and 11991 deletions
|
@ -13,9 +13,10 @@ Lint/AssignmentInCondition:
|
||||||
- 'app/controllers/application_controller.rb'
|
- 'app/controllers/application_controller.rb'
|
||||||
- 'app/controllers/geocoder_controller.rb'
|
- 'app/controllers/geocoder_controller.rb'
|
||||||
- 'app/controllers/notes_controller.rb'
|
- 'app/controllers/notes_controller.rb'
|
||||||
|
- 'app/controllers/api/traces_controller.rb'
|
||||||
- 'app/controllers/traces_controller.rb'
|
- 'app/controllers/traces_controller.rb'
|
||||||
- 'app/controllers/users_controller.rb'
|
- 'app/controllers/users_controller.rb'
|
||||||
- 'app/controllers/user_preferences_controller.rb'
|
- 'app/controllers/api/user_preferences_controller.rb'
|
||||||
- 'app/helpers/application_helper.rb'
|
- 'app/helpers/application_helper.rb'
|
||||||
- 'app/helpers/browse_helper.rb'
|
- 'app/helpers/browse_helper.rb'
|
||||||
- 'app/helpers/browse_tags_helper.rb'
|
- 'app/helpers/browse_tags_helper.rb'
|
||||||
|
@ -28,7 +29,7 @@ Lint/AssignmentInCondition:
|
||||||
# Offense count: 4
|
# Offense count: 4
|
||||||
Lint/HandleExceptions:
|
Lint/HandleExceptions:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/controllers/amf_controller.rb'
|
- 'app/controllers/api/amf_controller.rb'
|
||||||
- 'app/controllers/users_controller.rb'
|
- 'app/controllers/users_controller.rb'
|
||||||
|
|
||||||
# Offense count: 692
|
# Offense count: 692
|
||||||
|
@ -178,7 +179,7 @@ Style/FrozenStringLiteralComment:
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Style/IfUnlessModifier:
|
Style/IfUnlessModifier:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/controllers/ways_controller.rb'
|
- 'app/controllers/api/ways_controller.rb'
|
||||||
|
|
||||||
# Offense count: 70
|
# Offense count: 70
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
|
File diff suppressed because it is too large
Load diff
1002
app/controllers/api/amf_controller.rb
Normal file
1002
app/controllers/api/amf_controller.rb
Normal file
File diff suppressed because it is too large
Load diff
85
app/controllers/api/changeset_comments_controller.rb
Normal file
85
app/controllers/api/changeset_comments_controller.rb
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
module Api
|
||||||
|
class ChangesetCommentsController < ApplicationController
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
before_action :authorize
|
||||||
|
before_action :api_deny_access_handler
|
||||||
|
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
|
before_action :require_public_data, :only => [:create]
|
||||||
|
before_action :check_api_writable
|
||||||
|
before_action :check_api_readable, :except => [:create]
|
||||||
|
around_action :api_call_handle_error
|
||||||
|
around_action :api_call_timeout
|
||||||
|
|
||||||
|
##
|
||||||
|
# Add a comment to a changeset
|
||||||
|
def create
|
||||||
|
# Check the arguments are sane
|
||||||
|
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
||||||
|
raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
|
||||||
|
|
||||||
|
# Extract the arguments
|
||||||
|
id = params[:id].to_i
|
||||||
|
body = params[:text]
|
||||||
|
|
||||||
|
# Find the changeset and check it is valid
|
||||||
|
changeset = Changeset.find(id)
|
||||||
|
raise OSM::APIChangesetNotYetClosedError, changeset if changeset.is_open?
|
||||||
|
|
||||||
|
# Add a comment to the changeset
|
||||||
|
comment = changeset.comments.create(:changeset => changeset,
|
||||||
|
:body => body,
|
||||||
|
:author => current_user)
|
||||||
|
|
||||||
|
# Notify current subscribers of the new comment
|
||||||
|
changeset.subscribers.visible.each do |user|
|
||||||
|
Notifier.changeset_comment_notification(comment, user).deliver_later if current_user != user
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add the commenter to the subscribers if necessary
|
||||||
|
changeset.subscribers << current_user unless changeset.subscribers.exists?(current_user.id)
|
||||||
|
|
||||||
|
# Return a copy of the updated changeset
|
||||||
|
render :xml => changeset.to_xml.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Sets visible flag on comment to false
|
||||||
|
def destroy
|
||||||
|
# Check the arguments are sane
|
||||||
|
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
||||||
|
|
||||||
|
# Extract the arguments
|
||||||
|
id = params[:id].to_i
|
||||||
|
|
||||||
|
# Find the changeset
|
||||||
|
comment = ChangesetComment.find(id)
|
||||||
|
|
||||||
|
# Hide the comment
|
||||||
|
comment.update(:visible => false)
|
||||||
|
|
||||||
|
# Return a copy of the updated changeset
|
||||||
|
render :xml => comment.changeset.to_xml.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Sets visible flag on comment to true
|
||||||
|
def restore
|
||||||
|
# Check the arguments are sane
|
||||||
|
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
||||||
|
|
||||||
|
# Extract the arguments
|
||||||
|
id = params[:id].to_i
|
||||||
|
|
||||||
|
# Find the changeset
|
||||||
|
comment = ChangesetComment.find(id)
|
||||||
|
|
||||||
|
# Unhide the comment
|
||||||
|
comment.update(:visible => true)
|
||||||
|
|
||||||
|
# Return a copy of the updated changeset
|
||||||
|
render :xml => comment.changeset.to_xml.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
422
app/controllers/api/changesets_controller.rb
Normal file
422
app/controllers/api/changesets_controller.rb
Normal file
|
@ -0,0 +1,422 @@
|
||||||
|
# The ChangesetController is the RESTful interface to Changeset objects
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class ChangesetsController < ApplicationController
|
||||||
|
layout "site"
|
||||||
|
require "xml/libxml"
|
||||||
|
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
before_action :authorize, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe]
|
||||||
|
before_action :api_deny_access_handler, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe, :expand_bbox]
|
||||||
|
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
|
before_action :require_public_data, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe]
|
||||||
|
before_action :check_api_writable, :only => [:create, :update, :upload, :subscribe, :unsubscribe]
|
||||||
|
before_action :check_api_readable, :except => [:create, :update, :upload, :download, :query, :subscribe, :unsubscribe]
|
||||||
|
before_action(:only => [:index, :feed]) { |c| c.check_database_readable(true) }
|
||||||
|
around_action :api_call_handle_error
|
||||||
|
around_action :api_call_timeout, :except => [:upload]
|
||||||
|
|
||||||
|
# Helper methods for checking consistency
|
||||||
|
include ConsistencyValidations
|
||||||
|
|
||||||
|
# Create a changeset from XML.
|
||||||
|
def create
|
||||||
|
assert_method :put
|
||||||
|
|
||||||
|
cs = Changeset.from_xml(request.raw_post, true)
|
||||||
|
|
||||||
|
# Assume that Changeset.from_xml has thrown an exception if there is an error parsing the xml
|
||||||
|
cs.user = current_user
|
||||||
|
cs.save_with_tags!
|
||||||
|
|
||||||
|
# Subscribe user to changeset comments
|
||||||
|
cs.subscribers << current_user
|
||||||
|
|
||||||
|
render :plain => cs.id.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return XML giving the basic info about the changeset. Does not
|
||||||
|
# return anything about the nodes, ways and relations in the changeset.
|
||||||
|
def show
|
||||||
|
changeset = Changeset.find(params[:id])
|
||||||
|
|
||||||
|
render :xml => changeset.to_xml(params[:include_discussion].presence).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# marks a changeset as closed. this may be called multiple times
|
||||||
|
# on the same changeset, so is idempotent.
|
||||||
|
def close
|
||||||
|
assert_method :put
|
||||||
|
|
||||||
|
changeset = Changeset.find(params[:id])
|
||||||
|
check_changeset_consistency(changeset, current_user)
|
||||||
|
|
||||||
|
# to close the changeset, we'll just set its closed_at time to
|
||||||
|
# now. this might not be enough if there are concurrency issues,
|
||||||
|
# but we'll have to wait and see.
|
||||||
|
changeset.set_closed_time_now
|
||||||
|
|
||||||
|
changeset.save!
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# insert a (set of) points into a changeset bounding box. this can only
|
||||||
|
# increase the size of the bounding box. this is a hint that clients can
|
||||||
|
# set either before uploading a large number of changes, or changes that
|
||||||
|
# the client (but not the server) knows will affect areas further away.
|
||||||
|
def expand_bbox
|
||||||
|
# only allow POST requests, because although this method is
|
||||||
|
# idempotent, there is no "document" to PUT really...
|
||||||
|
assert_method :post
|
||||||
|
|
||||||
|
cs = Changeset.find(params[:id])
|
||||||
|
check_changeset_consistency(cs, current_user)
|
||||||
|
|
||||||
|
# keep an array of lons and lats
|
||||||
|
lon = []
|
||||||
|
lat = []
|
||||||
|
|
||||||
|
# the request is in pseudo-osm format... this is kind-of an
|
||||||
|
# abuse, maybe should change to some other format?
|
||||||
|
doc = XML::Parser.string(request.raw_post, :options => XML::Parser::Options::NOERROR).parse
|
||||||
|
doc.find("//osm/node").each do |n|
|
||||||
|
lon << n["lon"].to_f * GeoRecord::SCALE
|
||||||
|
lat << n["lat"].to_f * GeoRecord::SCALE
|
||||||
|
end
|
||||||
|
|
||||||
|
# add the existing bounding box to the lon-lat array
|
||||||
|
lon << cs.min_lon unless cs.min_lon.nil?
|
||||||
|
lat << cs.min_lat unless cs.min_lat.nil?
|
||||||
|
lon << cs.max_lon unless cs.max_lon.nil?
|
||||||
|
lat << cs.max_lat unless cs.max_lat.nil?
|
||||||
|
|
||||||
|
# collapse the arrays to minimum and maximum
|
||||||
|
cs.min_lon = lon.min
|
||||||
|
cs.min_lat = lat.min
|
||||||
|
cs.max_lon = lon.max
|
||||||
|
cs.max_lat = lat.max
|
||||||
|
|
||||||
|
# save the larger bounding box and return the changeset, which
|
||||||
|
# will include the bigger bounding box.
|
||||||
|
cs.save!
|
||||||
|
render :xml => cs.to_xml.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Upload a diff in a single transaction.
|
||||||
|
#
|
||||||
|
# This means that each change within the diff must succeed, i.e: that
|
||||||
|
# each version number mentioned is still current. Otherwise the entire
|
||||||
|
# transaction *must* be rolled back.
|
||||||
|
#
|
||||||
|
# Furthermore, each element in the diff can only reference the current
|
||||||
|
# changeset.
|
||||||
|
#
|
||||||
|
# Returns: a diffResult document, as described in
|
||||||
|
# http://wiki.openstreetmap.org/wiki/OSM_Protocol_Version_0.6
|
||||||
|
def upload
|
||||||
|
# only allow POST requests, as the upload method is most definitely
|
||||||
|
# not idempotent, as several uploads with placeholder IDs will have
|
||||||
|
# different side-effects.
|
||||||
|
# see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.2
|
||||||
|
assert_method :post
|
||||||
|
|
||||||
|
changeset = Changeset.find(params[:id])
|
||||||
|
check_changeset_consistency(changeset, current_user)
|
||||||
|
|
||||||
|
diff_reader = DiffReader.new(request.raw_post, changeset)
|
||||||
|
Changeset.transaction do
|
||||||
|
result = diff_reader.commit
|
||||||
|
render :xml => result.to_s
|
||||||
|
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! do |a, b|
|
||||||
|
if a.timestamp == b.timestamp
|
||||||
|
a.version <=> b.version
|
||||||
|
else
|
||||||
|
a.timestamp <=> b.timestamp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# create changeset and user caches
|
||||||
|
changeset_cache = {}
|
||||||
|
user_display_name_cache = {}
|
||||||
|
|
||||||
|
# create an osmChange document for the output
|
||||||
|
result = OSM::API.new.get_xml_doc
|
||||||
|
result.root.name = "osmChange"
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
elements.each do |elt|
|
||||||
|
result.root <<
|
||||||
|
if elt.version == 1
|
||||||
|
# first version, so it must be newly-created.
|
||||||
|
created = XML::Node.new "create"
|
||||||
|
created << elt.to_xml_node(changeset_cache, user_display_name_cache)
|
||||||
|
elsif elt.visible
|
||||||
|
# must be a modify
|
||||||
|
modified = XML::Node.new "modify"
|
||||||
|
modified << elt.to_xml_node(changeset_cache, user_display_name_cache)
|
||||||
|
else
|
||||||
|
# if the element isn't visible then it must have been deleted
|
||||||
|
deleted = XML::Node.new "delete"
|
||||||
|
deleted << elt.to_xml_node(changeset_cache, user_display_name_cache)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
render :xml => result.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# query changesets by bounding box, time, user or open/closed status.
|
||||||
|
def query
|
||||||
|
# find any bounding box
|
||||||
|
bbox = BoundingBox.from_bbox_params(params) if params["bbox"]
|
||||||
|
|
||||||
|
# create the conditions that the user asked for. some or all of
|
||||||
|
# these may be nil.
|
||||||
|
changesets = Changeset.all
|
||||||
|
changesets = conditions_bbox(changesets, bbox)
|
||||||
|
changesets = conditions_user(changesets, params["user"], params["display_name"])
|
||||||
|
changesets = conditions_time(changesets, params["time"])
|
||||||
|
changesets = conditions_open(changesets, params["open"])
|
||||||
|
changesets = conditions_closed(changesets, params["closed"])
|
||||||
|
changesets = conditions_ids(changesets, params["changesets"])
|
||||||
|
|
||||||
|
# sort and limit the changesets
|
||||||
|
changesets = changesets.order("created_at DESC").limit(100)
|
||||||
|
|
||||||
|
# preload users, tags and comments
|
||||||
|
changesets = changesets.preload(:user, :changeset_tags, :comments)
|
||||||
|
|
||||||
|
# create the results document
|
||||||
|
results = OSM::API.new.get_xml_doc
|
||||||
|
|
||||||
|
# add all matching changesets to the XML results document
|
||||||
|
changesets.order("created_at DESC").limit(100).each do |cs|
|
||||||
|
results.root << cs.to_xml_node
|
||||||
|
end
|
||||||
|
|
||||||
|
render :xml => results.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# updates a changeset's tags. none of the changeset's attributes are
|
||||||
|
# user-modifiable, so they will be ignored.
|
||||||
|
#
|
||||||
|
# changesets are not (yet?) versioned, so we don't have to deal with
|
||||||
|
# history tables here. changesets are locked to a single user, however.
|
||||||
|
#
|
||||||
|
# after succesful update, returns the XML of the changeset.
|
||||||
|
def update
|
||||||
|
# request *must* be a PUT.
|
||||||
|
assert_method :put
|
||||||
|
|
||||||
|
changeset = Changeset.find(params[:id])
|
||||||
|
new_changeset = Changeset.from_xml(request.raw_post)
|
||||||
|
|
||||||
|
check_changeset_consistency(changeset, current_user)
|
||||||
|
changeset.update_from(new_changeset, current_user)
|
||||||
|
render :xml => changeset.to_xml.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Adds a subscriber to the changeset
|
||||||
|
def subscribe
|
||||||
|
# Check the arguments are sane
|
||||||
|
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
||||||
|
|
||||||
|
# Extract the arguments
|
||||||
|
id = params[:id].to_i
|
||||||
|
|
||||||
|
# Find the changeset and check it is valid
|
||||||
|
changeset = Changeset.find(id)
|
||||||
|
raise OSM::APIChangesetAlreadySubscribedError, changeset if changeset.subscribers.exists?(current_user.id)
|
||||||
|
|
||||||
|
# Add the subscriber
|
||||||
|
changeset.subscribers << current_user
|
||||||
|
|
||||||
|
# Return a copy of the updated changeset
|
||||||
|
render :xml => changeset.to_xml.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Removes a subscriber from the changeset
|
||||||
|
def unsubscribe
|
||||||
|
# Check the arguments are sane
|
||||||
|
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
||||||
|
|
||||||
|
# Extract the arguments
|
||||||
|
id = params[:id].to_i
|
||||||
|
|
||||||
|
# Find the changeset and check it is valid
|
||||||
|
changeset = Changeset.find(id)
|
||||||
|
raise OSM::APIChangesetNotSubscribedError, changeset unless changeset.subscribers.exists?(current_user.id)
|
||||||
|
|
||||||
|
# Remove the subscriber
|
||||||
|
changeset.subscribers.delete(current_user)
|
||||||
|
|
||||||
|
# Return a copy of the updated changeset
|
||||||
|
render :xml => changeset.to_xml.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
# utility functions below.
|
||||||
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
##
|
||||||
|
# if a bounding box was specified do some sanity checks.
|
||||||
|
# restrict changesets to those enclosed by a bounding box
|
||||||
|
# we need to return both the changesets and the bounding box
|
||||||
|
def conditions_bbox(changesets, bbox)
|
||||||
|
if bbox
|
||||||
|
bbox.check_boundaries
|
||||||
|
bbox = bbox.to_scaled
|
||||||
|
|
||||||
|
changesets.where("min_lon < ? and max_lon > ? and min_lat < ? and max_lat > ?",
|
||||||
|
bbox.max_lon.to_i, bbox.min_lon.to_i,
|
||||||
|
bbox.max_lat.to_i, bbox.min_lat.to_i)
|
||||||
|
else
|
||||||
|
changesets
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# restrict changesets to those by a particular user
|
||||||
|
def conditions_user(changesets, user, name)
|
||||||
|
if user.nil? && name.nil?
|
||||||
|
changesets
|
||||||
|
else
|
||||||
|
# shouldn't provide both name and UID
|
||||||
|
raise OSM::APIBadUserInput, "provide either the user ID or display name, but not both" if user && name
|
||||||
|
|
||||||
|
# use either the name or the UID to find the user which we're selecting on.
|
||||||
|
u = if name.nil?
|
||||||
|
# user input checking, we don't have any UIDs < 1
|
||||||
|
raise OSM::APIBadUserInput, "invalid user ID" if user.to_i < 1
|
||||||
|
|
||||||
|
u = User.find(user.to_i)
|
||||||
|
else
|
||||||
|
u = User.find_by(:display_name => name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# make sure we found a user
|
||||||
|
raise OSM::APINotFoundError if u.nil?
|
||||||
|
|
||||||
|
# should be able to get changesets of public users only, or
|
||||||
|
# our own changesets regardless of public-ness.
|
||||||
|
unless u.data_public?
|
||||||
|
# get optional user auth stuff so that users can see their own
|
||||||
|
# changesets if they're non-public
|
||||||
|
setup_user_auth
|
||||||
|
|
||||||
|
raise OSM::APINotFoundError if current_user.nil? || current_user != u
|
||||||
|
end
|
||||||
|
|
||||||
|
changesets.where(:user_id => u.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# restrict changes to those closed during a particular time period
|
||||||
|
def conditions_time(changesets, time)
|
||||||
|
if time.nil?
|
||||||
|
changesets
|
||||||
|
elsif time.count(",") == 1
|
||||||
|
# if there is a range, i.e: comma separated, then the first is
|
||||||
|
# low, second is high - same as with bounding boxes.
|
||||||
|
|
||||||
|
# check that we actually have 2 elements in the array
|
||||||
|
times = time.split(/,/)
|
||||||
|
raise OSM::APIBadUserInput, "bad time range" if times.size != 2
|
||||||
|
|
||||||
|
from, to = times.collect { |t| Time.parse(t) }
|
||||||
|
changesets.where("closed_at >= ? and created_at <= ?", from, to)
|
||||||
|
else
|
||||||
|
# if there is no comma, assume its a lower limit on time
|
||||||
|
changesets.where("closed_at >= ?", Time.parse(time))
|
||||||
|
end
|
||||||
|
# stupid Time seems to throw both of these for bad parsing, so
|
||||||
|
# we have to catch both and ensure the correct code path is taken.
|
||||||
|
rescue ArgumentError => ex
|
||||||
|
raise OSM::APIBadUserInput, ex.message.to_s
|
||||||
|
rescue RuntimeError => ex
|
||||||
|
raise OSM::APIBadUserInput, ex.message.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# return changesets which are open (haven't been closed yet)
|
||||||
|
# we do this by seeing if the 'closed at' time is in the future. Also if we've
|
||||||
|
# hit the maximum number of changes then it counts as no longer open.
|
||||||
|
# if parameter 'open' is nill then open and closed changesets are returned
|
||||||
|
def conditions_open(changesets, open)
|
||||||
|
if open.nil?
|
||||||
|
changesets
|
||||||
|
else
|
||||||
|
changesets.where("closed_at >= ? and num_changes <= ?",
|
||||||
|
Time.now.getutc, Changeset::MAX_ELEMENTS)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# query changesets which are closed
|
||||||
|
# ('closed at' time has passed or changes limit is hit)
|
||||||
|
def conditions_closed(changesets, closed)
|
||||||
|
if closed.nil?
|
||||||
|
changesets
|
||||||
|
else
|
||||||
|
changesets.where("closed_at < ? or num_changes > ?",
|
||||||
|
Time.now.getutc, Changeset::MAX_ELEMENTS)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# query changesets by a list of ids
|
||||||
|
# (either specified as array or comma-separated string)
|
||||||
|
def conditions_ids(changesets, ids)
|
||||||
|
if ids.nil?
|
||||||
|
changesets
|
||||||
|
elsif ids.empty?
|
||||||
|
raise OSM::APIBadUserInput, "No changesets were given to search for"
|
||||||
|
else
|
||||||
|
ids = ids.split(",").collect(&:to_i)
|
||||||
|
changesets.where(:id => ids)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
83
app/controllers/api/nodes_controller.rb
Normal file
83
app/controllers/api/nodes_controller.rb
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
# The NodeController is the RESTful interface to Node objects
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class NodesController < ApplicationController
|
||||||
|
require "xml/libxml"
|
||||||
|
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
before_action :authorize, :only => [:create, :update, :delete]
|
||||||
|
before_action :api_deny_access_handler
|
||||||
|
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
|
before_action :require_public_data, :only => [:create, :update, :delete]
|
||||||
|
before_action :check_api_writable, :only => [:create, :update, :delete]
|
||||||
|
before_action :check_api_readable, :except => [:create, :update, :delete]
|
||||||
|
around_action :api_call_handle_error, :api_call_timeout
|
||||||
|
|
||||||
|
# Create a node from XML.
|
||||||
|
def create
|
||||||
|
assert_method :put
|
||||||
|
|
||||||
|
node = Node.from_xml(request.raw_post, true)
|
||||||
|
|
||||||
|
# Assume that Node.from_xml has thrown an exception if there is an error parsing the xml
|
||||||
|
node.create_with_history current_user
|
||||||
|
render :plain => node.id.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# Dump the details on a node given in params[:id]
|
||||||
|
def show
|
||||||
|
node = Node.find(params[:id])
|
||||||
|
|
||||||
|
response.last_modified = node.timestamp
|
||||||
|
|
||||||
|
if node.visible
|
||||||
|
render :xml => node.to_xml.to_s
|
||||||
|
else
|
||||||
|
head :gone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update a node from given XML
|
||||||
|
def update
|
||||||
|
node = Node.find(params[:id])
|
||||||
|
new_node = Node.from_xml(request.raw_post)
|
||||||
|
|
||||||
|
raise OSM::APIBadUserInput, "The id in the url (#{node.id}) is not the same as provided in the xml (#{new_node.id})" unless new_node && new_node.id == node.id
|
||||||
|
|
||||||
|
node.update_from(new_node, current_user)
|
||||||
|
render :plain => node.version.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delete a node. Doesn't actually delete it, but retains its history
|
||||||
|
# in a wiki-like way. We therefore treat it like an update, so the delete
|
||||||
|
# method returns the new version number.
|
||||||
|
def delete
|
||||||
|
node = Node.find(params[:id])
|
||||||
|
new_node = Node.from_xml(request.raw_post)
|
||||||
|
|
||||||
|
raise OSM::APIBadUserInput, "The id in the url (#{node.id}) is not the same as provided in the xml (#{new_node.id})" unless new_node && new_node.id == node.id
|
||||||
|
|
||||||
|
node.delete_with_history!(new_node, current_user)
|
||||||
|
render :plain => node.version.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# Dump the details on many nodes whose ids are given in the "nodes" parameter.
|
||||||
|
def index
|
||||||
|
raise OSM::APIBadUserInput, "The parameter nodes is required, and must be of the form nodes=id[,id[,id...]]" unless params["nodes"]
|
||||||
|
|
||||||
|
ids = params["nodes"].split(",").collect(&:to_i)
|
||||||
|
|
||||||
|
raise OSM::APIBadUserInput, "No nodes were given to search for" if ids.empty?
|
||||||
|
|
||||||
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
|
||||||
|
Node.find(ids).each do |node|
|
||||||
|
doc.root << node.to_xml_node
|
||||||
|
end
|
||||||
|
|
||||||
|
render :xml => doc.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
373
app/controllers/api/notes_controller.rb
Normal file
373
app/controllers/api/notes_controller.rb
Normal file
|
@ -0,0 +1,373 @@
|
||||||
|
module Api
|
||||||
|
class NotesController < ApplicationController
|
||||||
|
layout "site", :only => [:mine]
|
||||||
|
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
before_action :check_api_readable
|
||||||
|
before_action :setup_user_auth, :only => [:create, :comment, :show]
|
||||||
|
before_action :authorize, :only => [:close, :reopen, :destroy]
|
||||||
|
before_action :api_deny_access_handler
|
||||||
|
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
|
before_action :check_api_writable, :only => [:create, :comment, :close, :reopen, :destroy]
|
||||||
|
before_action :set_locale
|
||||||
|
around_action :api_call_handle_error, :api_call_timeout
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return a list of notes in a given area
|
||||||
|
def index
|
||||||
|
# Figure out the bbox - we prefer a bbox argument but also
|
||||||
|
# support the old, deprecated, method with four arguments
|
||||||
|
if params[:bbox]
|
||||||
|
bbox = BoundingBox.from_bbox_params(params)
|
||||||
|
else
|
||||||
|
raise OSM::APIBadUserInput, "No l was given" unless params[:l]
|
||||||
|
raise OSM::APIBadUserInput, "No r was given" unless params[:r]
|
||||||
|
raise OSM::APIBadUserInput, "No b was given" unless params[:b]
|
||||||
|
raise OSM::APIBadUserInput, "No t was given" unless params[:t]
|
||||||
|
|
||||||
|
bbox = BoundingBox.from_lrbt_params(params)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get any conditions that need to be applied
|
||||||
|
notes = closed_condition(Note.all)
|
||||||
|
|
||||||
|
# Check that the boundaries are valid
|
||||||
|
bbox.check_boundaries
|
||||||
|
|
||||||
|
# Check the the bounding box is not too big
|
||||||
|
bbox.check_size(MAX_NOTE_REQUEST_AREA)
|
||||||
|
|
||||||
|
# Find the notes we want to return
|
||||||
|
@notes = notes.bbox(bbox).order("updated_at DESC").limit(result_limit).preload(:comments)
|
||||||
|
|
||||||
|
# Render the result
|
||||||
|
respond_to do |format|
|
||||||
|
format.rss
|
||||||
|
format.xml
|
||||||
|
format.json
|
||||||
|
format.gpx
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Create a new note
|
||||||
|
def create
|
||||||
|
# Check the ACLs
|
||||||
|
raise OSM::APIAccessDenied if current_user.nil? && Acl.no_note_comment(request.remote_ip)
|
||||||
|
|
||||||
|
# Check the arguments are sane
|
||||||
|
raise OSM::APIBadUserInput, "No lat was given" unless params[:lat]
|
||||||
|
raise OSM::APIBadUserInput, "No lon was given" unless params[:lon]
|
||||||
|
raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
|
||||||
|
|
||||||
|
# Extract the arguments
|
||||||
|
lon = OSM.parse_float(params[:lon], OSM::APIBadUserInput, "lon was not a number")
|
||||||
|
lat = OSM.parse_float(params[:lat], OSM::APIBadUserInput, "lat was not a number")
|
||||||
|
comment = params[:text]
|
||||||
|
|
||||||
|
# Include in a transaction to ensure that there is always a note_comment for every note
|
||||||
|
Note.transaction do
|
||||||
|
# Create the note
|
||||||
|
@note = Note.create(:lat => lat, :lon => lon)
|
||||||
|
raise OSM::APIBadUserInput, "The note is outside this world" unless @note.in_world?
|
||||||
|
|
||||||
|
# Save the note
|
||||||
|
@note.save!
|
||||||
|
|
||||||
|
# Add a comment to the note
|
||||||
|
add_comment(@note, comment, "opened")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a copy of the new note
|
||||||
|
respond_to do |format|
|
||||||
|
format.xml { render :action => :show }
|
||||||
|
format.json { render :action => :show }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Add a comment to an existing note
|
||||||
|
def comment
|
||||||
|
# Check the ACLs
|
||||||
|
raise OSM::APIAccessDenied if current_user.nil? && Acl.no_note_comment(request.remote_ip)
|
||||||
|
|
||||||
|
# Check the arguments are sane
|
||||||
|
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
||||||
|
raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
|
||||||
|
|
||||||
|
# Extract the arguments
|
||||||
|
id = params[:id].to_i
|
||||||
|
comment = params[:text]
|
||||||
|
|
||||||
|
# Find the note and check it is valid
|
||||||
|
@note = Note.find(id)
|
||||||
|
raise OSM::APINotFoundError unless @note
|
||||||
|
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
|
||||||
|
raise OSM::APINoteAlreadyClosedError, @note if @note.closed?
|
||||||
|
|
||||||
|
# Add a comment to the note
|
||||||
|
Note.transaction do
|
||||||
|
add_comment(@note, comment, "commented")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a copy of the updated note
|
||||||
|
respond_to do |format|
|
||||||
|
format.xml { render :action => :show }
|
||||||
|
format.json { render :action => :show }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Close a note
|
||||||
|
def close
|
||||||
|
# Check the arguments are sane
|
||||||
|
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
||||||
|
|
||||||
|
# Extract the arguments
|
||||||
|
id = params[:id].to_i
|
||||||
|
comment = params[:text]
|
||||||
|
|
||||||
|
# Find the note and check it is valid
|
||||||
|
@note = Note.find_by(:id => id)
|
||||||
|
raise OSM::APINotFoundError unless @note
|
||||||
|
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
|
||||||
|
raise OSM::APINoteAlreadyClosedError, @note if @note.closed?
|
||||||
|
|
||||||
|
# Close the note and add a comment
|
||||||
|
Note.transaction do
|
||||||
|
@note.close
|
||||||
|
|
||||||
|
add_comment(@note, comment, "closed")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a copy of the updated note
|
||||||
|
respond_to do |format|
|
||||||
|
format.xml { render :action => :show }
|
||||||
|
format.json { render :action => :show }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Reopen a note
|
||||||
|
def reopen
|
||||||
|
# Check the arguments are sane
|
||||||
|
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
||||||
|
|
||||||
|
# Extract the arguments
|
||||||
|
id = params[:id].to_i
|
||||||
|
comment = params[:text]
|
||||||
|
|
||||||
|
# Find the note and check it is valid
|
||||||
|
@note = Note.find_by(:id => id)
|
||||||
|
raise OSM::APINotFoundError unless @note
|
||||||
|
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? || current_user.moderator?
|
||||||
|
raise OSM::APINoteAlreadyOpenError, @note unless @note.closed? || !@note.visible?
|
||||||
|
|
||||||
|
# Reopen the note and add a comment
|
||||||
|
Note.transaction do
|
||||||
|
@note.reopen
|
||||||
|
|
||||||
|
add_comment(@note, comment, "reopened")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a copy of the updated note
|
||||||
|
respond_to do |format|
|
||||||
|
format.xml { render :action => :show }
|
||||||
|
format.json { render :action => :show }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Get a feed of recent notes and comments
|
||||||
|
def feed
|
||||||
|
# Get any conditions that need to be applied
|
||||||
|
notes = closed_condition(Note.all)
|
||||||
|
|
||||||
|
# Process any bbox
|
||||||
|
if params[:bbox]
|
||||||
|
bbox = BoundingBox.from_bbox_params(params)
|
||||||
|
|
||||||
|
bbox.check_boundaries
|
||||||
|
bbox.check_size(MAX_NOTE_REQUEST_AREA)
|
||||||
|
|
||||||
|
notes = notes.bbox(bbox)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find the comments we want to return
|
||||||
|
@comments = NoteComment.where(:note_id => notes).order("created_at DESC").limit(result_limit).preload(:note)
|
||||||
|
|
||||||
|
# Render the result
|
||||||
|
respond_to do |format|
|
||||||
|
format.rss
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Read a note
|
||||||
|
def show
|
||||||
|
# Check the arguments are sane
|
||||||
|
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
||||||
|
|
||||||
|
# Find the note and check it is valid
|
||||||
|
@note = Note.find(params[:id])
|
||||||
|
raise OSM::APINotFoundError unless @note
|
||||||
|
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? || current_user&.moderator?
|
||||||
|
|
||||||
|
# Render the result
|
||||||
|
respond_to do |format|
|
||||||
|
format.xml
|
||||||
|
format.rss
|
||||||
|
format.json
|
||||||
|
format.gpx
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Delete (hide) a note
|
||||||
|
def destroy
|
||||||
|
# Check the arguments are sane
|
||||||
|
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
||||||
|
|
||||||
|
# Extract the arguments
|
||||||
|
id = params[:id].to_i
|
||||||
|
comment = params[:text]
|
||||||
|
|
||||||
|
# Find the note and check it is valid
|
||||||
|
@note = Note.find(id)
|
||||||
|
raise OSM::APINotFoundError unless @note
|
||||||
|
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
|
||||||
|
|
||||||
|
# Mark the note as hidden
|
||||||
|
Note.transaction do
|
||||||
|
@note.status = "hidden"
|
||||||
|
@note.save
|
||||||
|
|
||||||
|
add_comment(@note, comment, "hidden", false)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a copy of the updated note
|
||||||
|
respond_to do |format|
|
||||||
|
format.xml { render :action => :show }
|
||||||
|
format.json { render :action => :show }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return a list of notes matching a given string
|
||||||
|
def search
|
||||||
|
# Get the initial set of notes
|
||||||
|
@notes = closed_condition(Note.all)
|
||||||
|
|
||||||
|
# Add any user filter
|
||||||
|
if params[:display_name] || params[:user]
|
||||||
|
if params[:display_name]
|
||||||
|
@user = User.find_by(:display_name => params[:display_name])
|
||||||
|
|
||||||
|
raise OSM::APIBadUserInput, "User #{params[:display_name]} not known" unless @user
|
||||||
|
else
|
||||||
|
@user = User.find_by(:id => params[:user])
|
||||||
|
|
||||||
|
raise OSM::APIBadUserInput, "User #{params[:user]} not known" unless @user
|
||||||
|
end
|
||||||
|
|
||||||
|
@notes = @notes.joins(:comments).where(:note_comments => { :author_id => @user })
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add any text filter
|
||||||
|
@notes = @notes.joins(:comments).where("to_tsvector('english', note_comments.body) @@ plainto_tsquery('english', ?)", params[:q]) if params[:q]
|
||||||
|
|
||||||
|
# Add any date filter
|
||||||
|
if params[:from]
|
||||||
|
begin
|
||||||
|
from = Time.parse(params[:from])
|
||||||
|
rescue ArgumentError
|
||||||
|
raise OSM::APIBadUserInput, "Date #{params[:from]} is in a wrong format"
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
to = if params[:to]
|
||||||
|
Time.parse(params[:to])
|
||||||
|
else
|
||||||
|
Time.now
|
||||||
|
end
|
||||||
|
rescue ArgumentError
|
||||||
|
raise OSM::APIBadUserInput, "Date #{params[:to]} is in a wrong format"
|
||||||
|
end
|
||||||
|
|
||||||
|
@notes = @notes.where(:created_at => from..to)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find the notes we want to return
|
||||||
|
@notes = @notes.order("updated_at DESC").limit(result_limit).preload(:comments)
|
||||||
|
|
||||||
|
# Render the result
|
||||||
|
respond_to do |format|
|
||||||
|
format.rss { render :action => :index }
|
||||||
|
format.xml { render :action => :index }
|
||||||
|
format.json { render :action => :index }
|
||||||
|
format.gpx { render :action => :index }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
# utility functions below.
|
||||||
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
##
|
||||||
|
# Get the maximum number of results to return
|
||||||
|
def result_limit
|
||||||
|
if params[:limit]
|
||||||
|
if params[:limit].to_i.positive? && params[:limit].to_i <= 10000
|
||||||
|
params[:limit].to_i
|
||||||
|
else
|
||||||
|
raise OSM::APIBadUserInput, "Note limit must be between 1 and 10000"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
100
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Generate a condition to choose which notes we want based
|
||||||
|
# on their status and the user's request parameters
|
||||||
|
def closed_condition(notes)
|
||||||
|
closed_since = if params[:closed]
|
||||||
|
params[:closed].to_i
|
||||||
|
else
|
||||||
|
7
|
||||||
|
end
|
||||||
|
|
||||||
|
if closed_since.negative?
|
||||||
|
notes.where.not(:status => "hidden")
|
||||||
|
elsif closed_since.positive?
|
||||||
|
notes.where(:status => "open")
|
||||||
|
.or(notes.where(:status => "closed")
|
||||||
|
.where(notes.arel_table[:closed_at].gt(Time.now - closed_since.days)))
|
||||||
|
else
|
||||||
|
notes.where(:status => "open")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Add a comment to a note
|
||||||
|
def add_comment(note, text, event, notify = true)
|
||||||
|
attributes = { :visible => true, :event => event, :body => text }
|
||||||
|
|
||||||
|
if current_user
|
||||||
|
attributes[:author_id] = current_user.id
|
||||||
|
else
|
||||||
|
attributes[:author_ip] = request.remote_ip
|
||||||
|
end
|
||||||
|
|
||||||
|
comment = note.comments.create!(attributes)
|
||||||
|
|
||||||
|
note.comments.map(&:author).uniq.each do |user|
|
||||||
|
Notifier.note_comment_notification(comment, user).deliver_later if notify && user && user != current_user && user.visible?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
79
app/controllers/api/old_controller.rb
Normal file
79
app/controllers/api/old_controller.rb
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# this class pulls together the logic for all the old_* controllers
|
||||||
|
# into one place. as it turns out, the API methods for historical
|
||||||
|
# nodes, ways and relations are basically identical.
|
||||||
|
module Api
|
||||||
|
class OldController < ApplicationController
|
||||||
|
require "xml/libxml"
|
||||||
|
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
before_action :setup_user_auth, :only => [:history, :version]
|
||||||
|
before_action :api_deny_access_handler
|
||||||
|
before_action :authorize, :only => [:redact]
|
||||||
|
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
|
before_action :check_api_readable
|
||||||
|
before_action :check_api_writable, :only => [:redact]
|
||||||
|
around_action :api_call_handle_error, :api_call_timeout
|
||||||
|
before_action :lookup_old_element, :except => [:history]
|
||||||
|
before_action :lookup_old_element_versions, :only => [:history]
|
||||||
|
|
||||||
|
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
|
||||||
|
# to do that ourselves.
|
||||||
|
raise OSM::APINotFoundError if @elements.empty?
|
||||||
|
|
||||||
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
|
||||||
|
visible_elements = if show_redactions?
|
||||||
|
@elements
|
||||||
|
else
|
||||||
|
@elements.unredacted
|
||||||
|
end
|
||||||
|
|
||||||
|
visible_elements.each do |element|
|
||||||
|
doc.root << element.to_xml_node
|
||||||
|
end
|
||||||
|
|
||||||
|
render :xml => doc.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def version
|
||||||
|
if @old_element.redacted? && !show_redactions?
|
||||||
|
head :forbidden
|
||||||
|
|
||||||
|
else
|
||||||
|
response.last_modified = @old_element.timestamp
|
||||||
|
|
||||||
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
doc.root << @old_element.to_xml_node
|
||||||
|
|
||||||
|
render :xml => doc.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def redact
|
||||||
|
redaction_id = params["redaction"]
|
||||||
|
if redaction_id.nil?
|
||||||
|
# if no redaction ID was provided, then this is an unredact
|
||||||
|
# operation.
|
||||||
|
@old_element.redact!(nil)
|
||||||
|
else
|
||||||
|
# if a redaction ID was specified, then set this element to
|
||||||
|
# be redacted in that redaction.
|
||||||
|
redaction = Redaction.find(redaction_id.to_i)
|
||||||
|
@old_element.redact!(redaction)
|
||||||
|
end
|
||||||
|
|
||||||
|
# just return an empty 200 OK for success
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def show_redactions?
|
||||||
|
current_user&.moderator? && params[:show_redactions] == "true"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
13
app/controllers/api/old_nodes_controller.rb
Normal file
13
app/controllers/api/old_nodes_controller.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module Api
|
||||||
|
class OldNodesController < OldController
|
||||||
|
private
|
||||||
|
|
||||||
|
def lookup_old_element
|
||||||
|
@old_element = OldNode.find([params[:id], params[:version]])
|
||||||
|
end
|
||||||
|
|
||||||
|
def lookup_old_element_versions
|
||||||
|
@elements = OldNode.where(:node_id => params[:id]).order(:version)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
13
app/controllers/api/old_relations_controller.rb
Normal file
13
app/controllers/api/old_relations_controller.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module Api
|
||||||
|
class OldRelationsController < OldController
|
||||||
|
private
|
||||||
|
|
||||||
|
def lookup_old_element
|
||||||
|
@old_element = OldRelation.find([params[:id], params[:version]])
|
||||||
|
end
|
||||||
|
|
||||||
|
def lookup_old_element_versions
|
||||||
|
@elements = OldRelation.where(:relation_id => params[:id]).order(:version)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
13
app/controllers/api/old_ways_controller.rb
Normal file
13
app/controllers/api/old_ways_controller.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module Api
|
||||||
|
class OldWaysController < OldController
|
||||||
|
private
|
||||||
|
|
||||||
|
def lookup_old_element
|
||||||
|
@old_element = OldWay.find([params[:id], params[:version]])
|
||||||
|
end
|
||||||
|
|
||||||
|
def lookup_old_element_versions
|
||||||
|
@elements = OldWay.where(:way_id => params[:id]).order(:version)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
169
app/controllers/api/relations_controller.rb
Normal file
169
app/controllers/api/relations_controller.rb
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
module Api
|
||||||
|
class RelationsController < ApplicationController
|
||||||
|
require "xml/libxml"
|
||||||
|
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
before_action :authorize, :only => [:create, :update, :delete]
|
||||||
|
before_action :api_deny_access_handler
|
||||||
|
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
|
before_action :require_public_data, :only => [:create, :update, :delete]
|
||||||
|
before_action :check_api_writable, :only => [:create, :update, :delete]
|
||||||
|
before_action :check_api_readable, :except => [:create, :update, :delete]
|
||||||
|
around_action :api_call_handle_error, :api_call_timeout
|
||||||
|
|
||||||
|
def create
|
||||||
|
assert_method :put
|
||||||
|
|
||||||
|
relation = Relation.from_xml(request.raw_post, true)
|
||||||
|
|
||||||
|
# Assume that Relation.from_xml has thrown an exception if there is an error parsing the xml
|
||||||
|
relation.create_with_history current_user
|
||||||
|
render :plain => relation.id.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
relation = Relation.find(params[:id])
|
||||||
|
response.last_modified = relation.timestamp
|
||||||
|
if relation.visible
|
||||||
|
render :xml => relation.to_xml.to_s
|
||||||
|
else
|
||||||
|
head :gone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
logger.debug request.raw_post
|
||||||
|
|
||||||
|
relation = Relation.find(params[:id])
|
||||||
|
new_relation = Relation.from_xml(request.raw_post)
|
||||||
|
|
||||||
|
raise OSM::APIBadUserInput, "The id in the url (#{relation.id}) is not the same as provided in the xml (#{new_relation.id})" unless new_relation && new_relation.id == relation.id
|
||||||
|
|
||||||
|
relation.update_from new_relation, current_user
|
||||||
|
render :plain => relation.version.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete
|
||||||
|
relation = Relation.find(params[:id])
|
||||||
|
new_relation = Relation.from_xml(request.raw_post)
|
||||||
|
if new_relation && new_relation.id == relation.id
|
||||||
|
relation.delete_with_history!(new_relation, current_user)
|
||||||
|
render :plain => relation.version.to_s
|
||||||
|
else
|
||||||
|
head :bad_request
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
# full
|
||||||
|
#
|
||||||
|
# input parameters: id
|
||||||
|
#
|
||||||
|
# returns XML representation of one relation object plus all its
|
||||||
|
# members, plus all nodes part of member ways
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
def full
|
||||||
|
relation = Relation.find(params[:id])
|
||||||
|
|
||||||
|
if relation.visible
|
||||||
|
|
||||||
|
# first find the ids of nodes, ways and relations referenced by this
|
||||||
|
# relation - note that we exclude this relation just in case.
|
||||||
|
|
||||||
|
node_ids = relation.members.select { |m| m[0] == "Node" }.map { |m| m[1] }
|
||||||
|
way_ids = relation.members.select { |m| m[0] == "Way" }.map { |m| m[1] }
|
||||||
|
relation_ids = relation.members.select { |m| m[0] == "Relation" && m[1] != relation.id }.map { |m| m[1] }
|
||||||
|
|
||||||
|
# next load the relations and the ways.
|
||||||
|
|
||||||
|
relations = Relation.where(:id => relation_ids).includes(:relation_tags)
|
||||||
|
ways = Way.where(:id => way_ids).includes(:way_nodes, :way_tags)
|
||||||
|
|
||||||
|
# now additionally collect nodes referenced by ways. Note how we
|
||||||
|
# recursively evaluate ways but NOT relations.
|
||||||
|
|
||||||
|
way_node_ids = ways.collect do |way|
|
||||||
|
way.way_nodes.collect(&:node_id)
|
||||||
|
end
|
||||||
|
node_ids += way_node_ids.flatten
|
||||||
|
nodes = Node.where(:id => node_ids.uniq).includes(:node_tags)
|
||||||
|
|
||||||
|
# create XML.
|
||||||
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
visible_nodes = {}
|
||||||
|
changeset_cache = {}
|
||||||
|
user_display_name_cache = {}
|
||||||
|
|
||||||
|
nodes.each do |node|
|
||||||
|
next unless node.visible? # should be unnecessary if data is consistent.
|
||||||
|
|
||||||
|
doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
|
||||||
|
visible_nodes[node.id] = node
|
||||||
|
end
|
||||||
|
|
||||||
|
ways.each do |way|
|
||||||
|
next unless way.visible? # should be unnecessary if data is consistent.
|
||||||
|
|
||||||
|
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
|
||||||
|
end
|
||||||
|
|
||||||
|
relations.each do |rel|
|
||||||
|
next unless rel.visible? # should be unnecessary if data is consistent.
|
||||||
|
|
||||||
|
doc.root << rel.to_xml_node(changeset_cache, user_display_name_cache)
|
||||||
|
end
|
||||||
|
|
||||||
|
# finally add self and output
|
||||||
|
doc.root << relation.to_xml_node(changeset_cache, user_display_name_cache)
|
||||||
|
render :xml => doc.to_s
|
||||||
|
|
||||||
|
else
|
||||||
|
head :gone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
raise OSM::APIBadUserInput, "The parameter relations is required, and must be of the form relations=id[,id[,id...]]" unless params["relations"]
|
||||||
|
|
||||||
|
ids = params["relations"].split(",").collect(&:to_i)
|
||||||
|
|
||||||
|
raise OSM::APIBadUserInput, "No relations were given to search for" if ids.empty?
|
||||||
|
|
||||||
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
|
||||||
|
Relation.find(ids).each do |relation|
|
||||||
|
doc.root << relation.to_xml_node
|
||||||
|
end
|
||||||
|
|
||||||
|
render :xml => doc.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def relations_for_way
|
||||||
|
relations_for_object("Way")
|
||||||
|
end
|
||||||
|
|
||||||
|
def relations_for_node
|
||||||
|
relations_for_object("Node")
|
||||||
|
end
|
||||||
|
|
||||||
|
def relations_for_relation
|
||||||
|
relations_for_object("Relation")
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def relations_for_object(objtype)
|
||||||
|
relationids = RelationMember.where(:member_type => objtype, :member_id => params[:id]).collect(&:relation_id).uniq
|
||||||
|
|
||||||
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
|
||||||
|
Relation.find(relationids).each do |relation|
|
||||||
|
doc.root << relation.to_xml_node if relation.visible
|
||||||
|
end
|
||||||
|
|
||||||
|
render :xml => doc.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
102
app/controllers/api/search_controller.rb
Normal file
102
app/controllers/api/search_controller.rb
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
module Api
|
||||||
|
class SearchController < ApplicationController
|
||||||
|
# Support searching for nodes, ways, or all
|
||||||
|
# Can search by tag k, v, or both (type->k,value->v)
|
||||||
|
# Can search by name (k=name,v=....)
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
authorize_resource :class => false
|
||||||
|
|
||||||
|
def search_all
|
||||||
|
do_search(true, true, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_ways
|
||||||
|
do_search(true, false, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_nodes
|
||||||
|
do_search(false, true, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_relations
|
||||||
|
do_search(false, false, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_search(do_ways, do_nodes, do_relations)
|
||||||
|
type = params["type"]
|
||||||
|
value = params["value"]
|
||||||
|
unless type || value
|
||||||
|
name = params["name"]
|
||||||
|
if name
|
||||||
|
type = "name"
|
||||||
|
value = name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if do_nodes
|
||||||
|
response.headers["Error"] = "Searching of nodes is currently unavailable"
|
||||||
|
head :service_unavailable
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
unless value
|
||||||
|
response.headers["Error"] = "Searching for a key without value is currently unavailable"
|
||||||
|
head :service_unavailable
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Matching for node tags table
|
||||||
|
if do_nodes
|
||||||
|
nodes = Node.joins(:node_tags)
|
||||||
|
nodes = nodes.where(:current_node_tags => { :k => type }) if type
|
||||||
|
nodes = nodes.where(:current_node_tags => { :v => value }) if value
|
||||||
|
nodes = nodes.limit(100)
|
||||||
|
else
|
||||||
|
nodes = []
|
||||||
|
end
|
||||||
|
|
||||||
|
# Matching for way tags table
|
||||||
|
if do_ways
|
||||||
|
ways = Way.joins(:way_tags)
|
||||||
|
ways = ways.where(:current_way_tags => { :k => type }) if type
|
||||||
|
ways = ways.where(:current_way_tags => { :v => value }) if value
|
||||||
|
ways = ways.limit(100)
|
||||||
|
else
|
||||||
|
ways = []
|
||||||
|
end
|
||||||
|
|
||||||
|
# Matching for relation tags table
|
||||||
|
if do_relations
|
||||||
|
relations = Relation.joins(:relation_tags)
|
||||||
|
relations = relations.where(:current_relation_tags => { :k => type }) if type
|
||||||
|
relations = relations.where(:current_relation_tags => { :v => value }) if value
|
||||||
|
relations = relations.limit(2000)
|
||||||
|
else
|
||||||
|
relations = []
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fetch any node needed for our ways (only have matching nodes so far)
|
||||||
|
nodes += Node.find(ways.collect(&:nds).uniq)
|
||||||
|
|
||||||
|
# Print
|
||||||
|
visible_nodes = {}
|
||||||
|
changeset_cache = {}
|
||||||
|
user_display_name_cache = {}
|
||||||
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
nodes.each do |node|
|
||||||
|
doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
|
||||||
|
visible_nodes[node.id] = node
|
||||||
|
end
|
||||||
|
|
||||||
|
ways.each do |way|
|
||||||
|
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
|
||||||
|
end
|
||||||
|
|
||||||
|
relations.each do |rel|
|
||||||
|
doc.root << rel.to_xml_node(changeset_cache, user_display_name_cache)
|
||||||
|
end
|
||||||
|
|
||||||
|
render :xml => doc.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
219
app/controllers/api/swf_controller.rb
Normal file
219
app/controllers/api/swf_controller.rb
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
module Api
|
||||||
|
class SwfController < ApplicationController
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
before_action :check_api_readable
|
||||||
|
authorize_resource :class => false
|
||||||
|
|
||||||
|
# to log:
|
||||||
|
# RAILS_DEFAULT_LOGGER.error("Args: #{args[0]}, #{args[1]}, #{args[2]}, #{args[3]}")
|
||||||
|
# $log.puts Time.new.to_s+','+Time.new.usec.to_s+": started GPS script"
|
||||||
|
# http://localhost:3000/api/0.4/swf/trackpoints?xmin=-2.32402605810577&xmax=-2.18386309423859&ymin=52.1546608755772&ymax=52.2272777906895&baselong=-2.25325793066437&basey=61.3948537948532&masterscale=5825.4222222222
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# Public methods
|
||||||
|
|
||||||
|
# ---- trackpoints compile SWF of trackpoints
|
||||||
|
|
||||||
|
def trackpoints
|
||||||
|
# - Initialise
|
||||||
|
|
||||||
|
baselong = params["baselong"].to_f
|
||||||
|
basey = params["basey"].to_f
|
||||||
|
masterscale = params["masterscale"].to_f
|
||||||
|
|
||||||
|
bbox = BoundingBox.new(params["xmin"], params["ymin"],
|
||||||
|
params["xmax"], params["ymax"])
|
||||||
|
start = params["start"].to_i
|
||||||
|
|
||||||
|
# - Begin movie
|
||||||
|
|
||||||
|
bounds_left = 0
|
||||||
|
bounds_right = 320 * 20
|
||||||
|
bounds_bottom = 0
|
||||||
|
bounds_top = 240 * 20
|
||||||
|
|
||||||
|
m = ""
|
||||||
|
m += swf_record(9, 255.chr + 155.chr + 155.chr) # Background
|
||||||
|
absx = 0
|
||||||
|
absy = 0
|
||||||
|
xl = yb = 9999999
|
||||||
|
xr = yt = -9999999
|
||||||
|
|
||||||
|
# - Send SQL for GPS tracks
|
||||||
|
|
||||||
|
b = ""
|
||||||
|
lasttime = 0
|
||||||
|
lasttrack = lastfile = "-1"
|
||||||
|
|
||||||
|
if params["token"]
|
||||||
|
user = User.authenticate(:token => params[:token])
|
||||||
|
sql = "SELECT gps_points.latitude*0.0000001 AS lat,gps_points.longitude*0.0000001 AS lon,gpx_files.id AS fileid," + " EXTRACT(EPOCH FROM gps_points.timestamp) AS ts, gps_points.trackid AS trackid " + " FROM gpx_files,gps_points " + "WHERE gpx_files.id=gpx_id " + " AND gpx_files.user_id=#{user.id} " + " AND " + OSM.sql_for_area(bbox, "gps_points.") + " AND (gps_points.timestamp IS NOT NULL) " + "ORDER BY fileid DESC,ts " + "LIMIT 10000 OFFSET #{start}"
|
||||||
|
else
|
||||||
|
sql = "SELECT latitude*0.0000001 AS lat,longitude*0.0000001 AS lon,gpx_id AS fileid," + " EXTRACT(EPOCH FROM timestamp) AS ts, gps_points.trackid AS trackid " + " FROM gps_points " + "WHERE " + OSM.sql_for_area(bbox, "gps_points.") + " AND (gps_points.timestamp IS NOT NULL) " + "ORDER BY fileid DESC,ts " + "LIMIT 10000 OFFSET #{start}"
|
||||||
|
end
|
||||||
|
gpslist = ActiveRecord::Base.connection.select_all sql
|
||||||
|
|
||||||
|
# - Draw GPS trace lines
|
||||||
|
|
||||||
|
r = start_shape
|
||||||
|
gpslist.each do |row|
|
||||||
|
xs = (long2coord(row["lon"].to_f, baselong, masterscale) * 20).floor
|
||||||
|
ys = (lat2coord(row["lat"].to_f, basey, masterscale) * 20).floor
|
||||||
|
xl = [xs, xl].min
|
||||||
|
xr = [xs, xr].max
|
||||||
|
yb = [ys, yb].min
|
||||||
|
yt = [ys, yt].max
|
||||||
|
if row["ts"].to_i - lasttime > 180 || row["fileid"] != lastfile || row["trackid"] != lasttrack # or row['ts'].to_i==lasttime
|
||||||
|
b += start_and_move(xs, ys, "01")
|
||||||
|
absx = xs.floor
|
||||||
|
absy = ys.floor
|
||||||
|
end
|
||||||
|
b += draw_to(absx, absy, xs, ys)
|
||||||
|
absx = xs.floor
|
||||||
|
absy = ys.floor
|
||||||
|
lasttime = row["ts"].to_i
|
||||||
|
lastfile = row["fileid"]
|
||||||
|
lasttrack = row["trackid"]
|
||||||
|
r += [b.slice!(0...80)].pack("B*") while b.length > 80
|
||||||
|
end
|
||||||
|
|
||||||
|
# (Unwayed segments removed)
|
||||||
|
|
||||||
|
# - Write shape
|
||||||
|
|
||||||
|
b += end_shape
|
||||||
|
r += [b].pack("B*")
|
||||||
|
m += swf_record(2, pack_u16(1) + pack_rect(xl, xr, yb, yt) + r)
|
||||||
|
m += swf_record(4, pack_u16(1) + pack_u16(1))
|
||||||
|
|
||||||
|
# - Create Flash header and write to browser
|
||||||
|
|
||||||
|
m += swf_record(1, "") # Show frame
|
||||||
|
m += swf_record(0, "") # End
|
||||||
|
|
||||||
|
m = pack_rect(bounds_left, bounds_right, bounds_bottom, bounds_top) + 0.chr + 12.chr + pack_u16(1) + m
|
||||||
|
m = "FWS" + 6.chr + pack_u32(m.length + 8) + m
|
||||||
|
|
||||||
|
render :body => m, :content_type => "application/x-shockwave-flash"
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# =======================================================================
|
||||||
|
# SWF functions
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Line-drawing
|
||||||
|
|
||||||
|
def start_shape
|
||||||
|
s = 0.chr # No fill styles
|
||||||
|
s += 2.chr # Two line styles
|
||||||
|
s += pack_u16(0) + 0.chr + 255.chr + 255.chr # Width 5, RGB #00FFFF
|
||||||
|
s += pack_u16(0) + 255.chr + 0.chr + 255.chr # Width 5, RGB #FF00FF
|
||||||
|
s += 34.chr # 2 fill, 2 line index bits
|
||||||
|
s
|
||||||
|
end
|
||||||
|
|
||||||
|
def end_shape
|
||||||
|
"000000"
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_and_move(x, y, col)
|
||||||
|
d = "001001" # Line style change, moveTo
|
||||||
|
l = [length_sb(x), length_sb(y)].max
|
||||||
|
d += format("%05b%0*b%0*b", l, l, x, l, y)
|
||||||
|
d += col # Select line style
|
||||||
|
d
|
||||||
|
end
|
||||||
|
|
||||||
|
def draw_to(absx, absy, x, y)
|
||||||
|
dx = x - absx
|
||||||
|
dy = y - absy
|
||||||
|
|
||||||
|
# Split the line up if there's anything>16383, because
|
||||||
|
# that would overflow the 4 bits allowed for length
|
||||||
|
mstep = [dx.abs / 16383, dy.abs / 16383, 1].max.ceil
|
||||||
|
xstep = dx / mstep
|
||||||
|
ystep = dy / mstep
|
||||||
|
d = ""
|
||||||
|
1.upto(mstep).each do
|
||||||
|
d += draw_section(x, y, x + xstep, y + ystep)
|
||||||
|
x += xstep
|
||||||
|
y += ystep
|
||||||
|
end
|
||||||
|
d
|
||||||
|
end
|
||||||
|
|
||||||
|
def draw_section(x1, y1, x2, y2)
|
||||||
|
d = "11" # TypeFlag, EdgeFlag
|
||||||
|
dx = x2 - x1
|
||||||
|
dy = y2 - y1
|
||||||
|
l = [length_sb(dx), length_sb(dy)].max
|
||||||
|
d += format("%04b", l - 2)
|
||||||
|
d += "1" # GeneralLine
|
||||||
|
d += format("%0*b%0*b", l, dx, l, dy)
|
||||||
|
d
|
||||||
|
end
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Specific data types
|
||||||
|
|
||||||
|
# SWF data block type
|
||||||
|
|
||||||
|
def swf_record(id, r)
|
||||||
|
if r.length > 62
|
||||||
|
# Long header: tag id, 0x3F, length
|
||||||
|
pack_u16((id << 6) + 0x3F) + pack_u32(r.length) + r
|
||||||
|
else
|
||||||
|
# Short header: tag id, length
|
||||||
|
pack_u16((id << 6) + r.length) + r
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# SWF RECT type
|
||||||
|
|
||||||
|
def pack_rect(a, b, c, d)
|
||||||
|
l = [length_sb(a),
|
||||||
|
length_sb(b),
|
||||||
|
length_sb(c),
|
||||||
|
length_sb(d)].max
|
||||||
|
# create binary string (00111001 etc.) - 5-byte length, then bbox
|
||||||
|
n = format("%05b%0*b%0*b%0*b%0*b", l, l, a, l, b, l, c, l, d)
|
||||||
|
# pack into byte string
|
||||||
|
[n].pack("B*")
|
||||||
|
end
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Generic pack functions
|
||||||
|
|
||||||
|
def pack_u16(n)
|
||||||
|
[n.floor].pack("v")
|
||||||
|
end
|
||||||
|
|
||||||
|
def pack_u32(n)
|
||||||
|
[n.floor].pack("V")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find number of bits required to store arbitrary-length binary
|
||||||
|
|
||||||
|
def length_sb(n)
|
||||||
|
Math.frexp(n + (n.zero? ? 1 : 0))[1] + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# Co-ordinate conversion
|
||||||
|
# (this is duplicated from amf_controller, should probably share)
|
||||||
|
|
||||||
|
def lat2coord(a, basey, masterscale)
|
||||||
|
-(lat2y(a) - basey) * masterscale
|
||||||
|
end
|
||||||
|
|
||||||
|
def long2coord(a, baselong, masterscale)
|
||||||
|
(a - baselong) * masterscale
|
||||||
|
end
|
||||||
|
|
||||||
|
def lat2y(a)
|
||||||
|
180 / Math::PI * Math.log(Math.tan(Math::PI / 4 + a * (Math::PI / 180) / 2))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
169
app/controllers/api/traces_controller.rb
Normal file
169
app/controllers/api/traces_controller.rb
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
module Api
|
||||||
|
class TracesController < ApplicationController
|
||||||
|
layout "site", :except => :georss
|
||||||
|
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
before_action :authorize_web
|
||||||
|
before_action :set_locale
|
||||||
|
before_action :authorize
|
||||||
|
before_action :api_deny_access_handler
|
||||||
|
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
|
before_action :check_database_readable, :except => [:api_read, :api_data]
|
||||||
|
before_action :check_database_writable, :only => [:api_create, :api_update, :api_delete]
|
||||||
|
before_action :check_api_readable, :only => [:api_read, :api_data]
|
||||||
|
before_action :check_api_writable, :only => [:api_create, :api_update, :api_delete]
|
||||||
|
before_action :offline_redirect, :only => [:api_create, :api_delete, :api_data]
|
||||||
|
around_action :api_call_handle_error
|
||||||
|
|
||||||
|
def api_read
|
||||||
|
trace = Trace.visible.find(params[:id])
|
||||||
|
|
||||||
|
if trace.public? || trace.user == current_user
|
||||||
|
render :xml => trace.to_xml.to_s
|
||||||
|
else
|
||||||
|
head :forbidden
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def api_update
|
||||||
|
trace = Trace.visible.find(params[:id])
|
||||||
|
|
||||||
|
if trace.user == current_user
|
||||||
|
trace.update_from_xml(request.raw_post)
|
||||||
|
trace.save!
|
||||||
|
|
||||||
|
head :ok
|
||||||
|
else
|
||||||
|
head :forbidden
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def api_delete
|
||||||
|
trace = Trace.visible.find(params[:id])
|
||||||
|
|
||||||
|
if trace.user == current_user
|
||||||
|
trace.visible = false
|
||||||
|
trace.save!
|
||||||
|
|
||||||
|
head :ok
|
||||||
|
else
|
||||||
|
head :forbidden
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def api_data
|
||||||
|
trace = Trace.visible.find(params[:id])
|
||||||
|
|
||||||
|
if trace.public? || trace.user == current_user
|
||||||
|
if request.format == Mime[:xml]
|
||||||
|
send_data(trace.xml_file.read, :filename => "#{trace.id}.xml", :type => request.format.to_s, :disposition => "attachment")
|
||||||
|
elsif request.format == Mime[:gpx]
|
||||||
|
send_data(trace.xml_file.read, :filename => "#{trace.id}.gpx", :type => request.format.to_s, :disposition => "attachment")
|
||||||
|
else
|
||||||
|
send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => "attachment")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
head :forbidden
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def api_create
|
||||||
|
tags = params[:tags] || ""
|
||||||
|
description = params[:description] || ""
|
||||||
|
visibility = params[:visibility]
|
||||||
|
|
||||||
|
if visibility.nil?
|
||||||
|
visibility = if params[:public]&.to_i&.nonzero?
|
||||||
|
"public"
|
||||||
|
else
|
||||||
|
"private"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if params[:file].respond_to?(:read)
|
||||||
|
trace = do_create(params[:file], tags, description, visibility)
|
||||||
|
|
||||||
|
if trace.id
|
||||||
|
render :plain => trace.id.to_s
|
||||||
|
elsif trace.valid?
|
||||||
|
head :internal_server_error
|
||||||
|
else
|
||||||
|
head :bad_request
|
||||||
|
end
|
||||||
|
else
|
||||||
|
head :bad_request
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def do_create(file, tags, description, visibility)
|
||||||
|
# Sanitise the user's filename
|
||||||
|
name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, "_")
|
||||||
|
|
||||||
|
# Get a temporary filename...
|
||||||
|
filename = "/tmp/#{rand}"
|
||||||
|
|
||||||
|
# ...and save the uploaded file to that location
|
||||||
|
File.open(filename, "wb") { |f| f.write(file.read) }
|
||||||
|
|
||||||
|
# Create the trace object, falsely marked as already
|
||||||
|
# inserted to stop the import daemon trying to load it
|
||||||
|
trace = Trace.new(
|
||||||
|
:name => name,
|
||||||
|
:tagstring => tags,
|
||||||
|
:description => description,
|
||||||
|
:visibility => visibility,
|
||||||
|
:inserted => true,
|
||||||
|
:user => current_user,
|
||||||
|
:timestamp => Time.now.getutc
|
||||||
|
)
|
||||||
|
|
||||||
|
if trace.valid?
|
||||||
|
Trace.transaction do
|
||||||
|
begin
|
||||||
|
# Save the trace object
|
||||||
|
trace.save!
|
||||||
|
|
||||||
|
# Rename the temporary file to the final name
|
||||||
|
FileUtils.mv(filename, trace.trace_name)
|
||||||
|
rescue StandardError
|
||||||
|
# Remove the file as we have failed to update the database
|
||||||
|
FileUtils.rm_f(filename)
|
||||||
|
|
||||||
|
# Pass the exception on
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
# Clear the inserted flag to make the import daemon load the trace
|
||||||
|
trace.inserted = false
|
||||||
|
trace.save!
|
||||||
|
rescue StandardError
|
||||||
|
# Remove the file as we have failed to update the database
|
||||||
|
FileUtils.rm_f(trace.trace_name)
|
||||||
|
|
||||||
|
# Pass the exception on
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Finally save the user's preferred privacy level
|
||||||
|
if pref = current_user.preferences.where(:k => "gps.trace.visibility").first
|
||||||
|
pref.v = visibility
|
||||||
|
pref.save
|
||||||
|
else
|
||||||
|
current_user.preferences.create(:k => "gps.trace.visibility", :v => visibility)
|
||||||
|
end
|
||||||
|
|
||||||
|
trace
|
||||||
|
end
|
||||||
|
|
||||||
|
def offline_redirect
|
||||||
|
redirect_to :action => :offline if STATUS == :gpx_offline
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
90
app/controllers/api/user_preferences_controller.rb
Normal file
90
app/controllers/api/user_preferences_controller.rb
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
# Update and read user preferences, which are arbitrayr key/val pairs
|
||||||
|
module Api
|
||||||
|
class UserPreferencesController < ApplicationController
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
before_action :authorize
|
||||||
|
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
|
around_action :api_call_handle_error
|
||||||
|
|
||||||
|
##
|
||||||
|
# return all the preferences as an XML document
|
||||||
|
def read
|
||||||
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
|
||||||
|
prefs = current_user.preferences
|
||||||
|
|
||||||
|
el1 = XML::Node.new "preferences"
|
||||||
|
|
||||||
|
prefs.each do |pref|
|
||||||
|
el1 << pref.to_xml_node
|
||||||
|
end
|
||||||
|
|
||||||
|
doc.root << el1
|
||||||
|
render :xml => doc.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# return the value for a single preference
|
||||||
|
def read_one
|
||||||
|
pref = UserPreference.find([current_user.id, params[:preference_key]])
|
||||||
|
|
||||||
|
render :plain => pref.v.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# update the entire set of preferences
|
||||||
|
def update
|
||||||
|
old_preferences = current_user.preferences.each_with_object({}) do |preference, preferences|
|
||||||
|
preferences[preference.k] = preference
|
||||||
|
end
|
||||||
|
|
||||||
|
new_preferences = {}
|
||||||
|
|
||||||
|
doc = XML::Parser.string(request.raw_post, :options => XML::Parser::Options::NOERROR).parse
|
||||||
|
|
||||||
|
doc.find("//preferences/preference").each do |pt|
|
||||||
|
if preference = old_preferences.delete(pt["k"])
|
||||||
|
preference.v = pt["v"]
|
||||||
|
elsif new_preferences.include?(pt["k"])
|
||||||
|
raise OSM::APIDuplicatePreferenceError, pt["k"]
|
||||||
|
else
|
||||||
|
preference = current_user.preferences.build(:k => pt["k"], :v => pt["v"])
|
||||||
|
end
|
||||||
|
|
||||||
|
new_preferences[preference.k] = preference
|
||||||
|
end
|
||||||
|
|
||||||
|
old_preferences.each_value(&:delete)
|
||||||
|
|
||||||
|
new_preferences.each_value(&:save!)
|
||||||
|
|
||||||
|
render :plain => ""
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# update the value of a single preference
|
||||||
|
def update_one
|
||||||
|
begin
|
||||||
|
pref = UserPreference.find([current_user.id, params[:preference_key]])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
pref = UserPreference.new
|
||||||
|
pref.user = current_user
|
||||||
|
pref.k = params[:preference_key]
|
||||||
|
end
|
||||||
|
|
||||||
|
pref.v = request.raw_post.chomp
|
||||||
|
pref.save!
|
||||||
|
|
||||||
|
render :plain => ""
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# delete a single preference
|
||||||
|
def delete_one
|
||||||
|
UserPreference.find([current_user.id, params[:preference_key]]).delete
|
||||||
|
|
||||||
|
render :plain => ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
66
app/controllers/api/users_controller.rb
Normal file
66
app/controllers/api/users_controller.rb
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
module Api
|
||||||
|
class UsersController < ApplicationController
|
||||||
|
layout "site", :except => [:api_details]
|
||||||
|
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
before_action :disable_terms_redirect, :only => [:api_details]
|
||||||
|
before_action :authorize, :only => [:api_details, :api_gpx_files]
|
||||||
|
before_action :api_deny_access_handler
|
||||||
|
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
|
before_action :check_api_readable
|
||||||
|
around_action :api_call_handle_error
|
||||||
|
before_action :lookup_user_by_id, :only => [:api_read]
|
||||||
|
|
||||||
|
def api_read
|
||||||
|
if @user.visible?
|
||||||
|
render :action => :api_read, :content_type => "text/xml"
|
||||||
|
else
|
||||||
|
head :gone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def api_details
|
||||||
|
@user = current_user
|
||||||
|
render :action => :api_read, :content_type => "text/xml"
|
||||||
|
end
|
||||||
|
|
||||||
|
def api_users
|
||||||
|
raise OSM::APIBadUserInput, "The parameter users is required, and must be of the form users=id[,id[,id...]]" unless params["users"]
|
||||||
|
|
||||||
|
ids = params["users"].split(",").collect(&:to_i)
|
||||||
|
|
||||||
|
raise OSM::APIBadUserInput, "No users were given to search for" if ids.empty?
|
||||||
|
|
||||||
|
@users = User.visible.find(ids)
|
||||||
|
|
||||||
|
render :action => :api_users, :content_type => "text/xml"
|
||||||
|
end
|
||||||
|
|
||||||
|
def api_gpx_files
|
||||||
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
current_user.traces.reload.each do |trace|
|
||||||
|
doc.root << trace.to_xml_node
|
||||||
|
end
|
||||||
|
render :xml => doc.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
##
|
||||||
|
# ensure that there is a "user" instance variable
|
||||||
|
def lookup_user_by_id
|
||||||
|
@user = User.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
def disable_terms_redirect
|
||||||
|
# this is necessary otherwise going to the user terms page, when
|
||||||
|
# having not agreed already would cause an infinite redirect loop.
|
||||||
|
# it's .now so that this doesn't propagate to other pages.
|
||||||
|
flash.now[:skip_terms] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
120
app/controllers/api/ways_controller.rb
Normal file
120
app/controllers/api/ways_controller.rb
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
module Api
|
||||||
|
class WaysController < ApplicationController
|
||||||
|
require "xml/libxml"
|
||||||
|
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
before_action :authorize, :only => [:create, :update, :delete]
|
||||||
|
before_action :api_deny_access_handler
|
||||||
|
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
|
before_action :require_public_data, :only => [:create, :update, :delete]
|
||||||
|
before_action :check_api_writable, :only => [:create, :update, :delete]
|
||||||
|
before_action :check_api_readable, :except => [:create, :update, :delete]
|
||||||
|
around_action :api_call_handle_error, :api_call_timeout
|
||||||
|
|
||||||
|
def create
|
||||||
|
assert_method :put
|
||||||
|
|
||||||
|
way = Way.from_xml(request.raw_post, true)
|
||||||
|
|
||||||
|
# Assume that Way.from_xml has thrown an exception if there is an error parsing the xml
|
||||||
|
way.create_with_history current_user
|
||||||
|
render :plain => way.id.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
way = Way.find(params[:id])
|
||||||
|
|
||||||
|
response.last_modified = way.timestamp
|
||||||
|
|
||||||
|
if way.visible
|
||||||
|
render :xml => way.to_xml.to_s
|
||||||
|
else
|
||||||
|
head :gone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
way = Way.find(params[:id])
|
||||||
|
new_way = Way.from_xml(request.raw_post)
|
||||||
|
|
||||||
|
unless new_way && new_way.id == way.id
|
||||||
|
raise OSM::APIBadUserInput, "The id in the url (#{way.id}) is not the same as provided in the xml (#{new_way.id})"
|
||||||
|
end
|
||||||
|
|
||||||
|
way.update_from(new_way, current_user)
|
||||||
|
render :plain => way.version.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# This is the API call to delete a way
|
||||||
|
def delete
|
||||||
|
way = Way.find(params[:id])
|
||||||
|
new_way = Way.from_xml(request.raw_post)
|
||||||
|
|
||||||
|
if new_way && new_way.id == way.id
|
||||||
|
way.delete_with_history!(new_way, current_user)
|
||||||
|
render :plain => way.version.to_s
|
||||||
|
else
|
||||||
|
head :bad_request
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def full
|
||||||
|
way = Way.includes(:nodes => :node_tags).find(params[:id])
|
||||||
|
|
||||||
|
if way.visible
|
||||||
|
visible_nodes = {}
|
||||||
|
changeset_cache = {}
|
||||||
|
user_display_name_cache = {}
|
||||||
|
|
||||||
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
way.nodes.uniq.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
|
||||||
|
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
|
||||||
|
|
||||||
|
render :xml => doc.to_s
|
||||||
|
else
|
||||||
|
head :gone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def index
|
||||||
|
unless params["ways"]
|
||||||
|
raise OSM::APIBadUserInput, "The parameter ways is required, and must be of the form ways=id[,id[,id...]]"
|
||||||
|
end
|
||||||
|
|
||||||
|
ids = params["ways"].split(",").collect(&:to_i)
|
||||||
|
|
||||||
|
raise OSM::APIBadUserInput, "No ways were given to search for" if ids.empty?
|
||||||
|
|
||||||
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
|
||||||
|
Way.find(ids).each do |way|
|
||||||
|
doc.root << way.to_xml_node
|
||||||
|
end
|
||||||
|
|
||||||
|
render :xml => doc.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# returns all the ways which are currently using the node given in the
|
||||||
|
# :id parameter. note that this used to return deleted ways as well, but
|
||||||
|
# this seemed not to be the expected behaviour, so it was removed.
|
||||||
|
def ways_for_node
|
||||||
|
wayids = WayNode.where(:node_id => params[:id]).collect { |ws| ws.id[0] }.uniq
|
||||||
|
|
||||||
|
doc = OSM::API.new.get_xml_doc
|
||||||
|
|
||||||
|
Way.find(wayids).each do |way|
|
||||||
|
doc.root << way.to_xml_node if way.visible
|
||||||
|
end
|
||||||
|
|
||||||
|
render :xml => doc.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,89 +1,11 @@
|
||||||
class ChangesetCommentsController < ApplicationController
|
class ChangesetCommentsController < ApplicationController
|
||||||
skip_before_action :verify_authenticity_token, :except => [:index]
|
before_action :authorize_web
|
||||||
before_action :authorize_web, :only => [:index]
|
before_action :set_locale
|
||||||
before_action :set_locale, :only => [:index]
|
|
||||||
before_action :authorize, :only => [:create, :destroy, :restore]
|
|
||||||
before_action :api_deny_access_handler, :only => [:create, :destroy, :restore]
|
|
||||||
|
|
||||||
authorize_resource
|
authorize_resource
|
||||||
|
|
||||||
before_action :require_public_data, :only => [:create]
|
|
||||||
before_action :check_api_writable, :only => [:create, :destroy, :restore]
|
|
||||||
before_action :check_api_readable, :except => [:create, :index]
|
|
||||||
before_action(:only => [:index]) { |c| c.check_database_readable(true) }
|
before_action(:only => [:index]) { |c| c.check_database_readable(true) }
|
||||||
around_action :api_call_handle_error, :except => [:index]
|
around_action :web_timeout
|
||||||
around_action :api_call_timeout, :except => [:index]
|
|
||||||
around_action :web_timeout, :only => [:index]
|
|
||||||
|
|
||||||
##
|
|
||||||
# Add a comment to a changeset
|
|
||||||
def create
|
|
||||||
# Check the arguments are sane
|
|
||||||
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
|
||||||
raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
|
|
||||||
|
|
||||||
# Extract the arguments
|
|
||||||
id = params[:id].to_i
|
|
||||||
body = params[:text]
|
|
||||||
|
|
||||||
# Find the changeset and check it is valid
|
|
||||||
changeset = Changeset.find(id)
|
|
||||||
raise OSM::APIChangesetNotYetClosedError, changeset if changeset.is_open?
|
|
||||||
|
|
||||||
# Add a comment to the changeset
|
|
||||||
comment = changeset.comments.create(:changeset => changeset,
|
|
||||||
:body => body,
|
|
||||||
:author => current_user)
|
|
||||||
|
|
||||||
# Notify current subscribers of the new comment
|
|
||||||
changeset.subscribers.visible.each do |user|
|
|
||||||
Notifier.changeset_comment_notification(comment, user).deliver_later if current_user != user
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add the commenter to the subscribers if necessary
|
|
||||||
changeset.subscribers << current_user unless changeset.subscribers.exists?(current_user.id)
|
|
||||||
|
|
||||||
# Return a copy of the updated changeset
|
|
||||||
render :xml => changeset.to_xml.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Sets visible flag on comment to false
|
|
||||||
def destroy
|
|
||||||
# Check the arguments are sane
|
|
||||||
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
|
||||||
|
|
||||||
# Extract the arguments
|
|
||||||
id = params[:id].to_i
|
|
||||||
|
|
||||||
# Find the changeset
|
|
||||||
comment = ChangesetComment.find(id)
|
|
||||||
|
|
||||||
# Hide the comment
|
|
||||||
comment.update(:visible => false)
|
|
||||||
|
|
||||||
# Return a copy of the updated changeset
|
|
||||||
render :xml => comment.changeset.to_xml.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Sets visible flag on comment to true
|
|
||||||
def restore
|
|
||||||
# Check the arguments are sane
|
|
||||||
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
|
||||||
|
|
||||||
# Extract the arguments
|
|
||||||
id = params[:id].to_i
|
|
||||||
|
|
||||||
# Find the changeset
|
|
||||||
comment = ChangesetComment.find(id)
|
|
||||||
|
|
||||||
# Unhide the comment
|
|
||||||
comment.update(:visible => true)
|
|
||||||
|
|
||||||
# Return a copy of the updated changeset
|
|
||||||
render :xml => comment.changeset.to_xml.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Get a feed of recent changeset comments
|
# Get a feed of recent changeset comments
|
||||||
|
|
|
@ -5,256 +5,17 @@ class ChangesetsController < ApplicationController
|
||||||
require "xml/libxml"
|
require "xml/libxml"
|
||||||
|
|
||||||
skip_before_action :verify_authenticity_token, :except => [:index]
|
skip_before_action :verify_authenticity_token, :except => [:index]
|
||||||
before_action :authorize_web, :only => [:index, :feed]
|
before_action :authorize_web
|
||||||
before_action :set_locale, :only => [:index, :feed]
|
before_action :set_locale
|
||||||
before_action :authorize, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe]
|
|
||||||
before_action :api_deny_access_handler, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe, :expand_bbox]
|
|
||||||
|
|
||||||
authorize_resource
|
authorize_resource
|
||||||
|
|
||||||
before_action :require_public_data, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe]
|
|
||||||
before_action :check_api_writable, :only => [:create, :update, :upload, :subscribe, :unsubscribe]
|
|
||||||
before_action :check_api_readable, :except => [:create, :update, :upload, :download, :query, :index, :feed, :subscribe, :unsubscribe]
|
|
||||||
before_action(:only => [:index, :feed]) { |c| c.check_database_readable(true) }
|
before_action(:only => [:index, :feed]) { |c| c.check_database_readable(true) }
|
||||||
around_action :api_call_handle_error, :except => [:index, :feed]
|
around_action :web_timeout
|
||||||
around_action :api_call_timeout, :except => [:index, :feed, :upload]
|
|
||||||
around_action :web_timeout, :only => [:index, :feed]
|
|
||||||
|
|
||||||
# Helper methods for checking consistency
|
# Helper methods for checking consistency
|
||||||
include ConsistencyValidations
|
include ConsistencyValidations
|
||||||
|
|
||||||
# Create a changeset from XML.
|
|
||||||
def create
|
|
||||||
assert_method :put
|
|
||||||
|
|
||||||
cs = Changeset.from_xml(request.raw_post, true)
|
|
||||||
|
|
||||||
# Assume that Changeset.from_xml has thrown an exception if there is an error parsing the xml
|
|
||||||
cs.user = current_user
|
|
||||||
cs.save_with_tags!
|
|
||||||
|
|
||||||
# Subscribe user to changeset comments
|
|
||||||
cs.subscribers << current_user
|
|
||||||
|
|
||||||
render :plain => cs.id.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Return XML giving the basic info about the changeset. Does not
|
|
||||||
# return anything about the nodes, ways and relations in the changeset.
|
|
||||||
def show
|
|
||||||
changeset = Changeset.find(params[:id])
|
|
||||||
|
|
||||||
render :xml => changeset.to_xml(params[:include_discussion].presence).to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# marks a changeset as closed. this may be called multiple times
|
|
||||||
# on the same changeset, so is idempotent.
|
|
||||||
def close
|
|
||||||
assert_method :put
|
|
||||||
|
|
||||||
changeset = Changeset.find(params[:id])
|
|
||||||
check_changeset_consistency(changeset, current_user)
|
|
||||||
|
|
||||||
# to close the changeset, we'll just set its closed_at time to
|
|
||||||
# now. this might not be enough if there are concurrency issues,
|
|
||||||
# but we'll have to wait and see.
|
|
||||||
changeset.set_closed_time_now
|
|
||||||
|
|
||||||
changeset.save!
|
|
||||||
head :ok
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# insert a (set of) points into a changeset bounding box. this can only
|
|
||||||
# increase the size of the bounding box. this is a hint that clients can
|
|
||||||
# set either before uploading a large number of changes, or changes that
|
|
||||||
# the client (but not the server) knows will affect areas further away.
|
|
||||||
def expand_bbox
|
|
||||||
# only allow POST requests, because although this method is
|
|
||||||
# idempotent, there is no "document" to PUT really...
|
|
||||||
assert_method :post
|
|
||||||
|
|
||||||
cs = Changeset.find(params[:id])
|
|
||||||
check_changeset_consistency(cs, current_user)
|
|
||||||
|
|
||||||
# keep an array of lons and lats
|
|
||||||
lon = []
|
|
||||||
lat = []
|
|
||||||
|
|
||||||
# the request is in pseudo-osm format... this is kind-of an
|
|
||||||
# abuse, maybe should change to some other format?
|
|
||||||
doc = XML::Parser.string(request.raw_post, :options => XML::Parser::Options::NOERROR).parse
|
|
||||||
doc.find("//osm/node").each do |n|
|
|
||||||
lon << n["lon"].to_f * GeoRecord::SCALE
|
|
||||||
lat << n["lat"].to_f * GeoRecord::SCALE
|
|
||||||
end
|
|
||||||
|
|
||||||
# add the existing bounding box to the lon-lat array
|
|
||||||
lon << cs.min_lon unless cs.min_lon.nil?
|
|
||||||
lat << cs.min_lat unless cs.min_lat.nil?
|
|
||||||
lon << cs.max_lon unless cs.max_lon.nil?
|
|
||||||
lat << cs.max_lat unless cs.max_lat.nil?
|
|
||||||
|
|
||||||
# collapse the arrays to minimum and maximum
|
|
||||||
cs.min_lon = lon.min
|
|
||||||
cs.min_lat = lat.min
|
|
||||||
cs.max_lon = lon.max
|
|
||||||
cs.max_lat = lat.max
|
|
||||||
|
|
||||||
# save the larger bounding box and return the changeset, which
|
|
||||||
# will include the bigger bounding box.
|
|
||||||
cs.save!
|
|
||||||
render :xml => cs.to_xml.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Upload a diff in a single transaction.
|
|
||||||
#
|
|
||||||
# This means that each change within the diff must succeed, i.e: that
|
|
||||||
# each version number mentioned is still current. Otherwise the entire
|
|
||||||
# transaction *must* be rolled back.
|
|
||||||
#
|
|
||||||
# Furthermore, each element in the diff can only reference the current
|
|
||||||
# changeset.
|
|
||||||
#
|
|
||||||
# Returns: a diffResult document, as described in
|
|
||||||
# http://wiki.openstreetmap.org/wiki/OSM_Protocol_Version_0.6
|
|
||||||
def upload
|
|
||||||
# only allow POST requests, as the upload method is most definitely
|
|
||||||
# not idempotent, as several uploads with placeholder IDs will have
|
|
||||||
# different side-effects.
|
|
||||||
# see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.2
|
|
||||||
assert_method :post
|
|
||||||
|
|
||||||
changeset = Changeset.find(params[:id])
|
|
||||||
check_changeset_consistency(changeset, current_user)
|
|
||||||
|
|
||||||
diff_reader = DiffReader.new(request.raw_post, changeset)
|
|
||||||
Changeset.transaction do
|
|
||||||
result = diff_reader.commit
|
|
||||||
render :xml => result.to_s
|
|
||||||
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! do |a, b|
|
|
||||||
if a.timestamp == b.timestamp
|
|
||||||
a.version <=> b.version
|
|
||||||
else
|
|
||||||
a.timestamp <=> b.timestamp
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# create changeset and user caches
|
|
||||||
changeset_cache = {}
|
|
||||||
user_display_name_cache = {}
|
|
||||||
|
|
||||||
# create an osmChange document for the output
|
|
||||||
result = OSM::API.new.get_xml_doc
|
|
||||||
result.root.name = "osmChange"
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
elements.each do |elt|
|
|
||||||
result.root <<
|
|
||||||
if elt.version == 1
|
|
||||||
# first version, so it must be newly-created.
|
|
||||||
created = XML::Node.new "create"
|
|
||||||
created << elt.to_xml_node(changeset_cache, user_display_name_cache)
|
|
||||||
elsif elt.visible
|
|
||||||
# must be a modify
|
|
||||||
modified = XML::Node.new "modify"
|
|
||||||
modified << elt.to_xml_node(changeset_cache, user_display_name_cache)
|
|
||||||
else
|
|
||||||
# if the element isn't visible then it must have been deleted
|
|
||||||
deleted = XML::Node.new "delete"
|
|
||||||
deleted << elt.to_xml_node(changeset_cache, user_display_name_cache)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
render :xml => result.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# query changesets by bounding box, time, user or open/closed status.
|
|
||||||
def query
|
|
||||||
# find any bounding box
|
|
||||||
bbox = BoundingBox.from_bbox_params(params) if params["bbox"]
|
|
||||||
|
|
||||||
# create the conditions that the user asked for. some or all of
|
|
||||||
# these may be nil.
|
|
||||||
changesets = Changeset.all
|
|
||||||
changesets = conditions_bbox(changesets, bbox)
|
|
||||||
changesets = conditions_user(changesets, params["user"], params["display_name"])
|
|
||||||
changesets = conditions_time(changesets, params["time"])
|
|
||||||
changesets = conditions_open(changesets, params["open"])
|
|
||||||
changesets = conditions_closed(changesets, params["closed"])
|
|
||||||
changesets = conditions_ids(changesets, params["changesets"])
|
|
||||||
|
|
||||||
# sort and limit the changesets
|
|
||||||
changesets = changesets.order("created_at DESC").limit(100)
|
|
||||||
|
|
||||||
# preload users, tags and comments
|
|
||||||
changesets = changesets.preload(:user, :changeset_tags, :comments)
|
|
||||||
|
|
||||||
# create the results document
|
|
||||||
results = OSM::API.new.get_xml_doc
|
|
||||||
|
|
||||||
# add all matching changesets to the XML results document
|
|
||||||
changesets.order("created_at DESC").limit(100).each do |cs|
|
|
||||||
results.root << cs.to_xml_node
|
|
||||||
end
|
|
||||||
|
|
||||||
render :xml => results.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# updates a changeset's tags. none of the changeset's attributes are
|
|
||||||
# user-modifiable, so they will be ignored.
|
|
||||||
#
|
|
||||||
# changesets are not (yet?) versioned, so we don't have to deal with
|
|
||||||
# history tables here. changesets are locked to a single user, however.
|
|
||||||
#
|
|
||||||
# after succesful update, returns the XML of the changeset.
|
|
||||||
def update
|
|
||||||
# request *must* be a PUT.
|
|
||||||
assert_method :put
|
|
||||||
|
|
||||||
changeset = Changeset.find(params[:id])
|
|
||||||
new_changeset = Changeset.from_xml(request.raw_post)
|
|
||||||
|
|
||||||
check_changeset_consistency(changeset, current_user)
|
|
||||||
changeset.update_from(new_changeset, current_user)
|
|
||||||
render :xml => changeset.to_xml.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# list non-empty changesets in reverse chronological order
|
# list non-empty changesets in reverse chronological order
|
||||||
def index
|
def index
|
||||||
|
@ -312,46 +73,6 @@ class ChangesetsController < ApplicationController
|
||||||
index
|
index
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
|
||||||
# Adds a subscriber to the changeset
|
|
||||||
def subscribe
|
|
||||||
# Check the arguments are sane
|
|
||||||
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
|
||||||
|
|
||||||
# Extract the arguments
|
|
||||||
id = params[:id].to_i
|
|
||||||
|
|
||||||
# Find the changeset and check it is valid
|
|
||||||
changeset = Changeset.find(id)
|
|
||||||
raise OSM::APIChangesetAlreadySubscribedError, changeset if changeset.subscribers.exists?(current_user.id)
|
|
||||||
|
|
||||||
# Add the subscriber
|
|
||||||
changeset.subscribers << current_user
|
|
||||||
|
|
||||||
# Return a copy of the updated changeset
|
|
||||||
render :xml => changeset.to_xml.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Removes a subscriber from the changeset
|
|
||||||
def unsubscribe
|
|
||||||
# Check the arguments are sane
|
|
||||||
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
|
||||||
|
|
||||||
# Extract the arguments
|
|
||||||
id = params[:id].to_i
|
|
||||||
|
|
||||||
# Find the changeset and check it is valid
|
|
||||||
changeset = Changeset.find(id)
|
|
||||||
raise OSM::APIChangesetNotSubscribedError, changeset unless changeset.subscribers.exists?(current_user.id)
|
|
||||||
|
|
||||||
# Remove the subscriber
|
|
||||||
changeset.subscribers.delete(current_user)
|
|
||||||
|
|
||||||
# Return a copy of the updated changeset
|
|
||||||
render :xml => changeset.to_xml.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
@ -375,109 +96,6 @@ class ChangesetsController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
|
||||||
# restrict changesets to those by a particular user
|
|
||||||
def conditions_user(changesets, user, name)
|
|
||||||
if user.nil? && name.nil?
|
|
||||||
changesets
|
|
||||||
else
|
|
||||||
# shouldn't provide both name and UID
|
|
||||||
raise OSM::APIBadUserInput, "provide either the user ID or display name, but not both" if user && name
|
|
||||||
|
|
||||||
# use either the name or the UID to find the user which we're selecting on.
|
|
||||||
u = if name.nil?
|
|
||||||
# user input checking, we don't have any UIDs < 1
|
|
||||||
raise OSM::APIBadUserInput, "invalid user ID" if user.to_i < 1
|
|
||||||
|
|
||||||
u = User.find(user.to_i)
|
|
||||||
else
|
|
||||||
u = User.find_by(:display_name => name)
|
|
||||||
end
|
|
||||||
|
|
||||||
# make sure we found a user
|
|
||||||
raise OSM::APINotFoundError if u.nil?
|
|
||||||
|
|
||||||
# should be able to get changesets of public users only, or
|
|
||||||
# our own changesets regardless of public-ness.
|
|
||||||
unless u.data_public?
|
|
||||||
# get optional user auth stuff so that users can see their own
|
|
||||||
# changesets if they're non-public
|
|
||||||
setup_user_auth
|
|
||||||
|
|
||||||
raise OSM::APINotFoundError if current_user.nil? || current_user != u
|
|
||||||
end
|
|
||||||
|
|
||||||
changesets.where(:user_id => u.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# restrict changes to those closed during a particular time period
|
|
||||||
def conditions_time(changesets, time)
|
|
||||||
if time.nil?
|
|
||||||
changesets
|
|
||||||
elsif time.count(",") == 1
|
|
||||||
# if there is a range, i.e: comma separated, then the first is
|
|
||||||
# low, second is high - same as with bounding boxes.
|
|
||||||
|
|
||||||
# check that we actually have 2 elements in the array
|
|
||||||
times = time.split(/,/)
|
|
||||||
raise OSM::APIBadUserInput, "bad time range" if times.size != 2
|
|
||||||
|
|
||||||
from, to = times.collect { |t| Time.parse(t) }
|
|
||||||
changesets.where("closed_at >= ? and created_at <= ?", from, to)
|
|
||||||
else
|
|
||||||
# if there is no comma, assume its a lower limit on time
|
|
||||||
changesets.where("closed_at >= ?", Time.parse(time))
|
|
||||||
end
|
|
||||||
# stupid Time seems to throw both of these for bad parsing, so
|
|
||||||
# we have to catch both and ensure the correct code path is taken.
|
|
||||||
rescue ArgumentError => ex
|
|
||||||
raise OSM::APIBadUserInput, ex.message.to_s
|
|
||||||
rescue RuntimeError => ex
|
|
||||||
raise OSM::APIBadUserInput, ex.message.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# return changesets which are open (haven't been closed yet)
|
|
||||||
# we do this by seeing if the 'closed at' time is in the future. Also if we've
|
|
||||||
# hit the maximum number of changes then it counts as no longer open.
|
|
||||||
# if parameter 'open' is nill then open and closed changesets are returned
|
|
||||||
def conditions_open(changesets, open)
|
|
||||||
if open.nil?
|
|
||||||
changesets
|
|
||||||
else
|
|
||||||
changesets.where("closed_at >= ? and num_changes <= ?",
|
|
||||||
Time.now.getutc, Changeset::MAX_ELEMENTS)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# query changesets which are closed
|
|
||||||
# ('closed at' time has passed or changes limit is hit)
|
|
||||||
def conditions_closed(changesets, closed)
|
|
||||||
if closed.nil?
|
|
||||||
changesets
|
|
||||||
else
|
|
||||||
changesets.where("closed_at < ? or num_changes > ?",
|
|
||||||
Time.now.getutc, Changeset::MAX_ELEMENTS)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# query changesets by a list of ids
|
|
||||||
# (either specified as array or comma-separated string)
|
|
||||||
def conditions_ids(changesets, ids)
|
|
||||||
if ids.nil?
|
|
||||||
changesets
|
|
||||||
elsif ids.empty?
|
|
||||||
raise OSM::APIBadUserInput, "No changesets were given to search for"
|
|
||||||
else
|
|
||||||
ids = ids.split(",").collect(&:to_i)
|
|
||||||
changesets.where(:id => ids)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# eliminate empty changesets (where the bbox has not been set)
|
# eliminate empty changesets (where the bbox has not been set)
|
||||||
# this should be applied to all changeset list displays
|
# this should be applied to all changeset list displays
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
# The NodeController is the RESTful interface to Node objects
|
|
||||||
|
|
||||||
class NodesController < ApplicationController
|
|
||||||
require "xml/libxml"
|
|
||||||
|
|
||||||
skip_before_action :verify_authenticity_token
|
|
||||||
before_action :authorize, :only => [:create, :update, :delete]
|
|
||||||
before_action :api_deny_access_handler
|
|
||||||
|
|
||||||
authorize_resource
|
|
||||||
|
|
||||||
before_action :require_public_data, :only => [:create, :update, :delete]
|
|
||||||
before_action :check_api_writable, :only => [:create, :update, :delete]
|
|
||||||
before_action :check_api_readable, :except => [:create, :update, :delete]
|
|
||||||
around_action :api_call_handle_error, :api_call_timeout
|
|
||||||
|
|
||||||
# Create a node from XML.
|
|
||||||
def create
|
|
||||||
assert_method :put
|
|
||||||
|
|
||||||
node = Node.from_xml(request.raw_post, true)
|
|
||||||
|
|
||||||
# Assume that Node.from_xml has thrown an exception if there is an error parsing the xml
|
|
||||||
node.create_with_history current_user
|
|
||||||
render :plain => node.id.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# Dump the details on a node given in params[:id]
|
|
||||||
def show
|
|
||||||
node = Node.find(params[:id])
|
|
||||||
|
|
||||||
response.last_modified = node.timestamp
|
|
||||||
|
|
||||||
if node.visible
|
|
||||||
render :xml => node.to_xml.to_s
|
|
||||||
else
|
|
||||||
head :gone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update a node from given XML
|
|
||||||
def update
|
|
||||||
node = Node.find(params[:id])
|
|
||||||
new_node = Node.from_xml(request.raw_post)
|
|
||||||
|
|
||||||
raise OSM::APIBadUserInput, "The id in the url (#{node.id}) is not the same as provided in the xml (#{new_node.id})" unless new_node && new_node.id == node.id
|
|
||||||
|
|
||||||
node.update_from(new_node, current_user)
|
|
||||||
render :plain => node.version.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# Delete a node. Doesn't actually delete it, but retains its history
|
|
||||||
# in a wiki-like way. We therefore treat it like an update, so the delete
|
|
||||||
# method returns the new version number.
|
|
||||||
def delete
|
|
||||||
node = Node.find(params[:id])
|
|
||||||
new_node = Node.from_xml(request.raw_post)
|
|
||||||
|
|
||||||
raise OSM::APIBadUserInput, "The id in the url (#{node.id}) is not the same as provided in the xml (#{new_node.id})" unless new_node && new_node.id == node.id
|
|
||||||
|
|
||||||
node.delete_with_history!(new_node, current_user)
|
|
||||||
render :plain => node.version.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# Dump the details on many nodes whose ids are given in the "nodes" parameter.
|
|
||||||
def index
|
|
||||||
raise OSM::APIBadUserInput, "The parameter nodes is required, and must be of the form nodes=id[,id[,id...]]" unless params["nodes"]
|
|
||||||
|
|
||||||
ids = params["nodes"].split(",").collect(&:to_i)
|
|
||||||
|
|
||||||
raise OSM::APIBadUserInput, "No nodes were given to search for" if ids.empty?
|
|
||||||
|
|
||||||
doc = OSM::API.new.get_xml_doc
|
|
||||||
|
|
||||||
Node.find(ids).each do |node|
|
|
||||||
doc.root << node.to_xml_node
|
|
||||||
end
|
|
||||||
|
|
||||||
render :xml => doc.to_s
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,316 +1,14 @@
|
||||||
class NotesController < ApplicationController
|
class NotesController < ApplicationController
|
||||||
layout "site", :only => [:mine]
|
layout "site", :only => [:mine]
|
||||||
|
|
||||||
skip_before_action :verify_authenticity_token, :except => [:mine]
|
|
||||||
before_action :check_api_readable
|
before_action :check_api_readable
|
||||||
before_action :authorize_web, :only => [:mine]
|
before_action :authorize_web
|
||||||
before_action :setup_user_auth, :only => [:create, :comment, :show]
|
|
||||||
before_action :authorize, :only => [:close, :reopen, :destroy]
|
|
||||||
before_action :api_deny_access_handler, :except => [:mine]
|
|
||||||
|
|
||||||
authorize_resource
|
authorize_resource
|
||||||
|
|
||||||
before_action :check_api_writable, :only => [:create, :comment, :close, :reopen, :destroy]
|
|
||||||
before_action :set_locale
|
before_action :set_locale
|
||||||
around_action :api_call_handle_error, :api_call_timeout
|
around_action :api_call_handle_error, :api_call_timeout
|
||||||
|
|
||||||
##
|
|
||||||
# Return a list of notes in a given area
|
|
||||||
def index
|
|
||||||
# Figure out the bbox - we prefer a bbox argument but also
|
|
||||||
# support the old, deprecated, method with four arguments
|
|
||||||
if params[:bbox]
|
|
||||||
bbox = BoundingBox.from_bbox_params(params)
|
|
||||||
else
|
|
||||||
raise OSM::APIBadUserInput, "No l was given" unless params[:l]
|
|
||||||
raise OSM::APIBadUserInput, "No r was given" unless params[:r]
|
|
||||||
raise OSM::APIBadUserInput, "No b was given" unless params[:b]
|
|
||||||
raise OSM::APIBadUserInput, "No t was given" unless params[:t]
|
|
||||||
|
|
||||||
bbox = BoundingBox.from_lrbt_params(params)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get any conditions that need to be applied
|
|
||||||
notes = closed_condition(Note.all)
|
|
||||||
|
|
||||||
# Check that the boundaries are valid
|
|
||||||
bbox.check_boundaries
|
|
||||||
|
|
||||||
# Check the the bounding box is not too big
|
|
||||||
bbox.check_size(MAX_NOTE_REQUEST_AREA)
|
|
||||||
|
|
||||||
# Find the notes we want to return
|
|
||||||
@notes = notes.bbox(bbox).order("updated_at DESC").limit(result_limit).preload(:comments)
|
|
||||||
|
|
||||||
# Render the result
|
|
||||||
respond_to do |format|
|
|
||||||
format.rss
|
|
||||||
format.xml
|
|
||||||
format.json
|
|
||||||
format.gpx
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Create a new note
|
|
||||||
def create
|
|
||||||
# Check the ACLs
|
|
||||||
raise OSM::APIAccessDenied if current_user.nil? && Acl.no_note_comment(request.remote_ip)
|
|
||||||
|
|
||||||
# Check the arguments are sane
|
|
||||||
raise OSM::APIBadUserInput, "No lat was given" unless params[:lat]
|
|
||||||
raise OSM::APIBadUserInput, "No lon was given" unless params[:lon]
|
|
||||||
raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
|
|
||||||
|
|
||||||
# Extract the arguments
|
|
||||||
lon = OSM.parse_float(params[:lon], OSM::APIBadUserInput, "lon was not a number")
|
|
||||||
lat = OSM.parse_float(params[:lat], OSM::APIBadUserInput, "lat was not a number")
|
|
||||||
comment = params[:text]
|
|
||||||
|
|
||||||
# Include in a transaction to ensure that there is always a note_comment for every note
|
|
||||||
Note.transaction do
|
|
||||||
# Create the note
|
|
||||||
@note = Note.create(:lat => lat, :lon => lon)
|
|
||||||
raise OSM::APIBadUserInput, "The note is outside this world" unless @note.in_world?
|
|
||||||
|
|
||||||
# Save the note
|
|
||||||
@note.save!
|
|
||||||
|
|
||||||
# Add a comment to the note
|
|
||||||
add_comment(@note, comment, "opened")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return a copy of the new note
|
|
||||||
respond_to do |format|
|
|
||||||
format.xml { render :action => :show }
|
|
||||||
format.json { render :action => :show }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Add a comment to an existing note
|
|
||||||
def comment
|
|
||||||
# Check the ACLs
|
|
||||||
raise OSM::APIAccessDenied if current_user.nil? && Acl.no_note_comment(request.remote_ip)
|
|
||||||
|
|
||||||
# Check the arguments are sane
|
|
||||||
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
|
||||||
raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
|
|
||||||
|
|
||||||
# Extract the arguments
|
|
||||||
id = params[:id].to_i
|
|
||||||
comment = params[:text]
|
|
||||||
|
|
||||||
# Find the note and check it is valid
|
|
||||||
@note = Note.find(id)
|
|
||||||
raise OSM::APINotFoundError unless @note
|
|
||||||
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
|
|
||||||
raise OSM::APINoteAlreadyClosedError, @note if @note.closed?
|
|
||||||
|
|
||||||
# Add a comment to the note
|
|
||||||
Note.transaction do
|
|
||||||
add_comment(@note, comment, "commented")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return a copy of the updated note
|
|
||||||
respond_to do |format|
|
|
||||||
format.xml { render :action => :show }
|
|
||||||
format.json { render :action => :show }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Close a note
|
|
||||||
def close
|
|
||||||
# Check the arguments are sane
|
|
||||||
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
|
||||||
|
|
||||||
# Extract the arguments
|
|
||||||
id = params[:id].to_i
|
|
||||||
comment = params[:text]
|
|
||||||
|
|
||||||
# Find the note and check it is valid
|
|
||||||
@note = Note.find_by(:id => id)
|
|
||||||
raise OSM::APINotFoundError unless @note
|
|
||||||
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
|
|
||||||
raise OSM::APINoteAlreadyClosedError, @note if @note.closed?
|
|
||||||
|
|
||||||
# Close the note and add a comment
|
|
||||||
Note.transaction do
|
|
||||||
@note.close
|
|
||||||
|
|
||||||
add_comment(@note, comment, "closed")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return a copy of the updated note
|
|
||||||
respond_to do |format|
|
|
||||||
format.xml { render :action => :show }
|
|
||||||
format.json { render :action => :show }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Reopen a note
|
|
||||||
def reopen
|
|
||||||
# Check the arguments are sane
|
|
||||||
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
|
||||||
|
|
||||||
# Extract the arguments
|
|
||||||
id = params[:id].to_i
|
|
||||||
comment = params[:text]
|
|
||||||
|
|
||||||
# Find the note and check it is valid
|
|
||||||
@note = Note.find_by(:id => id)
|
|
||||||
raise OSM::APINotFoundError unless @note
|
|
||||||
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? || current_user.moderator?
|
|
||||||
raise OSM::APINoteAlreadyOpenError, @note unless @note.closed? || !@note.visible?
|
|
||||||
|
|
||||||
# Reopen the note and add a comment
|
|
||||||
Note.transaction do
|
|
||||||
@note.reopen
|
|
||||||
|
|
||||||
add_comment(@note, comment, "reopened")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return a copy of the updated note
|
|
||||||
respond_to do |format|
|
|
||||||
format.xml { render :action => :show }
|
|
||||||
format.json { render :action => :show }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Get a feed of recent notes and comments
|
|
||||||
def feed
|
|
||||||
# Get any conditions that need to be applied
|
|
||||||
notes = closed_condition(Note.all)
|
|
||||||
|
|
||||||
# Process any bbox
|
|
||||||
if params[:bbox]
|
|
||||||
bbox = BoundingBox.from_bbox_params(params)
|
|
||||||
|
|
||||||
bbox.check_boundaries
|
|
||||||
bbox.check_size(MAX_NOTE_REQUEST_AREA)
|
|
||||||
|
|
||||||
notes = notes.bbox(bbox)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Find the comments we want to return
|
|
||||||
@comments = NoteComment.where(:note_id => notes).order("created_at DESC").limit(result_limit).preload(:note)
|
|
||||||
|
|
||||||
# Render the result
|
|
||||||
respond_to do |format|
|
|
||||||
format.rss
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Read a note
|
|
||||||
def show
|
|
||||||
# Check the arguments are sane
|
|
||||||
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
|
||||||
|
|
||||||
# Find the note and check it is valid
|
|
||||||
@note = Note.find(params[:id])
|
|
||||||
raise OSM::APINotFoundError unless @note
|
|
||||||
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible? || current_user&.moderator?
|
|
||||||
|
|
||||||
# Render the result
|
|
||||||
respond_to do |format|
|
|
||||||
format.xml
|
|
||||||
format.rss
|
|
||||||
format.json
|
|
||||||
format.gpx
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Delete (hide) a note
|
|
||||||
def destroy
|
|
||||||
# Check the arguments are sane
|
|
||||||
raise OSM::APIBadUserInput, "No id was given" unless params[:id]
|
|
||||||
|
|
||||||
# Extract the arguments
|
|
||||||
id = params[:id].to_i
|
|
||||||
comment = params[:text]
|
|
||||||
|
|
||||||
# Find the note and check it is valid
|
|
||||||
@note = Note.find(id)
|
|
||||||
raise OSM::APINotFoundError unless @note
|
|
||||||
raise OSM::APIAlreadyDeletedError.new("note", @note.id) unless @note.visible?
|
|
||||||
|
|
||||||
# Mark the note as hidden
|
|
||||||
Note.transaction do
|
|
||||||
@note.status = "hidden"
|
|
||||||
@note.save
|
|
||||||
|
|
||||||
add_comment(@note, comment, "hidden", false)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return a copy of the updated note
|
|
||||||
respond_to do |format|
|
|
||||||
format.xml { render :action => :show }
|
|
||||||
format.json { render :action => :show }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Return a list of notes matching a given string
|
|
||||||
def search
|
|
||||||
# Get the initial set of notes
|
|
||||||
@notes = closed_condition(Note.all)
|
|
||||||
|
|
||||||
# Add any user filter
|
|
||||||
if params[:display_name] || params[:user]
|
|
||||||
if params[:display_name]
|
|
||||||
@user = User.find_by(:display_name => params[:display_name])
|
|
||||||
|
|
||||||
raise OSM::APIBadUserInput, "User #{params[:display_name]} not known" unless @user
|
|
||||||
else
|
|
||||||
@user = User.find_by(:id => params[:user])
|
|
||||||
|
|
||||||
raise OSM::APIBadUserInput, "User #{params[:user]} not known" unless @user
|
|
||||||
end
|
|
||||||
|
|
||||||
@notes = @notes.joins(:comments).where(:note_comments => { :author_id => @user })
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add any text filter
|
|
||||||
@notes = @notes.joins(:comments).where("to_tsvector('english', note_comments.body) @@ plainto_tsquery('english', ?)", params[:q]) if params[:q]
|
|
||||||
|
|
||||||
# Add any date filter
|
|
||||||
if params[:from]
|
|
||||||
begin
|
|
||||||
from = Time.parse(params[:from])
|
|
||||||
rescue ArgumentError
|
|
||||||
raise OSM::APIBadUserInput, "Date #{params[:from]} is in a wrong format"
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
to = if params[:to]
|
|
||||||
Time.parse(params[:to])
|
|
||||||
else
|
|
||||||
Time.now
|
|
||||||
end
|
|
||||||
rescue ArgumentError
|
|
||||||
raise OSM::APIBadUserInput, "Date #{params[:to]} is in a wrong format"
|
|
||||||
end
|
|
||||||
|
|
||||||
@notes = @notes.where(:created_at => from..to)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Find the notes we want to return
|
|
||||||
@notes = @notes.order("updated_at DESC").limit(result_limit).preload(:comments)
|
|
||||||
|
|
||||||
# Render the result
|
|
||||||
respond_to do |format|
|
|
||||||
format.rss { render :action => :index }
|
|
||||||
format.xml { render :action => :index }
|
|
||||||
format.json { render :action => :index }
|
|
||||||
format.gpx { render :action => :index }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Display a list of notes by a specified user
|
# Display a list of notes by a specified user
|
||||||
def mine
|
def mine
|
||||||
|
@ -333,63 +31,4 @@ class NotesController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
# utility functions below.
|
|
||||||
#------------------------------------------------------------
|
|
||||||
|
|
||||||
##
|
|
||||||
# Get the maximum number of results to return
|
|
||||||
def result_limit
|
|
||||||
if params[:limit]
|
|
||||||
if params[:limit].to_i.positive? && params[:limit].to_i <= 10000
|
|
||||||
params[:limit].to_i
|
|
||||||
else
|
|
||||||
raise OSM::APIBadUserInput, "Note limit must be between 1 and 10000"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
100
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Generate a condition to choose which notes we want based
|
|
||||||
# on their status and the user's request parameters
|
|
||||||
def closed_condition(notes)
|
|
||||||
closed_since = if params[:closed]
|
|
||||||
params[:closed].to_i
|
|
||||||
else
|
|
||||||
7
|
|
||||||
end
|
|
||||||
|
|
||||||
if closed_since.negative?
|
|
||||||
notes.where.not(:status => "hidden")
|
|
||||||
elsif closed_since.positive?
|
|
||||||
notes.where(:status => "open")
|
|
||||||
.or(notes.where(:status => "closed")
|
|
||||||
.where(notes.arel_table[:closed_at].gt(Time.now - closed_since.days)))
|
|
||||||
else
|
|
||||||
notes.where(:status => "open")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Add a comment to a note
|
|
||||||
def add_comment(note, text, event, notify = true)
|
|
||||||
attributes = { :visible => true, :event => event, :body => text }
|
|
||||||
|
|
||||||
if current_user
|
|
||||||
attributes[:author_id] = current_user.id
|
|
||||||
else
|
|
||||||
attributes[:author_ip] = request.remote_ip
|
|
||||||
end
|
|
||||||
|
|
||||||
comment = note.comments.create!(attributes)
|
|
||||||
|
|
||||||
note.comments.map(&:author).uniq.each do |user|
|
|
||||||
Notifier.note_comment_notification(comment, user).deliver_later if notify && user && user != current_user && user.visible?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
# this class pulls together the logic for all the old_* controllers
|
|
||||||
# into one place. as it turns out, the API methods for historical
|
|
||||||
# nodes, ways and relations are basically identical.
|
|
||||||
class OldController < ApplicationController
|
|
||||||
require "xml/libxml"
|
|
||||||
|
|
||||||
skip_before_action :verify_authenticity_token
|
|
||||||
before_action :setup_user_auth, :only => [:history, :version]
|
|
||||||
before_action :api_deny_access_handler
|
|
||||||
before_action :authorize, :only => [:redact]
|
|
||||||
|
|
||||||
authorize_resource
|
|
||||||
|
|
||||||
before_action :check_api_readable
|
|
||||||
before_action :check_api_writable, :only => [:redact]
|
|
||||||
around_action :api_call_handle_error, :api_call_timeout
|
|
||||||
before_action :lookup_old_element, :except => [:history]
|
|
||||||
before_action :lookup_old_element_versions, :only => [:history]
|
|
||||||
|
|
||||||
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
|
|
||||||
# to do that ourselves.
|
|
||||||
raise OSM::APINotFoundError if @elements.empty?
|
|
||||||
|
|
||||||
doc = OSM::API.new.get_xml_doc
|
|
||||||
|
|
||||||
visible_elements = if show_redactions?
|
|
||||||
@elements
|
|
||||||
else
|
|
||||||
@elements.unredacted
|
|
||||||
end
|
|
||||||
|
|
||||||
visible_elements.each do |element|
|
|
||||||
doc.root << element.to_xml_node
|
|
||||||
end
|
|
||||||
|
|
||||||
render :xml => doc.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def version
|
|
||||||
if @old_element.redacted? && !show_redactions?
|
|
||||||
head :forbidden
|
|
||||||
|
|
||||||
else
|
|
||||||
response.last_modified = @old_element.timestamp
|
|
||||||
|
|
||||||
doc = OSM::API.new.get_xml_doc
|
|
||||||
doc.root << @old_element.to_xml_node
|
|
||||||
|
|
||||||
render :xml => doc.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def redact
|
|
||||||
redaction_id = params["redaction"]
|
|
||||||
if redaction_id.nil?
|
|
||||||
# if no redaction ID was provided, then this is an unredact
|
|
||||||
# operation.
|
|
||||||
@old_element.redact!(nil)
|
|
||||||
else
|
|
||||||
# if a redaction ID was specified, then set this element to
|
|
||||||
# be redacted in that redaction.
|
|
||||||
redaction = Redaction.find(redaction_id.to_i)
|
|
||||||
@old_element.redact!(redaction)
|
|
||||||
end
|
|
||||||
|
|
||||||
# just return an empty 200 OK for success
|
|
||||||
head :ok
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def show_redactions?
|
|
||||||
current_user&.moderator? && params[:show_redactions] == "true"
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,11 +0,0 @@
|
||||||
class OldNodesController < OldController
|
|
||||||
private
|
|
||||||
|
|
||||||
def lookup_old_element
|
|
||||||
@old_element = OldNode.find([params[:id], params[:version]])
|
|
||||||
end
|
|
||||||
|
|
||||||
def lookup_old_element_versions
|
|
||||||
@elements = OldNode.where(:node_id => params[:id]).order(:version)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,11 +0,0 @@
|
||||||
class OldRelationsController < OldController
|
|
||||||
private
|
|
||||||
|
|
||||||
def lookup_old_element
|
|
||||||
@old_element = OldRelation.find([params[:id], params[:version]])
|
|
||||||
end
|
|
||||||
|
|
||||||
def lookup_old_element_versions
|
|
||||||
@elements = OldRelation.where(:relation_id => params[:id]).order(:version)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,11 +0,0 @@
|
||||||
class OldWaysController < OldController
|
|
||||||
private
|
|
||||||
|
|
||||||
def lookup_old_element
|
|
||||||
@old_element = OldWay.find([params[:id], params[:version]])
|
|
||||||
end
|
|
||||||
|
|
||||||
def lookup_old_element_versions
|
|
||||||
@elements = OldWay.where(:way_id => params[:id]).order(:version)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,167 +0,0 @@
|
||||||
class RelationsController < ApplicationController
|
|
||||||
require "xml/libxml"
|
|
||||||
|
|
||||||
skip_before_action :verify_authenticity_token
|
|
||||||
before_action :authorize, :only => [:create, :update, :delete]
|
|
||||||
before_action :api_deny_access_handler
|
|
||||||
|
|
||||||
authorize_resource
|
|
||||||
|
|
||||||
before_action :require_public_data, :only => [:create, :update, :delete]
|
|
||||||
before_action :check_api_writable, :only => [:create, :update, :delete]
|
|
||||||
before_action :check_api_readable, :except => [:create, :update, :delete]
|
|
||||||
around_action :api_call_handle_error, :api_call_timeout
|
|
||||||
|
|
||||||
def create
|
|
||||||
assert_method :put
|
|
||||||
|
|
||||||
relation = Relation.from_xml(request.raw_post, true)
|
|
||||||
|
|
||||||
# Assume that Relation.from_xml has thrown an exception if there is an error parsing the xml
|
|
||||||
relation.create_with_history current_user
|
|
||||||
render :plain => relation.id.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
|
||||||
relation = Relation.find(params[:id])
|
|
||||||
response.last_modified = relation.timestamp
|
|
||||||
if relation.visible
|
|
||||||
render :xml => relation.to_xml.to_s
|
|
||||||
else
|
|
||||||
head :gone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
logger.debug request.raw_post
|
|
||||||
|
|
||||||
relation = Relation.find(params[:id])
|
|
||||||
new_relation = Relation.from_xml(request.raw_post)
|
|
||||||
|
|
||||||
raise OSM::APIBadUserInput, "The id in the url (#{relation.id}) is not the same as provided in the xml (#{new_relation.id})" unless new_relation && new_relation.id == relation.id
|
|
||||||
|
|
||||||
relation.update_from new_relation, current_user
|
|
||||||
render :plain => relation.version.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete
|
|
||||||
relation = Relation.find(params[:id])
|
|
||||||
new_relation = Relation.from_xml(request.raw_post)
|
|
||||||
if new_relation && new_relation.id == relation.id
|
|
||||||
relation.delete_with_history!(new_relation, current_user)
|
|
||||||
render :plain => relation.version.to_s
|
|
||||||
else
|
|
||||||
head :bad_request
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------
|
|
||||||
# full
|
|
||||||
#
|
|
||||||
# input parameters: id
|
|
||||||
#
|
|
||||||
# returns XML representation of one relation object plus all its
|
|
||||||
# members, plus all nodes part of member ways
|
|
||||||
# -----------------------------------------------------------------
|
|
||||||
def full
|
|
||||||
relation = Relation.find(params[:id])
|
|
||||||
|
|
||||||
if relation.visible
|
|
||||||
|
|
||||||
# first find the ids of nodes, ways and relations referenced by this
|
|
||||||
# relation - note that we exclude this relation just in case.
|
|
||||||
|
|
||||||
node_ids = relation.members.select { |m| m[0] == "Node" }.map { |m| m[1] }
|
|
||||||
way_ids = relation.members.select { |m| m[0] == "Way" }.map { |m| m[1] }
|
|
||||||
relation_ids = relation.members.select { |m| m[0] == "Relation" && m[1] != relation.id }.map { |m| m[1] }
|
|
||||||
|
|
||||||
# next load the relations and the ways.
|
|
||||||
|
|
||||||
relations = Relation.where(:id => relation_ids).includes(:relation_tags)
|
|
||||||
ways = Way.where(:id => way_ids).includes(:way_nodes, :way_tags)
|
|
||||||
|
|
||||||
# now additionally collect nodes referenced by ways. Note how we
|
|
||||||
# recursively evaluate ways but NOT relations.
|
|
||||||
|
|
||||||
way_node_ids = ways.collect do |way|
|
|
||||||
way.way_nodes.collect(&:node_id)
|
|
||||||
end
|
|
||||||
node_ids += way_node_ids.flatten
|
|
||||||
nodes = Node.where(:id => node_ids.uniq).includes(:node_tags)
|
|
||||||
|
|
||||||
# create XML.
|
|
||||||
doc = OSM::API.new.get_xml_doc
|
|
||||||
visible_nodes = {}
|
|
||||||
changeset_cache = {}
|
|
||||||
user_display_name_cache = {}
|
|
||||||
|
|
||||||
nodes.each do |node|
|
|
||||||
next unless node.visible? # should be unnecessary if data is consistent.
|
|
||||||
|
|
||||||
doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
|
|
||||||
visible_nodes[node.id] = node
|
|
||||||
end
|
|
||||||
|
|
||||||
ways.each do |way|
|
|
||||||
next unless way.visible? # should be unnecessary if data is consistent.
|
|
||||||
|
|
||||||
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
|
|
||||||
end
|
|
||||||
|
|
||||||
relations.each do |rel|
|
|
||||||
next unless rel.visible? # should be unnecessary if data is consistent.
|
|
||||||
|
|
||||||
doc.root << rel.to_xml_node(changeset_cache, user_display_name_cache)
|
|
||||||
end
|
|
||||||
|
|
||||||
# finally add self and output
|
|
||||||
doc.root << relation.to_xml_node(changeset_cache, user_display_name_cache)
|
|
||||||
render :xml => doc.to_s
|
|
||||||
|
|
||||||
else
|
|
||||||
head :gone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def index
|
|
||||||
raise OSM::APIBadUserInput, "The parameter relations is required, and must be of the form relations=id[,id[,id...]]" unless params["relations"]
|
|
||||||
|
|
||||||
ids = params["relations"].split(",").collect(&:to_i)
|
|
||||||
|
|
||||||
raise OSM::APIBadUserInput, "No relations were given to search for" if ids.empty?
|
|
||||||
|
|
||||||
doc = OSM::API.new.get_xml_doc
|
|
||||||
|
|
||||||
Relation.find(ids).each do |relation|
|
|
||||||
doc.root << relation.to_xml_node
|
|
||||||
end
|
|
||||||
|
|
||||||
render :xml => doc.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def relations_for_way
|
|
||||||
relations_for_object("Way")
|
|
||||||
end
|
|
||||||
|
|
||||||
def relations_for_node
|
|
||||||
relations_for_object("Node")
|
|
||||||
end
|
|
||||||
|
|
||||||
def relations_for_relation
|
|
||||||
relations_for_object("Relation")
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def relations_for_object(objtype)
|
|
||||||
relationids = RelationMember.where(:member_type => objtype, :member_id => params[:id]).collect(&:relation_id).uniq
|
|
||||||
|
|
||||||
doc = OSM::API.new.get_xml_doc
|
|
||||||
|
|
||||||
Relation.find(relationids).each do |relation|
|
|
||||||
doc.root << relation.to_xml_node if relation.visible
|
|
||||||
end
|
|
||||||
|
|
||||||
render :xml => doc.to_s
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,100 +0,0 @@
|
||||||
class SearchController < ApplicationController
|
|
||||||
# Support searching for nodes, ways, or all
|
|
||||||
# Can search by tag k, v, or both (type->k,value->v)
|
|
||||||
# Can search by name (k=name,v=....)
|
|
||||||
skip_before_action :verify_authenticity_token
|
|
||||||
authorize_resource :class => false
|
|
||||||
|
|
||||||
def search_all
|
|
||||||
do_search(true, true, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def search_ways
|
|
||||||
do_search(true, false, false)
|
|
||||||
end
|
|
||||||
|
|
||||||
def search_nodes
|
|
||||||
do_search(false, true, false)
|
|
||||||
end
|
|
||||||
|
|
||||||
def search_relations
|
|
||||||
do_search(false, false, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def do_search(do_ways, do_nodes, do_relations)
|
|
||||||
type = params["type"]
|
|
||||||
value = params["value"]
|
|
||||||
unless type || value
|
|
||||||
name = params["name"]
|
|
||||||
if name
|
|
||||||
type = "name"
|
|
||||||
value = name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if do_nodes
|
|
||||||
response.headers["Error"] = "Searching of nodes is currently unavailable"
|
|
||||||
head :service_unavailable
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
unless value
|
|
||||||
response.headers["Error"] = "Searching for a key without value is currently unavailable"
|
|
||||||
head :service_unavailable
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
# Matching for node tags table
|
|
||||||
if do_nodes
|
|
||||||
nodes = Node.joins(:node_tags)
|
|
||||||
nodes = nodes.where(:current_node_tags => { :k => type }) if type
|
|
||||||
nodes = nodes.where(:current_node_tags => { :v => value }) if value
|
|
||||||
nodes = nodes.limit(100)
|
|
||||||
else
|
|
||||||
nodes = []
|
|
||||||
end
|
|
||||||
|
|
||||||
# Matching for way tags table
|
|
||||||
if do_ways
|
|
||||||
ways = Way.joins(:way_tags)
|
|
||||||
ways = ways.where(:current_way_tags => { :k => type }) if type
|
|
||||||
ways = ways.where(:current_way_tags => { :v => value }) if value
|
|
||||||
ways = ways.limit(100)
|
|
||||||
else
|
|
||||||
ways = []
|
|
||||||
end
|
|
||||||
|
|
||||||
# Matching for relation tags table
|
|
||||||
if do_relations
|
|
||||||
relations = Relation.joins(:relation_tags)
|
|
||||||
relations = relations.where(:current_relation_tags => { :k => type }) if type
|
|
||||||
relations = relations.where(:current_relation_tags => { :v => value }) if value
|
|
||||||
relations = relations.limit(2000)
|
|
||||||
else
|
|
||||||
relations = []
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fetch any node needed for our ways (only have matching nodes so far)
|
|
||||||
nodes += Node.find(ways.collect(&:nds).uniq)
|
|
||||||
|
|
||||||
# Print
|
|
||||||
visible_nodes = {}
|
|
||||||
changeset_cache = {}
|
|
||||||
user_display_name_cache = {}
|
|
||||||
doc = OSM::API.new.get_xml_doc
|
|
||||||
nodes.each do |node|
|
|
||||||
doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
|
|
||||||
visible_nodes[node.id] = node
|
|
||||||
end
|
|
||||||
|
|
||||||
ways.each do |way|
|
|
||||||
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
|
|
||||||
end
|
|
||||||
|
|
||||||
relations.each do |rel|
|
|
||||||
doc.root << rel.to_xml_node(changeset_cache, user_display_name_cache)
|
|
||||||
end
|
|
||||||
|
|
||||||
render :xml => doc.to_s
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,217 +0,0 @@
|
||||||
class SwfController < ApplicationController
|
|
||||||
skip_before_action :verify_authenticity_token
|
|
||||||
before_action :check_api_readable
|
|
||||||
authorize_resource :class => false
|
|
||||||
|
|
||||||
# to log:
|
|
||||||
# RAILS_DEFAULT_LOGGER.error("Args: #{args[0]}, #{args[1]}, #{args[2]}, #{args[3]}")
|
|
||||||
# $log.puts Time.new.to_s+','+Time.new.usec.to_s+": started GPS script"
|
|
||||||
# http://localhost:3000/api/0.4/swf/trackpoints?xmin=-2.32402605810577&xmax=-2.18386309423859&ymin=52.1546608755772&ymax=52.2272777906895&baselong=-2.25325793066437&basey=61.3948537948532&masterscale=5825.4222222222
|
|
||||||
|
|
||||||
# ====================================================================
|
|
||||||
# Public methods
|
|
||||||
|
|
||||||
# ---- trackpoints compile SWF of trackpoints
|
|
||||||
|
|
||||||
def trackpoints
|
|
||||||
# - Initialise
|
|
||||||
|
|
||||||
baselong = params["baselong"].to_f
|
|
||||||
basey = params["basey"].to_f
|
|
||||||
masterscale = params["masterscale"].to_f
|
|
||||||
|
|
||||||
bbox = BoundingBox.new(params["xmin"], params["ymin"],
|
|
||||||
params["xmax"], params["ymax"])
|
|
||||||
start = params["start"].to_i
|
|
||||||
|
|
||||||
# - Begin movie
|
|
||||||
|
|
||||||
bounds_left = 0
|
|
||||||
bounds_right = 320 * 20
|
|
||||||
bounds_bottom = 0
|
|
||||||
bounds_top = 240 * 20
|
|
||||||
|
|
||||||
m = ""
|
|
||||||
m += swf_record(9, 255.chr + 155.chr + 155.chr) # Background
|
|
||||||
absx = 0
|
|
||||||
absy = 0
|
|
||||||
xl = yb = 9999999
|
|
||||||
xr = yt = -9999999
|
|
||||||
|
|
||||||
# - Send SQL for GPS tracks
|
|
||||||
|
|
||||||
b = ""
|
|
||||||
lasttime = 0
|
|
||||||
lasttrack = lastfile = "-1"
|
|
||||||
|
|
||||||
if params["token"]
|
|
||||||
user = User.authenticate(:token => params[:token])
|
|
||||||
sql = "SELECT gps_points.latitude*0.0000001 AS lat,gps_points.longitude*0.0000001 AS lon,gpx_files.id AS fileid," + " EXTRACT(EPOCH FROM gps_points.timestamp) AS ts, gps_points.trackid AS trackid " + " FROM gpx_files,gps_points " + "WHERE gpx_files.id=gpx_id " + " AND gpx_files.user_id=#{user.id} " + " AND " + OSM.sql_for_area(bbox, "gps_points.") + " AND (gps_points.timestamp IS NOT NULL) " + "ORDER BY fileid DESC,ts " + "LIMIT 10000 OFFSET #{start}"
|
|
||||||
else
|
|
||||||
sql = "SELECT latitude*0.0000001 AS lat,longitude*0.0000001 AS lon,gpx_id AS fileid," + " EXTRACT(EPOCH FROM timestamp) AS ts, gps_points.trackid AS trackid " + " FROM gps_points " + "WHERE " + OSM.sql_for_area(bbox, "gps_points.") + " AND (gps_points.timestamp IS NOT NULL) " + "ORDER BY fileid DESC,ts " + "LIMIT 10000 OFFSET #{start}"
|
|
||||||
end
|
|
||||||
gpslist = ActiveRecord::Base.connection.select_all sql
|
|
||||||
|
|
||||||
# - Draw GPS trace lines
|
|
||||||
|
|
||||||
r = start_shape
|
|
||||||
gpslist.each do |row|
|
|
||||||
xs = (long2coord(row["lon"].to_f, baselong, masterscale) * 20).floor
|
|
||||||
ys = (lat2coord(row["lat"].to_f, basey, masterscale) * 20).floor
|
|
||||||
xl = [xs, xl].min
|
|
||||||
xr = [xs, xr].max
|
|
||||||
yb = [ys, yb].min
|
|
||||||
yt = [ys, yt].max
|
|
||||||
if row["ts"].to_i - lasttime > 180 || row["fileid"] != lastfile || row["trackid"] != lasttrack # or row['ts'].to_i==lasttime
|
|
||||||
b += start_and_move(xs, ys, "01")
|
|
||||||
absx = xs.floor
|
|
||||||
absy = ys.floor
|
|
||||||
end
|
|
||||||
b += draw_to(absx, absy, xs, ys)
|
|
||||||
absx = xs.floor
|
|
||||||
absy = ys.floor
|
|
||||||
lasttime = row["ts"].to_i
|
|
||||||
lastfile = row["fileid"]
|
|
||||||
lasttrack = row["trackid"]
|
|
||||||
r += [b.slice!(0...80)].pack("B*") while b.length > 80
|
|
||||||
end
|
|
||||||
|
|
||||||
# (Unwayed segments removed)
|
|
||||||
|
|
||||||
# - Write shape
|
|
||||||
|
|
||||||
b += end_shape
|
|
||||||
r += [b].pack("B*")
|
|
||||||
m += swf_record(2, pack_u16(1) + pack_rect(xl, xr, yb, yt) + r)
|
|
||||||
m += swf_record(4, pack_u16(1) + pack_u16(1))
|
|
||||||
|
|
||||||
# - Create Flash header and write to browser
|
|
||||||
|
|
||||||
m += swf_record(1, "") # Show frame
|
|
||||||
m += swf_record(0, "") # End
|
|
||||||
|
|
||||||
m = pack_rect(bounds_left, bounds_right, bounds_bottom, bounds_top) + 0.chr + 12.chr + pack_u16(1) + m
|
|
||||||
m = "FWS" + 6.chr + pack_u32(m.length + 8) + m
|
|
||||||
|
|
||||||
render :body => m, :content_type => "application/x-shockwave-flash"
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# =======================================================================
|
|
||||||
# SWF functions
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
|
||||||
# Line-drawing
|
|
||||||
|
|
||||||
def start_shape
|
|
||||||
s = 0.chr # No fill styles
|
|
||||||
s += 2.chr # Two line styles
|
|
||||||
s += pack_u16(0) + 0.chr + 255.chr + 255.chr # Width 5, RGB #00FFFF
|
|
||||||
s += pack_u16(0) + 255.chr + 0.chr + 255.chr # Width 5, RGB #FF00FF
|
|
||||||
s += 34.chr # 2 fill, 2 line index bits
|
|
||||||
s
|
|
||||||
end
|
|
||||||
|
|
||||||
def end_shape
|
|
||||||
"000000"
|
|
||||||
end
|
|
||||||
|
|
||||||
def start_and_move(x, y, col)
|
|
||||||
d = "001001" # Line style change, moveTo
|
|
||||||
l = [length_sb(x), length_sb(y)].max
|
|
||||||
d += format("%05b%0*b%0*b", l, l, x, l, y)
|
|
||||||
d += col # Select line style
|
|
||||||
d
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw_to(absx, absy, x, y)
|
|
||||||
dx = x - absx
|
|
||||||
dy = y - absy
|
|
||||||
|
|
||||||
# Split the line up if there's anything>16383, because
|
|
||||||
# that would overflow the 4 bits allowed for length
|
|
||||||
mstep = [dx.abs / 16383, dy.abs / 16383, 1].max.ceil
|
|
||||||
xstep = dx / mstep
|
|
||||||
ystep = dy / mstep
|
|
||||||
d = ""
|
|
||||||
1.upto(mstep).each do
|
|
||||||
d += draw_section(x, y, x + xstep, y + ystep)
|
|
||||||
x += xstep
|
|
||||||
y += ystep
|
|
||||||
end
|
|
||||||
d
|
|
||||||
end
|
|
||||||
|
|
||||||
def draw_section(x1, y1, x2, y2)
|
|
||||||
d = "11" # TypeFlag, EdgeFlag
|
|
||||||
dx = x2 - x1
|
|
||||||
dy = y2 - y1
|
|
||||||
l = [length_sb(dx), length_sb(dy)].max
|
|
||||||
d += format("%04b", l - 2)
|
|
||||||
d += "1" # GeneralLine
|
|
||||||
d += format("%0*b%0*b", l, dx, l, dy)
|
|
||||||
d
|
|
||||||
end
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
|
||||||
# Specific data types
|
|
||||||
|
|
||||||
# SWF data block type
|
|
||||||
|
|
||||||
def swf_record(id, r)
|
|
||||||
if r.length > 62
|
|
||||||
# Long header: tag id, 0x3F, length
|
|
||||||
pack_u16((id << 6) + 0x3F) + pack_u32(r.length) + r
|
|
||||||
else
|
|
||||||
# Short header: tag id, length
|
|
||||||
pack_u16((id << 6) + r.length) + r
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# SWF RECT type
|
|
||||||
|
|
||||||
def pack_rect(a, b, c, d)
|
|
||||||
l = [length_sb(a),
|
|
||||||
length_sb(b),
|
|
||||||
length_sb(c),
|
|
||||||
length_sb(d)].max
|
|
||||||
# create binary string (00111001 etc.) - 5-byte length, then bbox
|
|
||||||
n = format("%05b%0*b%0*b%0*b%0*b", l, l, a, l, b, l, c, l, d)
|
|
||||||
# pack into byte string
|
|
||||||
[n].pack("B*")
|
|
||||||
end
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
|
||||||
# Generic pack functions
|
|
||||||
|
|
||||||
def pack_u16(n)
|
|
||||||
[n.floor].pack("v")
|
|
||||||
end
|
|
||||||
|
|
||||||
def pack_u32(n)
|
|
||||||
[n.floor].pack("V")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Find number of bits required to store arbitrary-length binary
|
|
||||||
|
|
||||||
def length_sb(n)
|
|
||||||
Math.frexp(n + (n.zero? ? 1 : 0))[1] + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# ====================================================================
|
|
||||||
# Co-ordinate conversion
|
|
||||||
# (this is duplicated from amf_controller, should probably share)
|
|
||||||
|
|
||||||
def lat2coord(a, basey, masterscale)
|
|
||||||
-(lat2y(a) - basey) * masterscale
|
|
||||||
end
|
|
||||||
|
|
||||||
def long2coord(a, baselong, masterscale)
|
|
||||||
(a - baselong) * masterscale
|
|
||||||
end
|
|
||||||
|
|
||||||
def lat2y(a)
|
|
||||||
180 / Math::PI * Math.log(Math.tan(Math::PI / 4 + a * (Math::PI / 180) / 2))
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,21 +1,15 @@
|
||||||
class TracesController < ApplicationController
|
class TracesController < ApplicationController
|
||||||
layout "site", :except => :georss
|
layout "site", :except => :georss
|
||||||
|
|
||||||
skip_before_action :verify_authenticity_token, :only => [:api_create, :api_read, :api_update, :api_delete, :api_data]
|
|
||||||
before_action :authorize_web
|
before_action :authorize_web
|
||||||
before_action :set_locale
|
before_action :set_locale
|
||||||
before_action :authorize, :only => [:api_create, :api_read, :api_update, :api_delete, :api_data]
|
|
||||||
before_action :api_deny_access_handler, :only => [:api_create, :api_read, :api_update, :api_delete, :api_data]
|
|
||||||
|
|
||||||
authorize_resource
|
authorize_resource
|
||||||
|
|
||||||
before_action :check_database_readable, :except => [:api_read, :api_data]
|
before_action :check_database_readable
|
||||||
before_action :check_database_writable, :only => [:new, :create, :edit, :delete, :api_create, :api_update, :api_delete]
|
before_action :check_database_writable, :only => [:new, :create, :edit, :delete]
|
||||||
before_action :check_api_readable, :only => [:api_read, :api_data]
|
|
||||||
before_action :check_api_writable, :only => [:api_create, :api_update, :api_delete]
|
|
||||||
before_action :offline_warning, :only => [:mine, :show]
|
before_action :offline_warning, :only => [:mine, :show]
|
||||||
before_action :offline_redirect, :only => [:new, :create, :edit, :delete, :data, :api_create, :api_delete, :api_data]
|
before_action :offline_redirect, :only => [:new, :create, :edit, :delete, :data]
|
||||||
around_action :api_call_handle_error, :only => [:api_create, :api_read, :api_update, :api_delete, :api_data]
|
|
||||||
|
|
||||||
# Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.).
|
# Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.).
|
||||||
# target_user - if set, specifies the user to fetch traces for. if not set will fetch all traces
|
# target_user - if set, specifies the user to fetch traces for. if not set will fetch all traces
|
||||||
|
@ -263,86 +257,6 @@ class TracesController < ApplicationController
|
||||||
head :not_found
|
head :not_found
|
||||||
end
|
end
|
||||||
|
|
||||||
def api_read
|
|
||||||
trace = Trace.visible.find(params[:id])
|
|
||||||
|
|
||||||
if trace.public? || trace.user == current_user
|
|
||||||
render :xml => trace.to_xml.to_s
|
|
||||||
else
|
|
||||||
head :forbidden
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def api_update
|
|
||||||
trace = Trace.visible.find(params[:id])
|
|
||||||
|
|
||||||
if trace.user == current_user
|
|
||||||
trace.update_from_xml(request.raw_post)
|
|
||||||
trace.save!
|
|
||||||
|
|
||||||
head :ok
|
|
||||||
else
|
|
||||||
head :forbidden
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def api_delete
|
|
||||||
trace = Trace.visible.find(params[:id])
|
|
||||||
|
|
||||||
if trace.user == current_user
|
|
||||||
trace.visible = false
|
|
||||||
trace.save!
|
|
||||||
|
|
||||||
head :ok
|
|
||||||
else
|
|
||||||
head :forbidden
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def api_data
|
|
||||||
trace = Trace.visible.find(params[:id])
|
|
||||||
|
|
||||||
if trace.public? || trace.user == current_user
|
|
||||||
if request.format == Mime[:xml]
|
|
||||||
send_data(trace.xml_file.read, :filename => "#{trace.id}.xml", :type => request.format.to_s, :disposition => "attachment")
|
|
||||||
elsif request.format == Mime[:gpx]
|
|
||||||
send_data(trace.xml_file.read, :filename => "#{trace.id}.gpx", :type => request.format.to_s, :disposition => "attachment")
|
|
||||||
else
|
|
||||||
send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => "attachment")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
head :forbidden
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def api_create
|
|
||||||
tags = params[:tags] || ""
|
|
||||||
description = params[:description] || ""
|
|
||||||
visibility = params[:visibility]
|
|
||||||
|
|
||||||
if visibility.nil?
|
|
||||||
visibility = if params[:public]&.to_i&.nonzero?
|
|
||||||
"public"
|
|
||||||
else
|
|
||||||
"private"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if params[:file].respond_to?(:read)
|
|
||||||
trace = do_create(params[:file], tags, description, visibility)
|
|
||||||
|
|
||||||
if trace.id
|
|
||||||
render :plain => trace.id.to_s
|
|
||||||
elsif trace.valid?
|
|
||||||
head :internal_server_error
|
|
||||||
else
|
|
||||||
head :bad_request
|
|
||||||
end
|
|
||||||
else
|
|
||||||
head :bad_request
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def do_create(file, tags, description, visibility)
|
def do_create(file, tags, description, visibility)
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
# Update and read user preferences, which are arbitrayr key/val pairs
|
|
||||||
class UserPreferencesController < ApplicationController
|
|
||||||
skip_before_action :verify_authenticity_token
|
|
||||||
before_action :authorize
|
|
||||||
|
|
||||||
authorize_resource
|
|
||||||
|
|
||||||
around_action :api_call_handle_error
|
|
||||||
|
|
||||||
##
|
|
||||||
# return all the preferences as an XML document
|
|
||||||
def read
|
|
||||||
doc = OSM::API.new.get_xml_doc
|
|
||||||
|
|
||||||
prefs = current_user.preferences
|
|
||||||
|
|
||||||
el1 = XML::Node.new "preferences"
|
|
||||||
|
|
||||||
prefs.each do |pref|
|
|
||||||
el1 << pref.to_xml_node
|
|
||||||
end
|
|
||||||
|
|
||||||
doc.root << el1
|
|
||||||
render :xml => doc.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# return the value for a single preference
|
|
||||||
def read_one
|
|
||||||
pref = UserPreference.find([current_user.id, params[:preference_key]])
|
|
||||||
|
|
||||||
render :plain => pref.v.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# update the entire set of preferences
|
|
||||||
def update
|
|
||||||
old_preferences = current_user.preferences.each_with_object({}) do |preference, preferences|
|
|
||||||
preferences[preference.k] = preference
|
|
||||||
end
|
|
||||||
|
|
||||||
new_preferences = {}
|
|
||||||
|
|
||||||
doc = XML::Parser.string(request.raw_post, :options => XML::Parser::Options::NOERROR).parse
|
|
||||||
|
|
||||||
doc.find("//preferences/preference").each do |pt|
|
|
||||||
if preference = old_preferences.delete(pt["k"])
|
|
||||||
preference.v = pt["v"]
|
|
||||||
elsif new_preferences.include?(pt["k"])
|
|
||||||
raise OSM::APIDuplicatePreferenceError, pt["k"]
|
|
||||||
else
|
|
||||||
preference = current_user.preferences.build(:k => pt["k"], :v => pt["v"])
|
|
||||||
end
|
|
||||||
|
|
||||||
new_preferences[preference.k] = preference
|
|
||||||
end
|
|
||||||
|
|
||||||
old_preferences.each_value(&:delete)
|
|
||||||
|
|
||||||
new_preferences.each_value(&:save!)
|
|
||||||
|
|
||||||
render :plain => ""
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# update the value of a single preference
|
|
||||||
def update_one
|
|
||||||
begin
|
|
||||||
pref = UserPreference.find([current_user.id, params[:preference_key]])
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
pref = UserPreference.new
|
|
||||||
pref.user = current_user
|
|
||||||
pref.k = params[:preference_key]
|
|
||||||
end
|
|
||||||
|
|
||||||
pref.v = request.raw_post.chomp
|
|
||||||
pref.save!
|
|
||||||
|
|
||||||
render :plain => ""
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# delete a single preference
|
|
||||||
def delete_one
|
|
||||||
UserPreference.find([current_user.id, params[:preference_key]]).delete
|
|
||||||
|
|
||||||
render :plain => ""
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,22 +1,17 @@
|
||||||
class UsersController < ApplicationController
|
class UsersController < ApplicationController
|
||||||
layout "site", :except => [:api_details]
|
layout "site"
|
||||||
|
|
||||||
skip_before_action :verify_authenticity_token, :only => [:api_read, :api_users, :api_details, :api_gpx_files, :auth_success]
|
skip_before_action :verify_authenticity_token, :only => [:auth_success]
|
||||||
before_action :disable_terms_redirect, :only => [:terms, :save, :logout, :api_details]
|
before_action :disable_terms_redirect, :only => [:terms, :save, :logout]
|
||||||
before_action :authorize, :only => [:api_details, :api_gpx_files]
|
before_action :authorize_web
|
||||||
before_action :authorize_web, :except => [:api_read, :api_users, :api_details, :api_gpx_files]
|
before_action :set_locale
|
||||||
before_action :set_locale, :except => [:api_read, :api_users, :api_details, :api_gpx_files]
|
|
||||||
before_action :api_deny_access_handler, :only => [:api_read, :api_users, :api_details, :api_gpx_files]
|
|
||||||
|
|
||||||
authorize_resource
|
authorize_resource
|
||||||
|
|
||||||
before_action :require_self, :only => [:account]
|
before_action :require_self, :only => [:account]
|
||||||
before_action :check_database_readable, :except => [:login, :api_read, :api_users, :api_details, :api_gpx_files]
|
before_action :check_database_readable, :except => [:login]
|
||||||
before_action :check_database_writable, :only => [:new, :account, :confirm, :confirm_email, :lost_password, :reset_password, :go_public, :make_friend, :remove_friend]
|
before_action :check_database_writable, :only => [:new, :account, :confirm, :confirm_email, :lost_password, :reset_password, :go_public, :make_friend, :remove_friend]
|
||||||
before_action :check_api_readable, :only => [:api_read, :api_users, :api_details, :api_gpx_files]
|
|
||||||
before_action :require_cookies, :only => [:new, :login, :confirm]
|
before_action :require_cookies, :only => [:new, :login, :confirm]
|
||||||
around_action :api_call_handle_error, :only => [:api_read, :api_users, :api_details, :api_gpx_files]
|
|
||||||
before_action :lookup_user_by_id, :only => [:api_read]
|
|
||||||
before_action :lookup_user_by_name, :only => [:set_status, :delete]
|
before_action :lookup_user_by_name, :only => [:set_status, :delete]
|
||||||
before_action :allow_thirdparty_images, :only => [:show, :account]
|
before_action :allow_thirdparty_images, :only => [:show, :account]
|
||||||
|
|
||||||
|
@ -373,39 +368,6 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def api_read
|
|
||||||
if @user.visible?
|
|
||||||
render :action => :api_read, :content_type => "text/xml"
|
|
||||||
else
|
|
||||||
head :gone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def api_details
|
|
||||||
@user = current_user
|
|
||||||
render :action => :api_read, :content_type => "text/xml"
|
|
||||||
end
|
|
||||||
|
|
||||||
def api_users
|
|
||||||
raise OSM::APIBadUserInput, "The parameter users is required, and must be of the form users=id[,id[,id...]]" unless params["users"]
|
|
||||||
|
|
||||||
ids = params["users"].split(",").collect(&:to_i)
|
|
||||||
|
|
||||||
raise OSM::APIBadUserInput, "No users were given to search for" if ids.empty?
|
|
||||||
|
|
||||||
@users = User.visible.find(ids)
|
|
||||||
|
|
||||||
render :action => :api_users, :content_type => "text/xml"
|
|
||||||
end
|
|
||||||
|
|
||||||
def api_gpx_files
|
|
||||||
doc = OSM::API.new.get_xml_doc
|
|
||||||
current_user.traces.reload.each do |trace|
|
|
||||||
doc.root << trace.to_xml_node
|
|
||||||
end
|
|
||||||
render :xml => doc.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@user = User.find_by(:display_name => params[:display_name])
|
@user = User.find_by(:display_name => params[:display_name])
|
||||||
|
|
||||||
|
@ -752,12 +714,6 @@ class UsersController < ApplicationController
|
||||||
head :forbidden if params[:display_name] != current_user.display_name
|
head :forbidden if params[:display_name] != current_user.display_name
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
|
||||||
# ensure that there is a "user" instance variable
|
|
||||||
def lookup_user_by_id
|
|
||||||
@user = User.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# ensure that there is a "user" instance variable
|
# ensure that there is a "user" instance variable
|
||||||
def lookup_user_by_name
|
def lookup_user_by_name
|
||||||
|
|
|
@ -1,118 +0,0 @@
|
||||||
class WaysController < ApplicationController
|
|
||||||
require "xml/libxml"
|
|
||||||
|
|
||||||
skip_before_action :verify_authenticity_token
|
|
||||||
before_action :authorize, :only => [:create, :update, :delete]
|
|
||||||
before_action :api_deny_access_handler
|
|
||||||
|
|
||||||
authorize_resource
|
|
||||||
|
|
||||||
before_action :require_public_data, :only => [:create, :update, :delete]
|
|
||||||
before_action :check_api_writable, :only => [:create, :update, :delete]
|
|
||||||
before_action :check_api_readable, :except => [:create, :update, :delete]
|
|
||||||
around_action :api_call_handle_error, :api_call_timeout
|
|
||||||
|
|
||||||
def create
|
|
||||||
assert_method :put
|
|
||||||
|
|
||||||
way = Way.from_xml(request.raw_post, true)
|
|
||||||
|
|
||||||
# Assume that Way.from_xml has thrown an exception if there is an error parsing the xml
|
|
||||||
way.create_with_history current_user
|
|
||||||
render :plain => way.id.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
|
||||||
way = Way.find(params[:id])
|
|
||||||
|
|
||||||
response.last_modified = way.timestamp
|
|
||||||
|
|
||||||
if way.visible
|
|
||||||
render :xml => way.to_xml.to_s
|
|
||||||
else
|
|
||||||
head :gone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
way = Way.find(params[:id])
|
|
||||||
new_way = Way.from_xml(request.raw_post)
|
|
||||||
|
|
||||||
unless new_way && new_way.id == way.id
|
|
||||||
raise OSM::APIBadUserInput, "The id in the url (#{way.id}) is not the same as provided in the xml (#{new_way.id})"
|
|
||||||
end
|
|
||||||
|
|
||||||
way.update_from(new_way, current_user)
|
|
||||||
render :plain => way.version.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# This is the API call to delete a way
|
|
||||||
def delete
|
|
||||||
way = Way.find(params[:id])
|
|
||||||
new_way = Way.from_xml(request.raw_post)
|
|
||||||
|
|
||||||
if new_way && new_way.id == way.id
|
|
||||||
way.delete_with_history!(new_way, current_user)
|
|
||||||
render :plain => way.version.to_s
|
|
||||||
else
|
|
||||||
head :bad_request
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def full
|
|
||||||
way = Way.includes(:nodes => :node_tags).find(params[:id])
|
|
||||||
|
|
||||||
if way.visible
|
|
||||||
visible_nodes = {}
|
|
||||||
changeset_cache = {}
|
|
||||||
user_display_name_cache = {}
|
|
||||||
|
|
||||||
doc = OSM::API.new.get_xml_doc
|
|
||||||
way.nodes.uniq.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
|
|
||||||
doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
|
|
||||||
|
|
||||||
render :xml => doc.to_s
|
|
||||||
else
|
|
||||||
head :gone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def index
|
|
||||||
unless params["ways"]
|
|
||||||
raise OSM::APIBadUserInput, "The parameter ways is required, and must be of the form ways=id[,id[,id...]]"
|
|
||||||
end
|
|
||||||
|
|
||||||
ids = params["ways"].split(",").collect(&:to_i)
|
|
||||||
|
|
||||||
raise OSM::APIBadUserInput, "No ways were given to search for" if ids.empty?
|
|
||||||
|
|
||||||
doc = OSM::API.new.get_xml_doc
|
|
||||||
|
|
||||||
Way.find(ids).each do |way|
|
|
||||||
doc.root << way.to_xml_node
|
|
||||||
end
|
|
||||||
|
|
||||||
render :xml => doc.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# returns all the ways which are currently using the node given in the
|
|
||||||
# :id parameter. note that this used to return deleted ways as well, but
|
|
||||||
# this seemed not to be the expected behaviour, so it was removed.
|
|
||||||
def ways_for_node
|
|
||||||
wayids = WayNode.where(:node_id => params[:id]).collect { |ws| ws.id[0] }.uniq
|
|
||||||
|
|
||||||
doc = OSM::API.new.get_xml_doc
|
|
||||||
|
|
||||||
Way.find(wayids).each do |way|
|
|
||||||
doc.root << way.to_xml_node if way.visible
|
|
||||||
end
|
|
||||||
|
|
||||||
render :xml => doc.to_s
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -18,7 +18,7 @@ module NoteHelper
|
||||||
elsif author.status == "deleted"
|
elsif author.status == "deleted"
|
||||||
t("users.no_such_user.deleted")
|
t("users.no_such_user.deleted")
|
||||||
else
|
else
|
||||||
link_to h(author.display_name), link_options.merge(:controller => "users", :action => "show", :display_name => author.display_name)
|
link_to h(author.display_name), link_options.merge(:controller => "/users", :action => "show", :display_name => author.display_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,11 +2,11 @@ xml.item do
|
||||||
location = describe_location(note.lat, note.lon, 14, locale)
|
location = describe_location(note.lat, note.lon, 14, locale)
|
||||||
|
|
||||||
if note.closed?
|
if note.closed?
|
||||||
xml.title t("notes.rss.closed", :place => location)
|
xml.title t("api.notes.rss.closed", :place => location)
|
||||||
elsif note.comments.length > 1
|
elsif note.comments.length > 1
|
||||||
xml.title t("notes.rss.commented", :place => location)
|
xml.title t("api.notes.rss.commented", :place => location)
|
||||||
else
|
else
|
||||||
xml.title t("notes.rss.opened", :place => location)
|
xml.title t("api.notes.rss.opened", :place => location)
|
||||||
end
|
end
|
||||||
|
|
||||||
xml.link browse_note_url(note)
|
xml.link browse_note_url(note)
|
|
@ -5,18 +5,18 @@ xml.rss("version" => "2.0",
|
||||||
"xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
|
"xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
|
||||||
"xmlns:georss" => "http://www.georss.org/georss") do
|
"xmlns:georss" => "http://www.georss.org/georss") do
|
||||||
xml.channel do
|
xml.channel do
|
||||||
xml.title t("notes.rss.title")
|
xml.title t("api.notes.rss.title")
|
||||||
xml.description t("notes.rss.description_area", :min_lat => @min_lat, :min_lon => @min_lon, :max_lat => @max_lat, :max_lon => @max_lon)
|
xml.description t("api.notes.rss.description_area", :min_lat => @min_lat, :min_lon => @min_lon, :max_lat => @max_lat, :max_lon => @max_lon)
|
||||||
xml.link url_for(:controller => "site", :action => "index", :only_path => false)
|
xml.link url_for(:controller => "/site", :action => "index", :only_path => false)
|
||||||
|
|
||||||
@comments.each do |comment|
|
@comments.each do |comment|
|
||||||
location = describe_location(comment.note.lat, comment.note.lon, 14, locale)
|
location = describe_location(comment.note.lat, comment.note.lon, 14, locale)
|
||||||
|
|
||||||
xml.item do
|
xml.item do
|
||||||
xml.title t("notes.rss.#{comment.event}", :place => location)
|
xml.title t("api.notes.rss.#{comment.event}", :place => location)
|
||||||
|
|
||||||
xml.link url_for(:controller => "browse", :action => "note", :id => comment.note.id, :anchor => "c#{comment.id}", :only_path => false)
|
xml.link url_for(:controller => "/browse", :action => "note", :id => comment.note.id, :anchor => "c#{comment.id}", :only_path => false)
|
||||||
xml.guid url_for(:controller => "browse", :action => "note", :id => comment.note.id, :anchor => "c#{comment.id}", :only_path => false)
|
xml.guid url_for(:controller => "/browse", :action => "note", :id => comment.note.id, :anchor => "c#{comment.id}", :only_path => false)
|
||||||
|
|
||||||
xml.description do
|
xml.description do
|
||||||
xml.cdata! render(:partial => "entry", :object => comment, :formats => [:html])
|
xml.cdata! render(:partial => "entry", :object => comment, :formats => [:html])
|
|
@ -5,9 +5,9 @@ xml.rss("version" => "2.0",
|
||||||
"xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
|
"xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
|
||||||
"xmlns:georss" => "http://www.georss.org/georss") do
|
"xmlns:georss" => "http://www.georss.org/georss") do
|
||||||
xml.channel do
|
xml.channel do
|
||||||
xml.title t("notes.rss.title")
|
xml.title t("api.notes.rss.title")
|
||||||
xml.description t("notes.rss.description_area", :min_lat => @min_lat, :min_lon => @min_lon, :max_lat => @max_lat, :max_lon => @max_lon)
|
xml.description t("api.notes.rss.description_area", :min_lat => @min_lat, :min_lon => @min_lon, :max_lat => @max_lat, :max_lon => @max_lon)
|
||||||
xml.link url_for(:controller => "site", :action => "index", :only_path => false)
|
xml.link url_for(:controller => "/site", :action => "index", :only_path => false)
|
||||||
|
|
||||||
xml << (render(:partial => "note", :collection => @notes) || "")
|
xml << (render(:partial => "note", :collection => @notes) || "")
|
||||||
end
|
end
|
|
@ -4,9 +4,9 @@ xml.rss("version" => "2.0",
|
||||||
"xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
|
"xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
|
||||||
"xmlns:georss" => "http://www.georss.org/georss") do
|
"xmlns:georss" => "http://www.georss.org/georss") do
|
||||||
xml.channel do
|
xml.channel do
|
||||||
xml.title t("notes.rss.title")
|
xml.title t("api.notes.rss.title")
|
||||||
xml.description t("notes.rss.description_item", :id => @note.id)
|
xml.description t("api.notes.rss.description_item", :id => @note.id)
|
||||||
xml.link url_for(:controller => "site", :action => "index", :only_path => false)
|
xml.link url_for(:controller => "/site", :action => "index", :only_path => false)
|
||||||
|
|
||||||
xml << render(:partial => "note", :object => @note)
|
xml << render(:partial => "note", :object => @note)
|
||||||
end
|
end
|
|
@ -137,7 +137,7 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class='secondary-actions'>
|
<div class='secondary-actions'>
|
||||||
<%= link_to(t('.changesetxml'), :controller => "changesets", :action => "show") %>
|
<%= link_to(t('.changesetxml'), :controller => "api/changesets", :action => "show") %>
|
||||||
·
|
·
|
||||||
<%= link_to(t('.osmchangexml'), :controller => "changesets", :action => "download") %>
|
<%= link_to(t('.osmchangexml'), :controller => "api/changesets", :action => "download") %>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<%= render :partial => @type, :object => @feature %>
|
<%= render :partial => @type, :object => @feature %>
|
||||||
|
|
||||||
<div class='secondary-actions'>
|
<div class='secondary-actions'>
|
||||||
<%= link_to(t('browse.download_xml'), :controller => @type.pluralize, :action => :show) %>
|
<%= link_to(t('browse.download_xml'), :controller => "api/#{@type.pluralize}", :action => :show) %>
|
||||||
·
|
·
|
||||||
<%= link_to(t('browse.view_history'), :action => "#{@type}_history") %>
|
<%= link_to(t('browse.view_history'), :action => "#{@type}_history") %>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<%= render :partial => @type, :collection => @feature.send("old_#{@type}s").reverse %>
|
<%= render :partial => @type, :collection => @feature.send("old_#{@type}s").reverse %>
|
||||||
|
|
||||||
<div class='secondary-actions'>
|
<div class='secondary-actions'>
|
||||||
<%= link_to(t('browse.download_xml'), :controller => "old_#{@type.pluralize}", :action => "history") %>
|
<%= link_to(t('browse.download_xml'), :controller => "api/old_#{@type.pluralize}", :action => "history") %>
|
||||||
·
|
·
|
||||||
<%= link_to(t('browse.view_details'), :action => @type) %>
|
<%= link_to(t('browse.view_details'), :action => @type) %>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -98,6 +98,28 @@ en:
|
||||||
remote:
|
remote:
|
||||||
name: "Remote Control"
|
name: "Remote Control"
|
||||||
description: "Remote Control (JOSM or Merkaartor)"
|
description: "Remote Control (JOSM or Merkaartor)"
|
||||||
|
api:
|
||||||
|
notes:
|
||||||
|
comment:
|
||||||
|
opened_at_html: "Created %{when} ago"
|
||||||
|
opened_at_by_html: "Created %{when} ago by %{user}"
|
||||||
|
commented_at_html: "Updated %{when} ago"
|
||||||
|
commented_at_by_html: "Updated %{when} ago by %{user}"
|
||||||
|
closed_at_html: "Resolved %{when} ago"
|
||||||
|
closed_at_by_html: "Resolved %{when} ago by %{user}"
|
||||||
|
reopened_at_html: "Reactivated %{when} ago"
|
||||||
|
reopened_at_by_html: "Reactivated %{when} ago by %{user}"
|
||||||
|
rss:
|
||||||
|
title: "OpenStreetMap Notes"
|
||||||
|
description_area: "A list of notes, reported, commented on or closed in your area [(%{min_lat}|%{min_lon}) -- (%{max_lat}|%{max_lon})]"
|
||||||
|
description_item: "An rss feed for note %{id}"
|
||||||
|
opened: "new note (near %{place})"
|
||||||
|
commented: "new comment (near %{place})"
|
||||||
|
closed: "closed note (near %{place})"
|
||||||
|
reopened: "reactivated note (near %{place})"
|
||||||
|
entry:
|
||||||
|
comment: Comment
|
||||||
|
full: Full note
|
||||||
browse:
|
browse:
|
||||||
created: "Created"
|
created: "Created"
|
||||||
closed: "Closed"
|
closed: "Closed"
|
||||||
|
@ -2331,26 +2353,6 @@ en:
|
||||||
next: "Next »"
|
next: "Next »"
|
||||||
previous: "« Previous"
|
previous: "« Previous"
|
||||||
notes:
|
notes:
|
||||||
comment:
|
|
||||||
opened_at_html: "Created %{when} ago"
|
|
||||||
opened_at_by_html: "Created %{when} ago by %{user}"
|
|
||||||
commented_at_html: "Updated %{when} ago"
|
|
||||||
commented_at_by_html: "Updated %{when} ago by %{user}"
|
|
||||||
closed_at_html: "Resolved %{when} ago"
|
|
||||||
closed_at_by_html: "Resolved %{when} ago by %{user}"
|
|
||||||
reopened_at_html: "Reactivated %{when} ago"
|
|
||||||
reopened_at_by_html: "Reactivated %{when} ago by %{user}"
|
|
||||||
rss:
|
|
||||||
title: "OpenStreetMap Notes"
|
|
||||||
description_area: "A list of notes, reported, commented on or closed in your area [(%{min_lat}|%{min_lon}) -- (%{max_lat}|%{max_lon})]"
|
|
||||||
description_item: "An rss feed for note %{id}"
|
|
||||||
opened: "new note (near %{place})"
|
|
||||||
commented: "new comment (near %{place})"
|
|
||||||
closed: "closed note (near %{place})"
|
|
||||||
reopened: "reactivated note (near %{place})"
|
|
||||||
entry:
|
|
||||||
comment: Comment
|
|
||||||
full: Full note
|
|
||||||
mine:
|
mine:
|
||||||
title: "Notes submitted or commented on by %{user}"
|
title: "Notes submitted or commented on by %{user}"
|
||||||
heading: "%{user}'s notes"
|
heading: "%{user}'s notes"
|
||||||
|
|
142
config/routes.rb
142
config/routes.rb
|
@ -8,52 +8,52 @@ OpenStreetMap::Application.routes.draw do
|
||||||
get "capabilities" => "api/capabilities#show"
|
get "capabilities" => "api/capabilities#show"
|
||||||
get "permissions" => "api/permissions#show"
|
get "permissions" => "api/permissions#show"
|
||||||
|
|
||||||
put "changeset/create" => "changesets#create"
|
put "changeset/create" => "api/changesets#create"
|
||||||
post "changeset/:id/upload" => "changesets#upload", :id => /\d+/
|
post "changeset/:id/upload" => "api/changesets#upload", :id => /\d+/
|
||||||
get "changeset/:id/download" => "changesets#download", :as => :changeset_download, :id => /\d+/
|
get "changeset/:id/download" => "api/changesets#download", :as => :changeset_download, :id => /\d+/
|
||||||
post "changeset/:id/expand_bbox" => "changesets#expand_bbox", :id => /\d+/
|
post "changeset/:id/expand_bbox" => "api/changesets#expand_bbox", :id => /\d+/
|
||||||
get "changeset/:id" => "changesets#show", :as => :changeset_show, :id => /\d+/
|
get "changeset/:id" => "api/changesets#show", :as => :changeset_show, :id => /\d+/
|
||||||
post "changeset/:id/subscribe" => "changesets#subscribe", :as => :changeset_subscribe, :id => /\d+/
|
post "changeset/:id/subscribe" => "api/changesets#subscribe", :as => :changeset_subscribe, :id => /\d+/
|
||||||
post "changeset/:id/unsubscribe" => "changesets#unsubscribe", :as => :changeset_unsubscribe, :id => /\d+/
|
post "changeset/:id/unsubscribe" => "api/changesets#unsubscribe", :as => :changeset_unsubscribe, :id => /\d+/
|
||||||
put "changeset/:id" => "changesets#update", :id => /\d+/
|
put "changeset/:id" => "api/changesets#update", :id => /\d+/
|
||||||
put "changeset/:id/close" => "changesets#close", :id => /\d+/
|
put "changeset/:id/close" => "api/changesets#close", :id => /\d+/
|
||||||
get "changesets" => "changesets#query"
|
get "changesets" => "api/changesets#query"
|
||||||
post "changeset/:id/comment" => "changeset_comments#create", :as => :changeset_comment, :id => /\d+/
|
post "changeset/:id/comment" => "api/changeset_comments#create", :as => :changeset_comment, :id => /\d+/
|
||||||
post "changeset/comment/:id/hide" => "changeset_comments#destroy", :as => :changeset_comment_hide, :id => /\d+/
|
post "changeset/comment/:id/hide" => "api/changeset_comments#destroy", :as => :changeset_comment_hide, :id => /\d+/
|
||||||
post "changeset/comment/:id/unhide" => "changeset_comments#restore", :as => :changeset_comment_unhide, :id => /\d+/
|
post "changeset/comment/:id/unhide" => "api/changeset_comments#restore", :as => :changeset_comment_unhide, :id => /\d+/
|
||||||
|
|
||||||
put "node/create" => "nodes#create"
|
put "node/create" => "api/nodes#create"
|
||||||
get "node/:id/ways" => "ways#ways_for_node", :id => /\d+/
|
get "node/:id/ways" => "api/ways#ways_for_node", :id => /\d+/
|
||||||
get "node/:id/relations" => "relations#relations_for_node", :id => /\d+/
|
get "node/:id/relations" => "api/relations#relations_for_node", :id => /\d+/
|
||||||
get "node/:id/history" => "old_nodes#history", :id => /\d+/
|
get "node/:id/history" => "api/old_nodes#history", :id => /\d+/
|
||||||
post "node/:id/:version/redact" => "old_nodes#redact", :version => /\d+/, :id => /\d+/
|
post "node/:id/:version/redact" => "api/old_nodes#redact", :version => /\d+/, :id => /\d+/
|
||||||
get "node/:id/:version" => "old_nodes#version", :id => /\d+/, :version => /\d+/
|
get "node/:id/:version" => "api/old_nodes#version", :id => /\d+/, :version => /\d+/
|
||||||
get "node/:id" => "nodes#show", :id => /\d+/
|
get "node/:id" => "api/nodes#show", :id => /\d+/
|
||||||
put "node/:id" => "nodes#update", :id => /\d+/
|
put "node/:id" => "api/nodes#update", :id => /\d+/
|
||||||
delete "node/:id" => "nodes#delete", :id => /\d+/
|
delete "node/:id" => "api/nodes#delete", :id => /\d+/
|
||||||
get "nodes" => "nodes#index"
|
get "nodes" => "api/nodes#index"
|
||||||
|
|
||||||
put "way/create" => "ways#create"
|
put "way/create" => "api/ways#create"
|
||||||
get "way/:id/history" => "old_ways#history", :id => /\d+/
|
get "way/:id/history" => "api/old_ways#history", :id => /\d+/
|
||||||
get "way/:id/full" => "ways#full", :id => /\d+/
|
get "way/:id/full" => "api/ways#full", :id => /\d+/
|
||||||
get "way/:id/relations" => "relations#relations_for_way", :id => /\d+/
|
get "way/:id/relations" => "api/relations#relations_for_way", :id => /\d+/
|
||||||
post "way/:id/:version/redact" => "old_ways#redact", :version => /\d+/, :id => /\d+/
|
post "way/:id/:version/redact" => "api/old_ways#redact", :version => /\d+/, :id => /\d+/
|
||||||
get "way/:id/:version" => "old_ways#version", :id => /\d+/, :version => /\d+/
|
get "way/:id/:version" => "api/old_ways#version", :id => /\d+/, :version => /\d+/
|
||||||
get "way/:id" => "ways#show", :id => /\d+/
|
get "way/:id" => "api/ways#show", :id => /\d+/
|
||||||
put "way/:id" => "ways#update", :id => /\d+/
|
put "way/:id" => "api/ways#update", :id => /\d+/
|
||||||
delete "way/:id" => "ways#delete", :id => /\d+/
|
delete "way/:id" => "api/ways#delete", :id => /\d+/
|
||||||
get "ways" => "ways#index"
|
get "ways" => "api/ways#index"
|
||||||
|
|
||||||
put "relation/create" => "relations#create"
|
put "relation/create" => "api/relations#create"
|
||||||
get "relation/:id/relations" => "relations#relations_for_relation", :id => /\d+/
|
get "relation/:id/relations" => "api/relations#relations_for_relation", :id => /\d+/
|
||||||
get "relation/:id/history" => "old_relations#history", :id => /\d+/
|
get "relation/:id/history" => "api/old_relations#history", :id => /\d+/
|
||||||
get "relation/:id/full" => "relations#full", :id => /\d+/
|
get "relation/:id/full" => "api/relations#full", :id => /\d+/
|
||||||
post "relation/:id/:version/redact" => "old_relations#redact", :version => /\d+/, :id => /\d+/
|
post "relation/:id/:version/redact" => "api/old_relations#redact", :version => /\d+/, :id => /\d+/
|
||||||
get "relation/:id/:version" => "old_relations#version", :id => /\d+/, :version => /\d+/
|
get "relation/:id/:version" => "api/old_relations#version", :id => /\d+/, :version => /\d+/
|
||||||
get "relation/:id" => "relations#show", :id => /\d+/
|
get "relation/:id" => "api/relations#show", :id => /\d+/
|
||||||
put "relation/:id" => "relations#update", :id => /\d+/
|
put "relation/:id" => "api/relations#update", :id => /\d+/
|
||||||
delete "relation/:id" => "relations#delete", :id => /\d+/
|
delete "relation/:id" => "api/relations#delete", :id => /\d+/
|
||||||
get "relations" => "relations#index"
|
get "relations" => "api/relations#index"
|
||||||
|
|
||||||
get "map" => "api/map#index"
|
get "map" => "api/map#index"
|
||||||
|
|
||||||
|
@ -61,36 +61,36 @@ OpenStreetMap::Application.routes.draw do
|
||||||
|
|
||||||
get "changes" => "api/changes#index"
|
get "changes" => "api/changes#index"
|
||||||
|
|
||||||
get "search" => "search#search_all", :as => "api_search"
|
get "search" => "api/search#search_all", :as => "api_search"
|
||||||
get "ways/search" => "search#search_ways"
|
get "ways/search" => "api/search#search_ways"
|
||||||
get "relations/search" => "search#search_relations"
|
get "relations/search" => "api/search#search_relations"
|
||||||
get "nodes/search" => "search#search_nodes"
|
get "nodes/search" => "api/search#search_nodes"
|
||||||
|
|
||||||
get "user/:id" => "users#api_read", :id => /\d+/
|
get "user/:id" => "api/users#api_read", :id => /\d+/
|
||||||
get "user/details" => "users#api_details"
|
get "user/details" => "api/users#api_details"
|
||||||
get "user/gpx_files" => "users#api_gpx_files"
|
get "user/gpx_files" => "api/users#api_gpx_files"
|
||||||
get "users" => "users#api_users", :as => :api_users
|
get "users" => "api/users#api_users", :as => :api_users
|
||||||
|
|
||||||
get "user/preferences" => "user_preferences#read"
|
get "user/preferences" => "api/user_preferences#read"
|
||||||
get "user/preferences/:preference_key" => "user_preferences#read_one"
|
get "user/preferences/:preference_key" => "api/user_preferences#read_one"
|
||||||
put "user/preferences" => "user_preferences#update"
|
put "user/preferences" => "api/user_preferences#update"
|
||||||
put "user/preferences/:preference_key" => "user_preferences#update_one"
|
put "user/preferences/:preference_key" => "api/user_preferences#update_one"
|
||||||
delete "user/preferences/:preference_key" => "user_preferences#delete_one"
|
delete "user/preferences/:preference_key" => "api/user_preferences#delete_one"
|
||||||
|
|
||||||
post "gpx/create" => "traces#api_create"
|
post "gpx/create" => "api/traces#api_create"
|
||||||
get "gpx/:id" => "traces#api_read", :id => /\d+/
|
get "gpx/:id" => "api/traces#api_read", :id => /\d+/
|
||||||
put "gpx/:id" => "traces#api_update", :id => /\d+/
|
put "gpx/:id" => "api/traces#api_update", :id => /\d+/
|
||||||
delete "gpx/:id" => "traces#api_delete", :id => /\d+/
|
delete "gpx/:id" => "api/traces#api_delete", :id => /\d+/
|
||||||
get "gpx/:id/details" => "traces#api_read", :id => /\d+/
|
get "gpx/:id/details" => "api/traces#api_read", :id => /\d+/
|
||||||
get "gpx/:id/data" => "traces#api_data"
|
get "gpx/:id/data" => "api/traces#api_data"
|
||||||
|
|
||||||
# AMF (ActionScript) API
|
# AMF (ActionScript) API
|
||||||
post "amf/read" => "amf#amf_read"
|
post "amf/read" => "api/amf#amf_read"
|
||||||
post "amf/write" => "amf#amf_write"
|
post "amf/write" => "api/amf#amf_write"
|
||||||
get "swf/trackpoints" => "swf#trackpoints"
|
get "swf/trackpoints" => "api/swf#trackpoints"
|
||||||
|
|
||||||
# Map notes API
|
# Map notes API
|
||||||
resources :notes, :except => [:new, :edit, :update], :constraints => { :id => /\d+/ }, :defaults => { :format => "xml" } do
|
resources :notes, :except => [:new, :edit, :update], :constraints => { :id => /\d+/ }, :defaults => { :format => "xml" }, :controller => "api/notes" do
|
||||||
collection do
|
collection do
|
||||||
get "search"
|
get "search"
|
||||||
get "feed", :defaults => { :format => "rss" }
|
get "feed", :defaults => { :format => "rss" }
|
||||||
|
@ -103,11 +103,11 @@ OpenStreetMap::Application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
post "notes/addPOIexec" => "notes#create"
|
post "notes/addPOIexec" => "api/notes#create"
|
||||||
post "notes/closePOIexec" => "notes#close"
|
post "notes/closePOIexec" => "api/notes#close"
|
||||||
post "notes/editPOIexec" => "notes#comment"
|
post "notes/editPOIexec" => "api/notes#comment"
|
||||||
get "notes/getGPX" => "notes#index", :format => "gpx"
|
get "notes/getGPX" => "api/notes#index", :format => "gpx"
|
||||||
get "notes/getRSSfeed" => "notes#feed", :format => "rss"
|
get "notes/getRSSfeed" => "api/notes#feed", :format => "rss"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Data browsing
|
# Data browsing
|
||||||
|
|
File diff suppressed because it is too large
Load diff
1465
test/controllers/api/amf_controller_test.rb
Normal file
1465
test/controllers/api/amf_controller_test.rb
Normal file
File diff suppressed because it is too large
Load diff
254
test/controllers/api/changeset_comments_controller_test.rb
Normal file
254
test/controllers/api/changeset_comments_controller_test.rb
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class ChangesetCommentsControllerTest < ActionController::TestCase
|
||||||
|
##
|
||||||
|
# test all routes which lead to this controller
|
||||||
|
def test_routes
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/changeset/1/comment", :method => :post },
|
||||||
|
{ :controller => "api/changeset_comments", :action => "create", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/changeset/comment/1/hide", :method => :post },
|
||||||
|
{ :controller => "api/changeset_comments", :action => "destroy", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/changeset/comment/1/unhide", :method => :post },
|
||||||
|
{ :controller => "api/changeset_comments", :action => "restore", :id => "1" }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# create comment success
|
||||||
|
def test_create_comment_success
|
||||||
|
user = create(:user)
|
||||||
|
user2 = create(:user)
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
suspended_user = create(:user, :suspended)
|
||||||
|
deleted_user = create(:user, :deleted)
|
||||||
|
private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
|
||||||
|
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
assert_difference "ChangesetComment.count", 1 do
|
||||||
|
assert_no_difference "ActionMailer::Base.deliveries.size" do
|
||||||
|
perform_enqueued_jobs do
|
||||||
|
post :create, :params => { :id => private_user_closed_changeset.id, :text => "This is a comment" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
changeset = create(:changeset, :closed, :user => private_user)
|
||||||
|
changeset.subscribers.push(private_user)
|
||||||
|
changeset.subscribers.push(user)
|
||||||
|
changeset.subscribers.push(suspended_user)
|
||||||
|
changeset.subscribers.push(deleted_user)
|
||||||
|
|
||||||
|
assert_difference "ChangesetComment.count", 1 do
|
||||||
|
assert_difference "ActionMailer::Base.deliveries.size", 1 do
|
||||||
|
perform_enqueued_jobs do
|
||||||
|
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
email = ActionMailer::Base.deliveries.first
|
||||||
|
assert_equal 1, email.to.length
|
||||||
|
assert_equal "[OpenStreetMap] #{user.display_name} has commented on one of your changesets", email.subject
|
||||||
|
assert_equal private_user.email, email.to.first
|
||||||
|
|
||||||
|
ActionMailer::Base.deliveries.clear
|
||||||
|
|
||||||
|
basic_authorization user2.email, "test"
|
||||||
|
|
||||||
|
assert_difference "ChangesetComment.count", 1 do
|
||||||
|
assert_difference "ActionMailer::Base.deliveries.size", 2 do
|
||||||
|
perform_enqueued_jobs do
|
||||||
|
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
email = ActionMailer::Base.deliveries.find { |e| e.to.first == private_user.email }
|
||||||
|
assert_not_nil email
|
||||||
|
assert_equal 1, email.to.length
|
||||||
|
assert_equal "[OpenStreetMap] #{user2.display_name} has commented on one of your changesets", email.subject
|
||||||
|
|
||||||
|
email = ActionMailer::Base.deliveries.find { |e| e.to.first == user.email }
|
||||||
|
assert_not_nil email
|
||||||
|
assert_equal 1, email.to.length
|
||||||
|
assert_equal "[OpenStreetMap] #{user2.display_name} has commented on a changeset you are interested in", email.subject
|
||||||
|
|
||||||
|
ActionMailer::Base.deliveries.clear
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# create comment fail
|
||||||
|
def test_create_comment_fail
|
||||||
|
# unauthorized
|
||||||
|
post :create, :params => { :id => create(:changeset, :closed).id, :text => "This is a comment" }
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
# bad changeset id
|
||||||
|
assert_no_difference "ChangesetComment.count" do
|
||||||
|
post :create, :params => { :id => 999111, :text => "This is a comment" }
|
||||||
|
end
|
||||||
|
assert_response :not_found
|
||||||
|
|
||||||
|
# not closed changeset
|
||||||
|
assert_no_difference "ChangesetComment.count" do
|
||||||
|
post :create, :params => { :id => create(:changeset).id, :text => "This is a comment" }
|
||||||
|
end
|
||||||
|
assert_response :conflict
|
||||||
|
|
||||||
|
# no text
|
||||||
|
assert_no_difference "ChangesetComment.count" do
|
||||||
|
post :create, :params => { :id => create(:changeset, :closed).id }
|
||||||
|
end
|
||||||
|
assert_response :bad_request
|
||||||
|
|
||||||
|
# empty text
|
||||||
|
assert_no_difference "ChangesetComment.count" do
|
||||||
|
post :create, :params => { :id => create(:changeset, :closed).id, :text => "" }
|
||||||
|
end
|
||||||
|
assert_response :bad_request
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test hide comment fail
|
||||||
|
def test_destroy_comment_fail
|
||||||
|
# unauthorized
|
||||||
|
comment = create(:changeset_comment)
|
||||||
|
assert_equal true, comment.visible
|
||||||
|
|
||||||
|
post :destroy, :params => { :id => comment.id }
|
||||||
|
assert_response :unauthorized
|
||||||
|
assert_equal true, comment.reload.visible
|
||||||
|
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
# not a moderator
|
||||||
|
post :destroy, :params => { :id => comment.id }
|
||||||
|
assert_response :forbidden
|
||||||
|
assert_equal true, comment.reload.visible
|
||||||
|
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
# bad comment id
|
||||||
|
post :destroy, :params => { :id => 999111 }
|
||||||
|
assert_response :not_found
|
||||||
|
assert_equal true, comment.reload.visible
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test hide comment succes
|
||||||
|
def test_hide_comment_success
|
||||||
|
comment = create(:changeset_comment)
|
||||||
|
assert_equal true, comment.visible
|
||||||
|
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
post :destroy, :params => { :id => comment.id }
|
||||||
|
assert_response :success
|
||||||
|
assert_equal false, comment.reload.visible
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test unhide comment fail
|
||||||
|
def test_restore_comment_fail
|
||||||
|
# unauthorized
|
||||||
|
comment = create(:changeset_comment, :visible => false)
|
||||||
|
assert_equal false, comment.visible
|
||||||
|
|
||||||
|
post :restore, :params => { :id => comment.id }
|
||||||
|
assert_response :unauthorized
|
||||||
|
assert_equal false, comment.reload.visible
|
||||||
|
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
# not a moderator
|
||||||
|
post :restore, :params => { :id => comment.id }
|
||||||
|
assert_response :forbidden
|
||||||
|
assert_equal false, comment.reload.visible
|
||||||
|
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
# bad comment id
|
||||||
|
post :restore, :params => { :id => 999111 }
|
||||||
|
assert_response :not_found
|
||||||
|
assert_equal false, comment.reload.visible
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test unhide comment succes
|
||||||
|
def test_unhide_comment_success
|
||||||
|
comment = create(:changeset_comment, :visible => false)
|
||||||
|
assert_equal false, comment.visible
|
||||||
|
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
post :restore, :params => { :id => comment.id }
|
||||||
|
assert_response :success
|
||||||
|
assert_equal true, comment.reload.visible
|
||||||
|
end
|
||||||
|
|
||||||
|
# This test ensures that token capabilities behave correctly for a method that
|
||||||
|
# requires the terms to have been agreed.
|
||||||
|
# (This would be better as an integration or system testcase, since the changeset_comment
|
||||||
|
# create method is simply a stand-in for any method that requires terms agreement.
|
||||||
|
# But writing oauth tests is hard, and so it's easier to put in a controller test.)
|
||||||
|
def test_api_write_and_terms_agreed_via_token
|
||||||
|
user = create(:user, :terms_agreed => nil)
|
||||||
|
token = create(:access_token, :user => user, :allow_write_api => true)
|
||||||
|
changeset = create(:changeset, :closed)
|
||||||
|
|
||||||
|
# Hack together an oauth request - an alternative would be to sign the request properly
|
||||||
|
@request.env["oauth.version"] = 1
|
||||||
|
@request.env["oauth.strategies"] = [:token]
|
||||||
|
@request.env["oauth.token"] = token
|
||||||
|
|
||||||
|
assert_difference "ChangesetComment.count", 0 do
|
||||||
|
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
|
||||||
|
end
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# Try again, after agreement with the terms
|
||||||
|
user.terms_agreed = Time.now
|
||||||
|
user.save!
|
||||||
|
|
||||||
|
assert_difference "ChangesetComment.count", 1 do
|
||||||
|
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
|
||||||
|
end
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
# This test does the same as above, but with basic auth, to similarly test that the
|
||||||
|
# abilities take into account terms agreement too.
|
||||||
|
def test_api_write_and_terms_agreed_via_basic_auth
|
||||||
|
user = create(:user, :terms_agreed => nil)
|
||||||
|
changeset = create(:changeset, :closed)
|
||||||
|
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
assert_difference "ChangesetComment.count", 0 do
|
||||||
|
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
|
||||||
|
end
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# Try again, after agreement with the terms
|
||||||
|
user.terms_agreed = Time.now
|
||||||
|
user.save!
|
||||||
|
|
||||||
|
assert_difference "ChangesetComment.count", 1 do
|
||||||
|
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
|
||||||
|
end
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
1963
test/controllers/api/changesets_controller_test.rb
Normal file
1963
test/controllers/api/changesets_controller_test.rb
Normal file
File diff suppressed because it is too large
Load diff
552
test/controllers/api/nodes_controller_test.rb
Normal file
552
test/controllers/api/nodes_controller_test.rb
Normal file
|
@ -0,0 +1,552 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class NodesControllerTest < ActionController::TestCase
|
||||||
|
##
|
||||||
|
# test all routes which lead to this controller
|
||||||
|
def test_routes
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/node/create", :method => :put },
|
||||||
|
{ :controller => "api/nodes", :action => "create" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/node/1", :method => :get },
|
||||||
|
{ :controller => "api/nodes", :action => "show", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/node/1", :method => :put },
|
||||||
|
{ :controller => "api/nodes", :action => "update", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/node/1", :method => :delete },
|
||||||
|
{ :controller => "api/nodes", :action => "delete", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/nodes", :method => :get },
|
||||||
|
{ :controller => "api/nodes", :action => "index" }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_create
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_changeset = create(:changeset, :user => private_user)
|
||||||
|
user = create(:user)
|
||||||
|
changeset = create(:changeset, :user => user)
|
||||||
|
|
||||||
|
# create a node with random lat/lon
|
||||||
|
lat = rand(-50..50) + rand
|
||||||
|
lon = rand(-50..50) + rand
|
||||||
|
|
||||||
|
## First try with no auth
|
||||||
|
# create a minimal xml file
|
||||||
|
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
|
||||||
|
assert_difference("OldNode.count", 0) do
|
||||||
|
put :create, :body => xml
|
||||||
|
end
|
||||||
|
# hope for unauthorized
|
||||||
|
assert_response :unauthorized, "node upload did not return unauthorized status"
|
||||||
|
|
||||||
|
## Now try with the user which doesn't have their data public
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
# create a minimal xml file
|
||||||
|
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{private_changeset.id}'/></osm>"
|
||||||
|
assert_difference("Node.count", 0) do
|
||||||
|
put :create, :body => xml
|
||||||
|
end
|
||||||
|
# hope for success
|
||||||
|
assert_require_public_data "node create did not return forbidden status"
|
||||||
|
|
||||||
|
## Now try with the user that has the public data
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# create a minimal xml file
|
||||||
|
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# hope for success
|
||||||
|
assert_response :success, "node upload did not return success status"
|
||||||
|
|
||||||
|
# read id of created node and search for it
|
||||||
|
nodeid = @response.body
|
||||||
|
checknode = Node.find(nodeid)
|
||||||
|
assert_not_nil checknode, "uploaded node not found in data base after upload"
|
||||||
|
# compare values
|
||||||
|
assert_in_delta lat * 10000000, checknode.latitude, 1, "saved node does not match requested latitude"
|
||||||
|
assert_in_delta lon * 10000000, checknode.longitude, 1, "saved node does not match requested longitude"
|
||||||
|
assert_equal changeset.id, checknode.changeset_id, "saved node does not belong to changeset that it was created in"
|
||||||
|
assert_equal true, checknode.visible, "saved node is not visible"
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_create_invalid_xml
|
||||||
|
## Only test public user here, as test_create should cover what's the forbiddens
|
||||||
|
## that would occur here
|
||||||
|
|
||||||
|
user = create(:user)
|
||||||
|
changeset = create(:changeset, :user => user)
|
||||||
|
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
lat = 3.434
|
||||||
|
lon = 3.23
|
||||||
|
|
||||||
|
# test that the upload is rejected when xml is valid, but osm doc isn't
|
||||||
|
xml = "<create/>"
|
||||||
|
put :create, :body => xml
|
||||||
|
assert_response :bad_request, "node upload did not return bad_request status"
|
||||||
|
assert_equal "Cannot parse valid node from xml string <create/>. XML doesn't contain an osm/node element.", @response.body
|
||||||
|
|
||||||
|
# test that the upload is rejected when no lat is supplied
|
||||||
|
# create a minimal xml file
|
||||||
|
xml = "<osm><node lon='#{lon}' changeset='#{changeset.id}'/></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# hope for success
|
||||||
|
assert_response :bad_request, "node upload did not return bad_request status"
|
||||||
|
assert_equal "Cannot parse valid node from xml string <node lon=\"3.23\" changeset=\"#{changeset.id}\"/>. lat missing", @response.body
|
||||||
|
|
||||||
|
# test that the upload is rejected when no lon is supplied
|
||||||
|
# create a minimal xml file
|
||||||
|
xml = "<osm><node lat='#{lat}' changeset='#{changeset.id}'/></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# hope for success
|
||||||
|
assert_response :bad_request, "node upload did not return bad_request status"
|
||||||
|
assert_equal "Cannot parse valid node from xml string <node lat=\"3.434\" changeset=\"#{changeset.id}\"/>. lon missing", @response.body
|
||||||
|
|
||||||
|
# test that the upload is rejected when lat is non-numeric
|
||||||
|
# create a minimal xml file
|
||||||
|
xml = "<osm><node lat='abc' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# hope for success
|
||||||
|
assert_response :bad_request, "node upload did not return bad_request status"
|
||||||
|
assert_equal "Cannot parse valid node from xml string <node lat=\"abc\" lon=\"#{lon}\" changeset=\"#{changeset.id}\"/>. lat not a number", @response.body
|
||||||
|
|
||||||
|
# test that the upload is rejected when lon is non-numeric
|
||||||
|
# create a minimal xml file
|
||||||
|
xml = "<osm><node lat='#{lat}' lon='abc' changeset='#{changeset.id}'/></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# hope for success
|
||||||
|
assert_response :bad_request, "node upload did not return bad_request status"
|
||||||
|
assert_equal "Cannot parse valid node from xml string <node lat=\"#{lat}\" lon=\"abc\" changeset=\"#{changeset.id}\"/>. lon not a number", @response.body
|
||||||
|
|
||||||
|
# test that the upload is rejected when we have a tag which is too long
|
||||||
|
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'><tag k='foo' v='#{'x' * 256}'/></node></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
assert_response :bad_request, "node upload did not return bad_request status"
|
||||||
|
assert_equal ["NodeTag ", " v: is too long (maximum is 255 characters) (\"#{'x' * 256}\")"], @response.body.split(/[0-9]+,foo:/)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_show
|
||||||
|
# check that a visible node is returned properly
|
||||||
|
get :show, :params => { :id => create(:node).id }
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
# check that an deleted node is not returned
|
||||||
|
get :show, :params => { :id => create(:node, :deleted).id }
|
||||||
|
assert_response :gone
|
||||||
|
|
||||||
|
# check chat a non-existent node is not returned
|
||||||
|
get :show, :params => { :id => 0 }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
# this tests deletion restrictions - basic deletion is tested in the unit
|
||||||
|
# tests for node!
|
||||||
|
def test_delete
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_user_changeset = create(:changeset, :user => private_user)
|
||||||
|
private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
|
||||||
|
private_node = create(:node, :changeset => private_user_changeset)
|
||||||
|
private_deleted_node = create(:node, :deleted, :changeset => private_user_changeset)
|
||||||
|
|
||||||
|
## first try to delete node without auth
|
||||||
|
delete :delete, :params => { :id => private_node.id }
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
## now set auth for the non-data public user
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
# try to delete with an invalid (closed) changeset
|
||||||
|
xml = update_changeset(private_node.to_xml, private_user_closed_changeset.id)
|
||||||
|
delete :delete, :params => { :id => private_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data("non-public user shouldn't be able to delete node")
|
||||||
|
|
||||||
|
# try to delete with an invalid (non-existent) changeset
|
||||||
|
xml = update_changeset(private_node.to_xml, 0)
|
||||||
|
delete :delete, :params => { :id => private_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data("shouldn't be able to delete node, when user's data is private")
|
||||||
|
|
||||||
|
# valid delete now takes a payload
|
||||||
|
xml = private_node.to_xml
|
||||||
|
delete :delete, :params => { :id => private_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data("shouldn't be able to delete node when user's data isn't public'")
|
||||||
|
|
||||||
|
# this won't work since the node is already deleted
|
||||||
|
xml = private_deleted_node.to_xml
|
||||||
|
delete :delete, :params => { :id => private_deleted_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data
|
||||||
|
|
||||||
|
# this won't work since the node never existed
|
||||||
|
delete :delete, :params => { :id => 0 }
|
||||||
|
assert_require_public_data
|
||||||
|
|
||||||
|
## these test whether nodes which are in-use can be deleted:
|
||||||
|
# in a way...
|
||||||
|
private_used_node = create(:node, :changeset => private_user_changeset)
|
||||||
|
create(:way_node, :node => private_used_node)
|
||||||
|
|
||||||
|
xml = private_used_node.to_xml
|
||||||
|
delete :delete, :params => { :id => private_used_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "shouldn't be able to delete a node used in a way (#{@response.body})"
|
||||||
|
|
||||||
|
# in a relation...
|
||||||
|
private_used_node2 = create(:node, :changeset => private_user_changeset)
|
||||||
|
create(:relation_member, :member => private_used_node2)
|
||||||
|
|
||||||
|
xml = private_used_node2.to_xml
|
||||||
|
delete :delete, :params => { :id => private_used_node2.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "shouldn't be able to delete a node used in a relation (#{@response.body})"
|
||||||
|
|
||||||
|
## now setup for the public data user
|
||||||
|
user = create(:user, :data_public => true)
|
||||||
|
changeset = create(:changeset, :user => user)
|
||||||
|
closed_changeset = create(:changeset, :closed, :user => user)
|
||||||
|
node = create(:node, :changeset => changeset)
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# try to delete with an invalid (closed) changeset
|
||||||
|
xml = update_changeset(node.to_xml, closed_changeset.id)
|
||||||
|
delete :delete, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict
|
||||||
|
|
||||||
|
# try to delete with an invalid (non-existent) changeset
|
||||||
|
xml = update_changeset(node.to_xml, 0)
|
||||||
|
delete :delete, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict
|
||||||
|
|
||||||
|
# try to delete a node with a different ID
|
||||||
|
other_node = create(:node)
|
||||||
|
xml = other_node.to_xml
|
||||||
|
delete :delete, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :bad_request,
|
||||||
|
"should not be able to delete a node with a different ID from the XML"
|
||||||
|
|
||||||
|
# try to delete a node rubbish in the payloads
|
||||||
|
xml = "<delete/>"
|
||||||
|
delete :delete, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :bad_request,
|
||||||
|
"should not be able to delete a node without a valid XML payload"
|
||||||
|
|
||||||
|
# valid delete now takes a payload
|
||||||
|
xml = node.to_xml
|
||||||
|
delete :delete, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
# valid delete should return the new version number, which should
|
||||||
|
# be greater than the old version number
|
||||||
|
assert @response.body.to_i > node.version,
|
||||||
|
"delete request should return a new version number for node"
|
||||||
|
|
||||||
|
# deleting the same node twice doesn't work
|
||||||
|
xml = node.to_xml
|
||||||
|
delete :delete, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :gone
|
||||||
|
|
||||||
|
# this won't work since the node never existed
|
||||||
|
delete :delete, :params => { :id => 0 }
|
||||||
|
assert_response :not_found
|
||||||
|
|
||||||
|
## these test whether nodes which are in-use can be deleted:
|
||||||
|
# in a way...
|
||||||
|
used_node = create(:node, :changeset => create(:changeset, :user => user))
|
||||||
|
way_node = create(:way_node, :node => used_node)
|
||||||
|
way_node2 = create(:way_node, :node => used_node)
|
||||||
|
|
||||||
|
xml = used_node.to_xml
|
||||||
|
delete :delete, :params => { :id => used_node.id }, :body => xml.to_s
|
||||||
|
assert_response :precondition_failed,
|
||||||
|
"shouldn't be able to delete a node used in a way (#{@response.body})"
|
||||||
|
assert_equal "Precondition failed: Node #{used_node.id} is still used by ways #{way_node.way.id},#{way_node2.way.id}.", @response.body
|
||||||
|
|
||||||
|
# in a relation...
|
||||||
|
used_node2 = create(:node, :changeset => create(:changeset, :user => user))
|
||||||
|
relation_member = create(:relation_member, :member => used_node2)
|
||||||
|
relation_member2 = create(:relation_member, :member => used_node2)
|
||||||
|
|
||||||
|
xml = used_node2.to_xml
|
||||||
|
delete :delete, :params => { :id => used_node2.id }, :body => xml.to_s
|
||||||
|
assert_response :precondition_failed,
|
||||||
|
"shouldn't be able to delete a node used in a relation (#{@response.body})"
|
||||||
|
assert_equal "Precondition failed: Node #{used_node2.id} is still used by relations #{relation_member.relation.id},#{relation_member2.relation.id}.", @response.body
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# tests whether the API works and prevents incorrect use while trying
|
||||||
|
# to update nodes.
|
||||||
|
def test_update
|
||||||
|
## First test with no user credentials
|
||||||
|
# try and update a node without authorisation
|
||||||
|
# first try to delete node without auth
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_node = create(:node, :changeset => create(:changeset, :user => private_user))
|
||||||
|
user = create(:user)
|
||||||
|
node = create(:node, :changeset => create(:changeset, :user => user))
|
||||||
|
|
||||||
|
xml = node.to_xml
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
## Second test with the private user
|
||||||
|
|
||||||
|
# setup auth
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
## trying to break changesets
|
||||||
|
|
||||||
|
# try and update in someone else's changeset
|
||||||
|
xml = update_changeset(private_node.to_xml,
|
||||||
|
create(:changeset).id)
|
||||||
|
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "update with other user's changeset should be forbidden when data isn't public"
|
||||||
|
|
||||||
|
# try and update in a closed changeset
|
||||||
|
xml = update_changeset(private_node.to_xml,
|
||||||
|
create(:changeset, :closed, :user => private_user).id)
|
||||||
|
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
|
||||||
|
|
||||||
|
# try and update in a non-existant changeset
|
||||||
|
xml = update_changeset(private_node.to_xml, 0)
|
||||||
|
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "update with changeset=0 should be forbidden, when data isn't public"
|
||||||
|
|
||||||
|
## try and submit invalid updates
|
||||||
|
xml = xml_attr_rewrite(private_node.to_xml, "lat", 91.0)
|
||||||
|
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "node at lat=91 should be forbidden, when data isn't public"
|
||||||
|
|
||||||
|
xml = xml_attr_rewrite(private_node.to_xml, "lat", -91.0)
|
||||||
|
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "node at lat=-91 should be forbidden, when data isn't public"
|
||||||
|
|
||||||
|
xml = xml_attr_rewrite(private_node.to_xml, "lon", 181.0)
|
||||||
|
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "node at lon=181 should be forbidden, when data isn't public"
|
||||||
|
|
||||||
|
xml = xml_attr_rewrite(private_node.to_xml, "lon", -181.0)
|
||||||
|
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "node at lon=-181 should be forbidden, when data isn't public"
|
||||||
|
|
||||||
|
## finally, produce a good request which still won't work
|
||||||
|
xml = private_node.to_xml
|
||||||
|
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "should have failed with a forbidden when data isn't public"
|
||||||
|
|
||||||
|
## Finally test with the public user
|
||||||
|
|
||||||
|
# try and update a node without authorisation
|
||||||
|
# first try to update node without auth
|
||||||
|
xml = node.to_xml
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# setup auth
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
## trying to break changesets
|
||||||
|
|
||||||
|
# try and update in someone else's changeset
|
||||||
|
xml = update_changeset(node.to_xml,
|
||||||
|
create(:changeset).id)
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict, "update with other user's changeset should be rejected"
|
||||||
|
|
||||||
|
# try and update in a closed changeset
|
||||||
|
xml = update_changeset(node.to_xml,
|
||||||
|
create(:changeset, :closed, :user => user).id)
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict, "update with closed changeset should be rejected"
|
||||||
|
|
||||||
|
# try and update in a non-existant changeset
|
||||||
|
xml = update_changeset(node.to_xml, 0)
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict, "update with changeset=0 should be rejected"
|
||||||
|
|
||||||
|
## try and submit invalid updates
|
||||||
|
xml = xml_attr_rewrite(node.to_xml, "lat", 91.0)
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :bad_request, "node at lat=91 should be rejected"
|
||||||
|
|
||||||
|
xml = xml_attr_rewrite(node.to_xml, "lat", -91.0)
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :bad_request, "node at lat=-91 should be rejected"
|
||||||
|
|
||||||
|
xml = xml_attr_rewrite(node.to_xml, "lon", 181.0)
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :bad_request, "node at lon=181 should be rejected"
|
||||||
|
|
||||||
|
xml = xml_attr_rewrite(node.to_xml, "lon", -181.0)
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :bad_request, "node at lon=-181 should be rejected"
|
||||||
|
|
||||||
|
## next, attack the versioning
|
||||||
|
current_node_version = node.version
|
||||||
|
|
||||||
|
# try and submit a version behind
|
||||||
|
xml = xml_attr_rewrite(node.to_xml,
|
||||||
|
"version", current_node_version - 1)
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict, "should have failed on old version number"
|
||||||
|
|
||||||
|
# try and submit a version ahead
|
||||||
|
xml = xml_attr_rewrite(node.to_xml,
|
||||||
|
"version", current_node_version + 1)
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict, "should have failed on skipped version number"
|
||||||
|
|
||||||
|
# try and submit total crap in the version field
|
||||||
|
xml = xml_attr_rewrite(node.to_xml,
|
||||||
|
"version", "p1r4t3s!")
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict,
|
||||||
|
"should not be able to put 'p1r4at3s!' in the version field"
|
||||||
|
|
||||||
|
## try an update with the wrong ID
|
||||||
|
xml = create(:node).to_xml
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :bad_request,
|
||||||
|
"should not be able to update a node with a different ID from the XML"
|
||||||
|
|
||||||
|
## try an update with a minimal valid XML doc which isn't a well-formed OSM doc.
|
||||||
|
xml = "<update/>"
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :bad_request,
|
||||||
|
"should not be able to update a node with non-OSM XML doc."
|
||||||
|
|
||||||
|
## finally, produce a good request which should work
|
||||||
|
xml = node.to_xml
|
||||||
|
put :update, :params => { :id => node.id }, :body => xml.to_s
|
||||||
|
assert_response :success, "a valid update request failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test fetching multiple nodes
|
||||||
|
def test_index
|
||||||
|
node1 = create(:node)
|
||||||
|
node2 = create(:node, :deleted)
|
||||||
|
node3 = create(:node)
|
||||||
|
node4 = create(:node, :with_history, :version => 2)
|
||||||
|
node5 = create(:node, :deleted, :with_history, :version => 2)
|
||||||
|
|
||||||
|
# check error when no parameter provided
|
||||||
|
get :index
|
||||||
|
assert_response :bad_request
|
||||||
|
|
||||||
|
# check error when no parameter value provided
|
||||||
|
get :index, :params => { :nodes => "" }
|
||||||
|
assert_response :bad_request
|
||||||
|
|
||||||
|
# test a working call
|
||||||
|
get :index, :params => { :nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id}" }
|
||||||
|
assert_response :success
|
||||||
|
assert_select "osm" do
|
||||||
|
assert_select "node", :count => 5
|
||||||
|
assert_select "node[id='#{node1.id}'][visible='true']", :count => 1
|
||||||
|
assert_select "node[id='#{node2.id}'][visible='false']", :count => 1
|
||||||
|
assert_select "node[id='#{node3.id}'][visible='true']", :count => 1
|
||||||
|
assert_select "node[id='#{node4.id}'][visible='true']", :count => 1
|
||||||
|
assert_select "node[id='#{node5.id}'][visible='false']", :count => 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# check error when a non-existent node is included
|
||||||
|
get :index, :params => { :nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id},0" }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test adding tags to a node
|
||||||
|
def test_duplicate_tags
|
||||||
|
existing_tag = create(:node_tag)
|
||||||
|
assert_equal true, existing_tag.node.changeset.user.data_public
|
||||||
|
# setup auth
|
||||||
|
basic_authorization existing_tag.node.changeset.user.email, "test"
|
||||||
|
|
||||||
|
# add an identical tag to the node
|
||||||
|
tag_xml = XML::Node.new("tag")
|
||||||
|
tag_xml["k"] = existing_tag.k
|
||||||
|
tag_xml["v"] = existing_tag.v
|
||||||
|
|
||||||
|
# add the tag into the existing xml
|
||||||
|
node_xml = existing_tag.node.to_xml
|
||||||
|
node_xml.find("//osm/node").first << tag_xml
|
||||||
|
|
||||||
|
# try and upload it
|
||||||
|
put :update, :params => { :id => existing_tag.node.id }, :body => node_xml.to_s
|
||||||
|
assert_response :bad_request,
|
||||||
|
"adding duplicate tags to a node should fail with 'bad request'"
|
||||||
|
assert_equal "Element node/#{existing_tag.node.id} has duplicate tags with key #{existing_tag.k}", @response.body
|
||||||
|
end
|
||||||
|
|
||||||
|
# test whether string injection is possible
|
||||||
|
def test_string_injection
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_changeset = create(:changeset, :user => private_user)
|
||||||
|
user = create(:user)
|
||||||
|
changeset = create(:changeset, :user => user)
|
||||||
|
|
||||||
|
## First try with the non-data public user
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
# try and put something into a string that the API might
|
||||||
|
# use unquoted and therefore allow code injection...
|
||||||
|
xml = "<osm><node lat='0' lon='0' changeset='#{private_changeset.id}'>" \
|
||||||
|
'<tag k="#{@user.inspect}" v="0"/>' \
|
||||||
|
"</node></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
assert_require_public_data "Shouldn't be able to create with non-public user"
|
||||||
|
|
||||||
|
## Then try with the public data user
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# try and put something into a string that the API might
|
||||||
|
# use unquoted and therefore allow code injection...
|
||||||
|
xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'>" \
|
||||||
|
'<tag k="#{@user.inspect}" v="0"/>' \
|
||||||
|
"</node></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
assert_response :success
|
||||||
|
nodeid = @response.body
|
||||||
|
|
||||||
|
# find the node in the database
|
||||||
|
checknode = Node.find(nodeid)
|
||||||
|
assert_not_nil checknode, "node not found in data base after upload"
|
||||||
|
|
||||||
|
# and grab it using the api
|
||||||
|
get :show, :params => { :id => nodeid }
|
||||||
|
assert_response :success
|
||||||
|
apinode = Node.from_xml(@response.body)
|
||||||
|
assert_not_nil apinode, "downloaded node is nil, but shouldn't be"
|
||||||
|
|
||||||
|
# check the tags are not corrupted
|
||||||
|
assert_equal checknode.tags, apinode.tags
|
||||||
|
assert apinode.tags.include?("\#{@user.inspect}")
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# update the changeset_id of a node element
|
||||||
|
def update_changeset(xml, changeset_id)
|
||||||
|
xml_attr_rewrite(xml, "changeset", changeset_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# update an attribute in the node element
|
||||||
|
def xml_attr_rewrite(xml, name, value)
|
||||||
|
xml.find("//osm/node").first[name] = value.to_s
|
||||||
|
xml
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# parse some xml
|
||||||
|
def xml_parse(xml)
|
||||||
|
parser = XML::Parser.string(xml)
|
||||||
|
parser.parse
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
1090
test/controllers/api/notes_controller_test.rb
Normal file
1090
test/controllers/api/notes_controller_test.rb
Normal file
File diff suppressed because it is too large
Load diff
430
test/controllers/api/old_nodes_controller_test.rb
Normal file
430
test/controllers/api/old_nodes_controller_test.rb
Normal file
|
@ -0,0 +1,430 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class OldNodesControllerTest < ActionController::TestCase
|
||||||
|
#
|
||||||
|
# TODO: test history
|
||||||
|
#
|
||||||
|
|
||||||
|
##
|
||||||
|
# test all routes which lead to this controller
|
||||||
|
def test_routes
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/node/1/history", :method => :get },
|
||||||
|
{ :controller => "api/old_nodes", :action => "history", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/node/1/2", :method => :get },
|
||||||
|
{ :controller => "api/old_nodes", :action => "version", :id => "1", :version => "2" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/node/1/2/redact", :method => :post },
|
||||||
|
{ :controller => "api/old_nodes", :action => "redact", :id => "1", :version => "2" }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the version call by submitting several revisions of a new node
|
||||||
|
# to the API and ensuring that later calls to version return the
|
||||||
|
# matching versions of the object.
|
||||||
|
#
|
||||||
|
##
|
||||||
|
# FIXME: Move this test to being an integration test since it spans multiple controllers
|
||||||
|
def test_version
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_node = create(:node, :with_history, :version => 4, :changeset => create(:changeset, :user => private_user))
|
||||||
|
user = create(:user)
|
||||||
|
node = create(:node, :with_history, :version => 4, :changeset => create(:changeset, :user => user))
|
||||||
|
create_list(:node_tag, 2, :node => node)
|
||||||
|
# Ensure that the current tags are propagated to the history too
|
||||||
|
propagate_tags(node, node.old_nodes.last)
|
||||||
|
|
||||||
|
## First try this with a non-public user
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
# setup a simple XML node
|
||||||
|
xml_doc = private_node.to_xml
|
||||||
|
xml_node = xml_doc.find("//osm/node").first
|
||||||
|
nodeid = private_node.id
|
||||||
|
|
||||||
|
# keep a hash of the versions => string, as we'll need something
|
||||||
|
# to test against later
|
||||||
|
versions = {}
|
||||||
|
|
||||||
|
# save a version for later checking
|
||||||
|
versions[xml_node["version"]] = xml_doc.to_s
|
||||||
|
|
||||||
|
# randomly move the node about
|
||||||
|
3.times do
|
||||||
|
# move the node somewhere else
|
||||||
|
xml_node["lat"] = precision(rand * 180 - 90).to_s
|
||||||
|
xml_node["lon"] = precision(rand * 360 - 180).to_s
|
||||||
|
with_controller(NodesController.new) do
|
||||||
|
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
|
||||||
|
assert_response :forbidden, "Should have rejected node update"
|
||||||
|
xml_node["version"] = @response.body.to_s
|
||||||
|
end
|
||||||
|
# save a version for later checking
|
||||||
|
versions[xml_node["version"]] = xml_doc.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# add a bunch of random tags
|
||||||
|
3.times do
|
||||||
|
xml_tag = XML::Node.new("tag")
|
||||||
|
xml_tag["k"] = random_string
|
||||||
|
xml_tag["v"] = random_string
|
||||||
|
xml_node << xml_tag
|
||||||
|
with_controller(NodesController.new) do
|
||||||
|
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
|
||||||
|
assert_response :forbidden,
|
||||||
|
"should have rejected node #{nodeid} (#{@response.body}) with forbidden"
|
||||||
|
xml_node["version"] = @response.body.to_s
|
||||||
|
end
|
||||||
|
# save a version for later checking
|
||||||
|
versions[xml_node["version"]] = xml_doc.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# probably should check that they didn't get written to the database
|
||||||
|
|
||||||
|
## Now do it with the public user
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# setup a simple XML node
|
||||||
|
|
||||||
|
xml_doc = node.to_xml
|
||||||
|
xml_node = xml_doc.find("//osm/node").first
|
||||||
|
nodeid = node.id
|
||||||
|
|
||||||
|
# keep a hash of the versions => string, as we'll need something
|
||||||
|
# to test against later
|
||||||
|
versions = {}
|
||||||
|
|
||||||
|
# save a version for later checking
|
||||||
|
versions[xml_node["version"]] = xml_doc.to_s
|
||||||
|
|
||||||
|
# randomly move the node about
|
||||||
|
3.times do
|
||||||
|
# move the node somewhere else
|
||||||
|
xml_node["lat"] = precision(rand * 180 - 90).to_s
|
||||||
|
xml_node["lon"] = precision(rand * 360 - 180).to_s
|
||||||
|
with_controller(NodesController.new) do
|
||||||
|
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
|
||||||
|
assert_response :success
|
||||||
|
xml_node["version"] = @response.body.to_s
|
||||||
|
end
|
||||||
|
# save a version for later checking
|
||||||
|
versions[xml_node["version"]] = xml_doc.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# add a bunch of random tags
|
||||||
|
3.times do
|
||||||
|
xml_tag = XML::Node.new("tag")
|
||||||
|
xml_tag["k"] = random_string
|
||||||
|
xml_tag["v"] = random_string
|
||||||
|
xml_node << xml_tag
|
||||||
|
with_controller(NodesController.new) do
|
||||||
|
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
|
||||||
|
assert_response :success,
|
||||||
|
"couldn't update node #{nodeid} (#{@response.body})"
|
||||||
|
xml_node["version"] = @response.body.to_s
|
||||||
|
end
|
||||||
|
# save a version for later checking
|
||||||
|
versions[xml_node["version"]] = xml_doc.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# check all the versions
|
||||||
|
versions.each_key do |key|
|
||||||
|
get :version, :params => { :id => nodeid, :version => key.to_i }
|
||||||
|
|
||||||
|
assert_response :success,
|
||||||
|
"couldn't get version #{key.to_i} of node #{nodeid}"
|
||||||
|
|
||||||
|
check_node = Node.from_xml(versions[key])
|
||||||
|
api_node = Node.from_xml(@response.body.to_s)
|
||||||
|
|
||||||
|
assert_nodes_are_equal check_node, api_node
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_not_found_version
|
||||||
|
check_not_found_id_version(70000, 312344)
|
||||||
|
check_not_found_id_version(-1, -13)
|
||||||
|
check_not_found_id_version(create(:node).id, 24354)
|
||||||
|
check_not_found_id_version(24356, create(:node).version)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_not_found_id_version(id, version)
|
||||||
|
get :version, :params => { :id => id, :version => version }
|
||||||
|
assert_response :not_found
|
||||||
|
rescue ActionController::UrlGenerationError => ex
|
||||||
|
assert_match(/No route matches/, ex.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Test that getting the current version is identical to picking
|
||||||
|
# that version with the version URI call.
|
||||||
|
def test_current_version
|
||||||
|
node = create(:node, :with_history)
|
||||||
|
used_node = create(:node, :with_history)
|
||||||
|
create(:way_node, :node => used_node)
|
||||||
|
node_used_by_relationship = create(:node, :with_history)
|
||||||
|
create(:relation_member, :member => node_used_by_relationship)
|
||||||
|
node_with_versions = create(:node, :with_history, :version => 4)
|
||||||
|
|
||||||
|
create(:node_tag, :node => node)
|
||||||
|
create(:node_tag, :node => used_node)
|
||||||
|
create(:node_tag, :node => node_used_by_relationship)
|
||||||
|
create(:node_tag, :node => node_with_versions)
|
||||||
|
propagate_tags(node, node.old_nodes.last)
|
||||||
|
propagate_tags(used_node, used_node.old_nodes.last)
|
||||||
|
propagate_tags(node_used_by_relationship, node_used_by_relationship.old_nodes.last)
|
||||||
|
propagate_tags(node_with_versions, node_with_versions.old_nodes.last)
|
||||||
|
|
||||||
|
check_current_version(node)
|
||||||
|
check_current_version(used_node)
|
||||||
|
check_current_version(node_used_by_relationship)
|
||||||
|
check_current_version(node_with_versions)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the redaction of an old version of a node, while not being
|
||||||
|
# authorised.
|
||||||
|
def test_redact_node_unauthorised
|
||||||
|
node = create(:node, :with_history, :version => 4)
|
||||||
|
node_v3 = node.old_nodes.find_by(:version => 3)
|
||||||
|
|
||||||
|
do_redact_node(node_v3,
|
||||||
|
create(:redaction))
|
||||||
|
assert_response :unauthorized, "should need to be authenticated to redact."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the redaction of an old version of a node, while being
|
||||||
|
# authorised as a normal user.
|
||||||
|
def test_redact_node_normal_user
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
node = create(:node, :with_history, :version => 4)
|
||||||
|
node_v3 = node.old_nodes.find_by(:version => 3)
|
||||||
|
|
||||||
|
do_redact_node(node_v3,
|
||||||
|
create(:redaction))
|
||||||
|
assert_response :forbidden, "should need to be moderator to redact."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test that, even as moderator, the current version of a node
|
||||||
|
# can't be redacted.
|
||||||
|
def test_redact_node_current_version
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
node = create(:node, :with_history, :version => 4)
|
||||||
|
node_v4 = node.old_nodes.find_by(:version => 4)
|
||||||
|
|
||||||
|
do_redact_node(node_v4,
|
||||||
|
create(:redaction))
|
||||||
|
assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test that redacted nodes aren't visible, regardless of
|
||||||
|
# authorisation except as moderator...
|
||||||
|
def test_version_redacted
|
||||||
|
node = create(:node, :with_history, :version => 2)
|
||||||
|
node_v1 = node.old_nodes.find_by(:version => 1)
|
||||||
|
node_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
||||||
|
assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
|
||||||
|
|
||||||
|
# not even to a logged-in user
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
||||||
|
assert_response :forbidden, "Redacted node shouldn't be visible via the version API, even when logged in."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test that redacted nodes aren't visible in the history
|
||||||
|
def test_history_redacted
|
||||||
|
node = create(:node, :with_history, :version => 2)
|
||||||
|
node_v1 = node.old_nodes.find_by(:version => 1)
|
||||||
|
node_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
get :history, :params => { :id => node_v1.node_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 0, "redacted node #{node_v1.node_id} version #{node_v1.version} shouldn't be present in the history."
|
||||||
|
|
||||||
|
# not even to a logged-in user
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
get :history, :params => { :id => node_v1.node_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 0, "redacted node #{node_v1.node_id} version #{node_v1.version} shouldn't be present in the history, even when logged in."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the redaction of an old version of a node, while being
|
||||||
|
# authorised as a moderator.
|
||||||
|
def test_redact_node_moderator
|
||||||
|
node = create(:node, :with_history, :version => 4)
|
||||||
|
node_v3 = node.old_nodes.find_by(:version => 3)
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
do_redact_node(node_v3, create(:redaction))
|
||||||
|
assert_response :success, "should be OK to redact old version as moderator."
|
||||||
|
|
||||||
|
# check moderator can still see the redacted data, when passing
|
||||||
|
# the appropriate flag
|
||||||
|
get :version, :params => { :id => node_v3.node_id, :version => node_v3.version }
|
||||||
|
assert_response :forbidden, "After redaction, node should be gone for moderator, when flag not passed."
|
||||||
|
get :version, :params => { :id => node_v3.node_id, :version => node_v3.version, :show_redactions => "true" }
|
||||||
|
assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
|
||||||
|
|
||||||
|
# and when accessed via history
|
||||||
|
get :history, :params => { :id => node_v3.node_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 0, "node #{node_v3.node_id} version #{node_v3.version} should not be present in the history for moderators when not passing flag."
|
||||||
|
get :history, :params => { :id => node_v3.node_id, :show_redactions => "true" }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 1, "node #{node_v3.node_id} version #{node_v3.version} should still be present in the history for moderators when passing flag."
|
||||||
|
end
|
||||||
|
|
||||||
|
# testing that if the moderator drops auth, he can't see the
|
||||||
|
# redacted stuff any more.
|
||||||
|
def test_redact_node_is_redacted
|
||||||
|
node = create(:node, :with_history, :version => 4)
|
||||||
|
node_v3 = node.old_nodes.find_by(:version => 3)
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
do_redact_node(node_v3, create(:redaction))
|
||||||
|
assert_response :success, "should be OK to redact old version as moderator."
|
||||||
|
|
||||||
|
# re-auth as non-moderator
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
# check can't see the redacted data
|
||||||
|
get :version, :params => { :id => node_v3.node_id, :version => node_v3.version }
|
||||||
|
assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
|
||||||
|
|
||||||
|
# and when accessed via history
|
||||||
|
get :history, :params => { :id => node_v3.node_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 0, "redacted node #{node_v3.node_id} version #{node_v3.version} shouldn't be present in the history."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the unredaction of an old version of a node, while not being
|
||||||
|
# authorised.
|
||||||
|
def test_unredact_node_unauthorised
|
||||||
|
node = create(:node, :with_history, :version => 2)
|
||||||
|
node_v1 = node.old_nodes.find_by(:version => 1)
|
||||||
|
node_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
post :redact, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
||||||
|
assert_response :unauthorized, "should need to be authenticated to unredact."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the unredaction of an old version of a node, while being
|
||||||
|
# authorised as a normal user.
|
||||||
|
def test_unredact_node_normal_user
|
||||||
|
user = create(:user)
|
||||||
|
node = create(:node, :with_history, :version => 2)
|
||||||
|
node_v1 = node.old_nodes.find_by(:version => 1)
|
||||||
|
node_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
post :redact, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
||||||
|
assert_response :forbidden, "should need to be moderator to unredact."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the unredaction of an old version of a node, while being
|
||||||
|
# authorised as a moderator.
|
||||||
|
def test_unredact_node_moderator
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
node = create(:node, :with_history, :version => 2)
|
||||||
|
node_v1 = node.old_nodes.find_by(:version => 1)
|
||||||
|
node_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
basic_authorization moderator_user.email, "test"
|
||||||
|
|
||||||
|
post :redact, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
||||||
|
assert_response :success, "should be OK to unredact old version as moderator."
|
||||||
|
|
||||||
|
# check moderator can now see the redacted data, when not
|
||||||
|
# passing the aspecial flag
|
||||||
|
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
||||||
|
assert_response :success, "After unredaction, node should not be gone for moderator."
|
||||||
|
|
||||||
|
# and when accessed via history
|
||||||
|
get :history, :params => { :id => node_v1.node_id }
|
||||||
|
assert_response :success, "Unredaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 1, "node #{node_v1.node_id} version #{node_v1.version} should now be present in the history for moderators without passing flag."
|
||||||
|
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
# check normal user can now see the redacted data
|
||||||
|
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
||||||
|
assert_response :success, "After unredaction, node should be visible to normal users."
|
||||||
|
|
||||||
|
# and when accessed via history
|
||||||
|
get :history, :params => { :id => node_v1.node_id }
|
||||||
|
assert_response :success, "Unredaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 1, "node #{node_v1.node_id} version #{node_v1.version} should now be present in the history for normal users without passing flag."
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def do_redact_node(node, redaction)
|
||||||
|
get :version, :params => { :id => node.node_id, :version => node.version }
|
||||||
|
assert_response :success, "should be able to get version #{node.version} of node #{node.node_id}."
|
||||||
|
|
||||||
|
# now redact it
|
||||||
|
post :redact, :params => { :id => node.node_id, :version => node.version, :redaction => redaction.id }
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_current_version(node_id)
|
||||||
|
# get the current version of the node
|
||||||
|
current_node = with_controller(NodesController.new) do
|
||||||
|
get :show, :params => { :id => node_id }
|
||||||
|
assert_response :success, "cant get current node #{node_id}"
|
||||||
|
Node.from_xml(@response.body)
|
||||||
|
end
|
||||||
|
assert_not_nil current_node, "getting node #{node_id} returned nil"
|
||||||
|
|
||||||
|
# get the "old" version of the node from the old_node interface
|
||||||
|
get :version, :params => { :id => node_id, :version => current_node.version }
|
||||||
|
assert_response :success, "cant get old node #{node_id}, v#{current_node.version}"
|
||||||
|
old_node = Node.from_xml(@response.body)
|
||||||
|
|
||||||
|
# check the nodes are the same
|
||||||
|
assert_nodes_are_equal current_node, old_node
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# returns a 16 character long string with some nasty characters in it.
|
||||||
|
# this ought to stress-test the tag handling as well as the versioning.
|
||||||
|
def random_string
|
||||||
|
letters = [["!", '"', "$", "&", ";", "@"],
|
||||||
|
("a".."z").to_a,
|
||||||
|
("A".."Z").to_a,
|
||||||
|
("0".."9").to_a].flatten
|
||||||
|
(1..16).map { |_i| letters[rand(letters.length)] }.join
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# truncate a floating point number to the scale that it is stored in
|
||||||
|
# the database. otherwise rounding errors can produce failing unit
|
||||||
|
# tests when they shouldn't.
|
||||||
|
def precision(f)
|
||||||
|
(f * GeoRecord::SCALE).round.to_f / GeoRecord::SCALE
|
||||||
|
end
|
||||||
|
|
||||||
|
def propagate_tags(node, old_node)
|
||||||
|
node.tags.each do |k, v|
|
||||||
|
create(:old_node_tag, :old_node => old_node, :k => k, :v => v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
274
test/controllers/api/old_relations_controller_test.rb
Normal file
274
test/controllers/api/old_relations_controller_test.rb
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class OldRelationsControllerTest < ActionController::TestCase
|
||||||
|
##
|
||||||
|
# test all routes which lead to this controller
|
||||||
|
def test_routes
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/relation/1/history", :method => :get },
|
||||||
|
{ :controller => "api/old_relations", :action => "history", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/relation/1/2", :method => :get },
|
||||||
|
{ :controller => "api/old_relations", :action => "version", :id => "1", :version => "2" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/relation/1/2/redact", :method => :post },
|
||||||
|
{ :controller => "api/old_relations", :action => "redact", :id => "1", :version => "2" }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# -------------------------------------
|
||||||
|
# Test reading old relations.
|
||||||
|
# -------------------------------------
|
||||||
|
def test_history
|
||||||
|
# check that a visible relations is returned properly
|
||||||
|
get :history, :params => { :id => create(:relation, :with_history).id }
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
# check chat a non-existent relations is not returned
|
||||||
|
get :history, :params => { :id => 0 }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the redaction of an old version of a relation, while not being
|
||||||
|
# authorised.
|
||||||
|
def test_redact_relation_unauthorised
|
||||||
|
relation = create(:relation, :with_history, :version => 4)
|
||||||
|
relation_v3 = relation.old_relations.find_by(:version => 3)
|
||||||
|
|
||||||
|
do_redact_relation(relation_v3, create(:redaction))
|
||||||
|
assert_response :unauthorized, "should need to be authenticated to redact."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the redaction of an old version of a relation, while being
|
||||||
|
# authorised as a normal user.
|
||||||
|
def test_redact_relation_normal_user
|
||||||
|
relation = create(:relation, :with_history, :version => 4)
|
||||||
|
relation_v3 = relation.old_relations.find_by(:version => 3)
|
||||||
|
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
do_redact_relation(relation_v3, create(:redaction))
|
||||||
|
assert_response :forbidden, "should need to be moderator to redact."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test that, even as moderator, the current version of a relation
|
||||||
|
# can't be redacted.
|
||||||
|
def test_redact_relation_current_version
|
||||||
|
relation = create(:relation, :with_history, :version => 4)
|
||||||
|
relation_latest = relation.old_relations.last
|
||||||
|
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
do_redact_relation(relation_latest, create(:redaction))
|
||||||
|
assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test that redacted relations aren't visible, regardless of
|
||||||
|
# authorisation except as moderator...
|
||||||
|
def test_version_redacted
|
||||||
|
relation = create(:relation, :with_history, :version => 2)
|
||||||
|
relation_v1 = relation.old_relations.find_by(:version => 1)
|
||||||
|
relation_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
||||||
|
assert_response :forbidden, "Redacted relation shouldn't be visible via the version API."
|
||||||
|
|
||||||
|
# not even to a logged-in user
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
||||||
|
assert_response :forbidden, "Redacted relation shouldn't be visible via the version API, even when logged in."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test that redacted relations aren't visible in the history
|
||||||
|
def test_history_redacted
|
||||||
|
relation = create(:relation, :with_history, :version => 2)
|
||||||
|
relation_v1 = relation.old_relations.find_by(:version => 1)
|
||||||
|
relation_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
get :history, :params => { :id => relation_v1.relation_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 0, "redacted relation #{relation_v1.relation_id} version #{relation_v1.version} shouldn't be present in the history."
|
||||||
|
|
||||||
|
# not even to a logged-in user
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
||||||
|
get :history, :params => { :id => relation_v1.relation_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 0, "redacted relation #{relation_v1.relation_id} version #{relation_v1.version} shouldn't be present in the history, even when logged in."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the redaction of an old version of a relation, while being
|
||||||
|
# authorised as a moderator.
|
||||||
|
def test_redact_relation_moderator
|
||||||
|
relation = create(:relation, :with_history, :version => 4)
|
||||||
|
relation_v3 = relation.old_relations.find_by(:version => 3)
|
||||||
|
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
do_redact_relation(relation_v3, create(:redaction))
|
||||||
|
assert_response :success, "should be OK to redact old version as moderator."
|
||||||
|
|
||||||
|
# check moderator can still see the redacted data, when passing
|
||||||
|
# the appropriate flag
|
||||||
|
get :version, :params => { :id => relation_v3.relation_id, :version => relation_v3.version }
|
||||||
|
assert_response :forbidden, "After redaction, relation should be gone for moderator, when flag not passed."
|
||||||
|
get :version, :params => { :id => relation_v3.relation_id, :version => relation_v3.version, :show_redactions => "true" }
|
||||||
|
assert_response :success, "After redaction, relation should not be gone for moderator, when flag passed."
|
||||||
|
|
||||||
|
# and when accessed via history
|
||||||
|
get :history, :params => { :id => relation_v3.relation_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm relation[id='#{relation_v3.relation_id}'][version='#{relation_v3.version}']", 0, "relation #{relation_v3.relation_id} version #{relation_v3.version} should not be present in the history for moderators when not passing flag."
|
||||||
|
get :history, :params => { :id => relation_v3.relation_id, :show_redactions => "true" }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm relation[id='#{relation_v3.relation_id}'][version='#{relation_v3.version}']", 1, "relation #{relation_v3.relation_id} version #{relation_v3.version} should still be present in the history for moderators when passing flag."
|
||||||
|
end
|
||||||
|
|
||||||
|
# testing that if the moderator drops auth, he can't see the
|
||||||
|
# redacted stuff any more.
|
||||||
|
def test_redact_relation_is_redacted
|
||||||
|
relation = create(:relation, :with_history, :version => 4)
|
||||||
|
relation_v3 = relation.old_relations.find_by(:version => 3)
|
||||||
|
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
do_redact_relation(relation_v3, create(:redaction))
|
||||||
|
assert_response :success, "should be OK to redact old version as moderator."
|
||||||
|
|
||||||
|
# re-auth as non-moderator
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
# check can't see the redacted data
|
||||||
|
get :version, :params => { :id => relation_v3.relation_id, :version => relation_v3.version }
|
||||||
|
assert_response :forbidden, "Redacted relation shouldn't be visible via the version API."
|
||||||
|
|
||||||
|
# and when accessed via history
|
||||||
|
get :history, :params => { :id => relation_v3.relation_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm relation[id='#{relation_v3.relation_id}'][version='#{relation_v3.version}']", 0, "redacted relation #{relation_v3.relation_id} version #{relation_v3.version} shouldn't be present in the history."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the unredaction of an old version of a relation, while not being
|
||||||
|
# authorised.
|
||||||
|
def test_unredact_relation_unauthorised
|
||||||
|
relation = create(:relation, :with_history, :version => 2)
|
||||||
|
relation_v1 = relation.old_relations.find_by(:version => 1)
|
||||||
|
relation_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
post :redact, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
||||||
|
assert_response :unauthorized, "should need to be authenticated to unredact."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the unredaction of an old version of a relation, while being
|
||||||
|
# authorised as a normal user.
|
||||||
|
def test_unredact_relation_normal_user
|
||||||
|
relation = create(:relation, :with_history, :version => 2)
|
||||||
|
relation_v1 = relation.old_relations.find_by(:version => 1)
|
||||||
|
relation_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
post :redact, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
||||||
|
assert_response :forbidden, "should need to be moderator to unredact."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the unredaction of an old version of a relation, while being
|
||||||
|
# authorised as a moderator.
|
||||||
|
def test_unredact_relation_moderator
|
||||||
|
relation = create(:relation, :with_history, :version => 2)
|
||||||
|
relation_v1 = relation.old_relations.find_by(:version => 1)
|
||||||
|
relation_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
post :redact, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
||||||
|
assert_response :success, "should be OK to unredact old version as moderator."
|
||||||
|
|
||||||
|
# check moderator can still see the redacted data, without passing
|
||||||
|
# the appropriate flag
|
||||||
|
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
||||||
|
assert_response :success, "After unredaction, relation should not be gone for moderator."
|
||||||
|
|
||||||
|
# and when accessed via history
|
||||||
|
get :history, :params => { :id => relation_v1.relation_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 1, "relation #{relation_v1.relation_id} version #{relation_v1.version} should still be present in the history for moderators."
|
||||||
|
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
# check normal user can now see the redacted data
|
||||||
|
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
||||||
|
assert_response :success, "After redaction, node should not be gone for normal user."
|
||||||
|
|
||||||
|
# and when accessed via history
|
||||||
|
get :history, :params => { :id => relation_v1.relation_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 1, "relation #{relation_v1.relation_id} version #{relation_v1.version} should still be present in the history for normal users."
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
##
|
||||||
|
# check that the current version of a relation is equivalent to the
|
||||||
|
# version which we're getting from the versions call.
|
||||||
|
def check_current_version(relation_id)
|
||||||
|
# get the current version
|
||||||
|
current_relation = with_controller(RelationsController.new) do
|
||||||
|
get :show, :params => { :id => relation_id }
|
||||||
|
assert_response :success, "can't get current relation #{relation_id}"
|
||||||
|
Relation.from_xml(@response.body)
|
||||||
|
end
|
||||||
|
assert_not_nil current_relation, "getting relation #{relation_id} returned nil"
|
||||||
|
|
||||||
|
# get the "old" version of the relation from the version method
|
||||||
|
get :version, :params => { :id => relation_id, :version => current_relation.version }
|
||||||
|
assert_response :success, "can't get old relation #{relation_id}, v#{current_relation.version}"
|
||||||
|
old_relation = Relation.from_xml(@response.body)
|
||||||
|
|
||||||
|
# check that the relations are identical
|
||||||
|
assert_relations_are_equal current_relation, old_relation
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# look at all the versions of the relation in the history and get each version from
|
||||||
|
# the versions call. check that they're the same.
|
||||||
|
def check_history_equals_versions(relation_id)
|
||||||
|
get :history, :params => { :id => relation_id }
|
||||||
|
assert_response :success, "can't get relation #{relation_id} from API"
|
||||||
|
history_doc = XML::Parser.string(@response.body).parse
|
||||||
|
assert_not_nil history_doc, "parsing relation #{relation_id} history failed"
|
||||||
|
|
||||||
|
history_doc.find("//osm/relation").each do |relation_doc|
|
||||||
|
history_relation = Relation.from_xml_node(relation_doc)
|
||||||
|
assert_not_nil history_relation, "parsing relation #{relation_id} version failed"
|
||||||
|
|
||||||
|
get :version, :params => { :id => relation_id, :version => history_relation.version }
|
||||||
|
assert_response :success, "couldn't get relation #{relation_id}, v#{history_relation.version}"
|
||||||
|
version_relation = Relation.from_xml(@response.body)
|
||||||
|
assert_not_nil version_relation, "failed to parse #{relation_id}, v#{history_relation.version}"
|
||||||
|
|
||||||
|
assert_relations_are_equal history_relation, version_relation
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_redact_relation(relation, redaction)
|
||||||
|
get :version, :params => { :id => relation.relation_id, :version => relation.version }
|
||||||
|
assert_response :success, "should be able to get version #{relation.version} of relation #{relation.relation_id}."
|
||||||
|
|
||||||
|
# now redact it
|
||||||
|
post :redact, :params => { :id => relation.relation_id, :version => relation.version, :redaction => redaction.id }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
320
test/controllers/api/old_ways_controller_test.rb
Normal file
320
test/controllers/api/old_ways_controller_test.rb
Normal file
|
@ -0,0 +1,320 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class OldWaysControllerTest < ActionController::TestCase
|
||||||
|
##
|
||||||
|
# test all routes which lead to this controller
|
||||||
|
def test_routes
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/way/1/history", :method => :get },
|
||||||
|
{ :controller => "api/old_ways", :action => "history", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/way/1/2", :method => :get },
|
||||||
|
{ :controller => "api/old_ways", :action => "version", :id => "1", :version => "2" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/way/1/2/redact", :method => :post },
|
||||||
|
{ :controller => "api/old_ways", :action => "redact", :id => "1", :version => "2" }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# -------------------------------------
|
||||||
|
# Test reading old ways.
|
||||||
|
# -------------------------------------
|
||||||
|
|
||||||
|
def test_history_visible
|
||||||
|
# check that a visible way is returned properly
|
||||||
|
get :history, :params => { :id => create(:way, :with_history).id }
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_history_invisible
|
||||||
|
# check that an invisible way's history is returned properly
|
||||||
|
get :history, :params => { :id => create(:way, :with_history, :deleted).id }
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_history_invalid
|
||||||
|
# check chat a non-existent way is not returned
|
||||||
|
get :history, :params => { :id => 0 }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# check that we can retrieve versions of a way
|
||||||
|
def test_version
|
||||||
|
way = create(:way, :with_history)
|
||||||
|
used_way = create(:way, :with_history)
|
||||||
|
create(:relation_member, :member => used_way)
|
||||||
|
way_with_versions = create(:way, :with_history, :version => 4)
|
||||||
|
|
||||||
|
create(:way_tag, :way => way)
|
||||||
|
create(:way_tag, :way => used_way)
|
||||||
|
create(:way_tag, :way => way_with_versions)
|
||||||
|
propagate_tags(way, way.old_ways.last)
|
||||||
|
propagate_tags(used_way, used_way.old_ways.last)
|
||||||
|
propagate_tags(way_with_versions, way_with_versions.old_ways.last)
|
||||||
|
|
||||||
|
check_current_version(way.id)
|
||||||
|
check_current_version(used_way.id)
|
||||||
|
check_current_version(way_with_versions.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# check that returned history is the same as getting all
|
||||||
|
# versions of a way from the api.
|
||||||
|
def test_history_equals_versions
|
||||||
|
way = create(:way, :with_history)
|
||||||
|
used_way = create(:way, :with_history)
|
||||||
|
create(:relation_member, :member => used_way)
|
||||||
|
way_with_versions = create(:way, :with_history, :version => 4)
|
||||||
|
|
||||||
|
check_history_equals_versions(way.id)
|
||||||
|
check_history_equals_versions(used_way.id)
|
||||||
|
check_history_equals_versions(way_with_versions.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the redaction of an old version of a way, while not being
|
||||||
|
# authorised.
|
||||||
|
def test_redact_way_unauthorised
|
||||||
|
way = create(:way, :with_history, :version => 4)
|
||||||
|
way_v3 = way.old_ways.find_by(:version => 3)
|
||||||
|
|
||||||
|
do_redact_way(way_v3, create(:redaction))
|
||||||
|
assert_response :unauthorized, "should need to be authenticated to redact."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the redaction of an old version of a way, while being
|
||||||
|
# authorised as a normal user.
|
||||||
|
def test_redact_way_normal_user
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
way = create(:way, :with_history, :version => 4)
|
||||||
|
way_v3 = way.old_ways.find_by(:version => 3)
|
||||||
|
|
||||||
|
do_redact_way(way_v3, create(:redaction))
|
||||||
|
assert_response :forbidden, "should need to be moderator to redact."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test that, even as moderator, the current version of a way
|
||||||
|
# can't be redacted.
|
||||||
|
def test_redact_way_current_version
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
way = create(:way, :with_history, :version => 4)
|
||||||
|
way_latest = way.old_ways.last
|
||||||
|
|
||||||
|
do_redact_way(way_latest, create(:redaction))
|
||||||
|
assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test that redacted ways aren't visible, regardless of
|
||||||
|
# authorisation except as moderator...
|
||||||
|
def test_version_redacted
|
||||||
|
way = create(:way, :with_history, :version => 2)
|
||||||
|
way_v1 = way.old_ways.find_by(:version => 1)
|
||||||
|
way_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
||||||
|
assert_response :forbidden, "Redacted way shouldn't be visible via the version API."
|
||||||
|
|
||||||
|
# not even to a logged-in user
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
||||||
|
assert_response :forbidden, "Redacted way shouldn't be visible via the version API, even when logged in."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test that redacted ways aren't visible in the history
|
||||||
|
def test_history_redacted
|
||||||
|
way = create(:way, :with_history, :version => 2)
|
||||||
|
way_v1 = way.old_ways.find_by(:version => 1)
|
||||||
|
way_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
get :history, :params => { :id => way_v1.way_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 0, "redacted way #{way_v1.way_id} version #{way_v1.version} shouldn't be present in the history."
|
||||||
|
|
||||||
|
# not even to a logged-in user
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
||||||
|
get :history, :params => { :id => way_v1.way_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 0, "redacted node #{way_v1.way_id} version #{way_v1.version} shouldn't be present in the history, even when logged in."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the redaction of an old version of a way, while being
|
||||||
|
# authorised as a moderator.
|
||||||
|
def test_redact_way_moderator
|
||||||
|
way = create(:way, :with_history, :version => 4)
|
||||||
|
way_v3 = way.old_ways.find_by(:version => 3)
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
do_redact_way(way_v3, create(:redaction))
|
||||||
|
assert_response :success, "should be OK to redact old version as moderator."
|
||||||
|
|
||||||
|
# check moderator can still see the redacted data, when passing
|
||||||
|
# the appropriate flag
|
||||||
|
get :version, :params => { :id => way_v3.way_id, :version => way_v3.version }
|
||||||
|
assert_response :forbidden, "After redaction, node should be gone for moderator, when flag not passed."
|
||||||
|
get :version, :params => { :id => way_v3.way_id, :version => way_v3.version, :show_redactions => "true" }
|
||||||
|
assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
|
||||||
|
|
||||||
|
# and when accessed via history
|
||||||
|
get :history, :params => { :id => way_v3.way_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm way[id='#{way_v3.way_id}'][version='#{way_v3.version}']", 0, "way #{way_v3.way_id} version #{way_v3.version} should not be present in the history for moderators when not passing flag."
|
||||||
|
get :history, :params => { :id => way_v3.way_id, :show_redactions => "true" }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm way[id='#{way_v3.way_id}'][version='#{way_v3.version}']", 1, "way #{way_v3.way_id} version #{way_v3.version} should still be present in the history for moderators when passing flag."
|
||||||
|
end
|
||||||
|
|
||||||
|
# testing that if the moderator drops auth, he can't see the
|
||||||
|
# redacted stuff any more.
|
||||||
|
def test_redact_way_is_redacted
|
||||||
|
way = create(:way, :with_history, :version => 4)
|
||||||
|
way_v3 = way.old_ways.find_by(:version => 3)
|
||||||
|
basic_authorization create(:moderator_user).email, "test"
|
||||||
|
|
||||||
|
do_redact_way(way_v3, create(:redaction))
|
||||||
|
assert_response :success, "should be OK to redact old version as moderator."
|
||||||
|
|
||||||
|
# re-auth as non-moderator
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
# check can't see the redacted data
|
||||||
|
get :version, :params => { :id => way_v3.way_id, :version => way_v3.version }
|
||||||
|
assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
|
||||||
|
|
||||||
|
# and when accessed via history
|
||||||
|
get :history, :params => { :id => way_v3.way_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm way[id='#{way_v3.way_id}'][version='#{way_v3.version}']", 0, "redacted way #{way_v3.way_id} version #{way_v3.version} shouldn't be present in the history."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the unredaction of an old version of a way, while not being
|
||||||
|
# authorised.
|
||||||
|
def test_unredact_way_unauthorised
|
||||||
|
way = create(:way, :with_history, :version => 2)
|
||||||
|
way_v1 = way.old_ways.find_by(:version => 1)
|
||||||
|
way_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
post :redact, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
||||||
|
assert_response :unauthorized, "should need to be authenticated to unredact."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the unredaction of an old version of a way, while being
|
||||||
|
# authorised as a normal user.
|
||||||
|
def test_unredact_way_normal_user
|
||||||
|
way = create(:way, :with_history, :version => 2)
|
||||||
|
way_v1 = way.old_ways.find_by(:version => 1)
|
||||||
|
way_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
post :redact, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
||||||
|
assert_response :forbidden, "should need to be moderator to unredact."
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test the unredaction of an old version of a way, while being
|
||||||
|
# authorised as a moderator.
|
||||||
|
def test_unredact_way_moderator
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
way = create(:way, :with_history, :version => 2)
|
||||||
|
way_v1 = way.old_ways.find_by(:version => 1)
|
||||||
|
way_v1.redact!(create(:redaction))
|
||||||
|
|
||||||
|
basic_authorization moderator_user.email, "test"
|
||||||
|
|
||||||
|
post :redact, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
||||||
|
assert_response :success, "should be OK to unredact old version as moderator."
|
||||||
|
|
||||||
|
# check moderator can still see the unredacted data, without passing
|
||||||
|
# the appropriate flag
|
||||||
|
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
||||||
|
assert_response :success, "After unredaction, node should not be gone for moderator."
|
||||||
|
|
||||||
|
# and when accessed via history
|
||||||
|
get :history, :params => { :id => way_v1.way_id }
|
||||||
|
assert_response :success, "Unredaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 1, "way #{way_v1.way_id} version #{way_v1.version} should still be present in the history for moderators."
|
||||||
|
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
# check normal user can now see the unredacted data
|
||||||
|
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
||||||
|
assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
|
||||||
|
|
||||||
|
# and when accessed via history
|
||||||
|
get :history, :params => { :id => way_v1.way_id }
|
||||||
|
assert_response :success, "Redaction shouldn't have stopped history working."
|
||||||
|
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 1, "way #{way_v1.way_id} version #{way_v1.version} should still be present in the history for normal users."
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
##
|
||||||
|
# check that the current version of a way is equivalent to the
|
||||||
|
# version which we're getting from the versions call.
|
||||||
|
def check_current_version(way_id)
|
||||||
|
# get the current version
|
||||||
|
current_way = with_controller(WaysController.new) do
|
||||||
|
get :show, :params => { :id => way_id }
|
||||||
|
assert_response :success, "can't get current way #{way_id}"
|
||||||
|
Way.from_xml(@response.body)
|
||||||
|
end
|
||||||
|
assert_not_nil current_way, "getting way #{way_id} returned nil"
|
||||||
|
|
||||||
|
# get the "old" version of the way from the version method
|
||||||
|
get :version, :params => { :id => way_id, :version => current_way.version }
|
||||||
|
assert_response :success, "can't get old way #{way_id}, v#{current_way.version}"
|
||||||
|
old_way = Way.from_xml(@response.body)
|
||||||
|
|
||||||
|
# check that the ways are identical
|
||||||
|
assert_ways_are_equal current_way, old_way
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# look at all the versions of the way in the history and get each version from
|
||||||
|
# the versions call. check that they're the same.
|
||||||
|
def check_history_equals_versions(way_id)
|
||||||
|
get :history, :params => { :id => way_id }
|
||||||
|
assert_response :success, "can't get way #{way_id} from API"
|
||||||
|
history_doc = XML::Parser.string(@response.body).parse
|
||||||
|
assert_not_nil history_doc, "parsing way #{way_id} history failed"
|
||||||
|
|
||||||
|
history_doc.find("//osm/way").each do |way_doc|
|
||||||
|
history_way = Way.from_xml_node(way_doc)
|
||||||
|
assert_not_nil history_way, "parsing way #{way_id} version failed"
|
||||||
|
|
||||||
|
get :version, :params => { :id => way_id, :version => history_way.version }
|
||||||
|
assert_response :success, "couldn't get way #{way_id}, v#{history_way.version}"
|
||||||
|
version_way = Way.from_xml(@response.body)
|
||||||
|
assert_not_nil version_way, "failed to parse #{way_id}, v#{history_way.version}"
|
||||||
|
|
||||||
|
assert_ways_are_equal history_way, version_way
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_redact_way(way, redaction)
|
||||||
|
get :version, :params => { :id => way.way_id, :version => way.version }
|
||||||
|
assert_response :success, "should be able to get version #{way.version} of way #{way.way_id}."
|
||||||
|
|
||||||
|
# now redact it
|
||||||
|
post :redact, :params => { :id => way.way_id, :version => way.version, :redaction => redaction.id }
|
||||||
|
end
|
||||||
|
|
||||||
|
def propagate_tags(way, old_way)
|
||||||
|
way.tags.each do |k, v|
|
||||||
|
create(:old_way_tag, :old_way => old_way, :k => k, :v => v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
1079
test/controllers/api/relations_controller_test.rb
Normal file
1079
test/controllers/api/relations_controller_test.rb
Normal file
File diff suppressed because it is too large
Load diff
108
test/controllers/api/search_controller_test.rb
Normal file
108
test/controllers/api/search_controller_test.rb
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class SearchControllerTest < ActionController::TestCase
|
||||||
|
##
|
||||||
|
# test all routes which lead to this controller
|
||||||
|
def test_routes
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/search", :method => :get },
|
||||||
|
{ :controller => "api/search", :action => "search_all" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/nodes/search", :method => :get },
|
||||||
|
{ :controller => "api/search", :action => "search_nodes" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/ways/search", :method => :get },
|
||||||
|
{ :controller => "api/search", :action => "search_ways" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/relations/search", :method => :get },
|
||||||
|
{ :controller => "api/search", :action => "search_relations" }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test searching nodes
|
||||||
|
def test_search_nodes
|
||||||
|
get :search_nodes, :params => { :type => "test" }
|
||||||
|
assert_response :service_unavailable
|
||||||
|
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
|
||||||
|
|
||||||
|
get :search_nodes, :params => { :type => "test", :value => "yes" }
|
||||||
|
assert_response :service_unavailable
|
||||||
|
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
|
||||||
|
|
||||||
|
get :search_nodes, :params => { :name => "Test Node" }
|
||||||
|
assert_response :service_unavailable
|
||||||
|
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test searching ways
|
||||||
|
def test_search_ways
|
||||||
|
first_way = create(:way_with_nodes, :nodes_count => 2)
|
||||||
|
deleted_way = create(:way_with_nodes, :deleted, :nodes_count => 2)
|
||||||
|
third_way = create(:way_with_nodes, :nodes_count => 2)
|
||||||
|
|
||||||
|
[first_way, deleted_way, third_way].each do |way|
|
||||||
|
create(:way_tag, :way => way, :k => "test", :v => "yes")
|
||||||
|
end
|
||||||
|
create(:way_tag, :way => third_way, :k => "name", :v => "Test Way")
|
||||||
|
|
||||||
|
get :search_ways, :params => { :type => "test" }
|
||||||
|
assert_response :service_unavailable
|
||||||
|
assert_equal "Searching for a key without value is currently unavailable", response.headers["Error"]
|
||||||
|
|
||||||
|
get :search_ways, :params => { :type => "test", :value => "yes" }
|
||||||
|
assert_response :success
|
||||||
|
assert_select "way", 3
|
||||||
|
|
||||||
|
get :search_ways, :params => { :name => "Test Way" }
|
||||||
|
assert_response :success
|
||||||
|
assert_select "way", 1
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test searching relations
|
||||||
|
def test_search_relations
|
||||||
|
first_relation = create(:relation)
|
||||||
|
deleted_relation = create(:relation)
|
||||||
|
third_relation = create(:relation)
|
||||||
|
|
||||||
|
[first_relation, deleted_relation, third_relation].each do |relation|
|
||||||
|
create(:relation_tag, :relation => relation, :k => "test", :v => "yes")
|
||||||
|
end
|
||||||
|
create(:relation_tag, :relation => third_relation, :k => "name", :v => "Test Relation")
|
||||||
|
|
||||||
|
get :search_relations, :params => { :type => "test" }
|
||||||
|
assert_response :service_unavailable
|
||||||
|
assert_equal "Searching for a key without value is currently unavailable", response.headers["Error"]
|
||||||
|
|
||||||
|
get :search_relations, :params => { :type => "test", :value => "yes" }
|
||||||
|
assert_response :success
|
||||||
|
assert_select "relation", 3
|
||||||
|
|
||||||
|
get :search_relations, :params => { :name => "Test Relation" }
|
||||||
|
assert_response :success
|
||||||
|
assert_select "relation", 1
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test searching nodes, ways and relations
|
||||||
|
def test_search_all
|
||||||
|
get :search_all, :params => { :type => "test" }
|
||||||
|
assert_response :service_unavailable
|
||||||
|
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
|
||||||
|
|
||||||
|
get :search_all, :params => { :type => "test", :value => "yes" }
|
||||||
|
assert_response :service_unavailable
|
||||||
|
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
|
||||||
|
|
||||||
|
get :search_all, :params => { :name => "Test" }
|
||||||
|
assert_response :service_unavailable
|
||||||
|
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
46
test/controllers/api/swf_controller_test.rb
Normal file
46
test/controllers/api/swf_controller_test.rb
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class SwfControllerTest < ActionController::TestCase
|
||||||
|
##
|
||||||
|
# test all routes which lead to this controller
|
||||||
|
def test_routes
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/swf/trackpoints", :method => :get },
|
||||||
|
{ :controller => "api/swf", :action => "trackpoints" }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# basic test that trackpoints at least returns some sort of flash movie
|
||||||
|
def test_trackpoints
|
||||||
|
user = create(:user)
|
||||||
|
other_user = create(:user)
|
||||||
|
create(:trace, :visibility => "trackable", :latitude => 51.51, :longitude => -0.14, :user => user) do |trace|
|
||||||
|
create(:tracepoint, :trace => trace, :trackid => 1, :latitude => (51.510 * GeoRecord::SCALE).to_i, :longitude => (-0.140 * GeoRecord::SCALE).to_i)
|
||||||
|
create(:tracepoint, :trace => trace, :trackid => 2, :latitude => (51.511 * GeoRecord::SCALE).to_i, :longitude => (-0.141 * GeoRecord::SCALE).to_i)
|
||||||
|
end
|
||||||
|
create(:trace, :visibility => "identifiable", :latitude => 51.512, :longitude => 0.142) do |trace|
|
||||||
|
create(:tracepoint, :trace => trace, :latitude => (51.512 * GeoRecord::SCALE).to_i, :longitude => (0.142 * GeoRecord::SCALE).to_i)
|
||||||
|
end
|
||||||
|
|
||||||
|
get :trackpoints, :params => { :xmin => -1, :xmax => 1, :ymin => 51, :ymax => 52, :baselong => 0, :basey => 0, :masterscale => 1 }
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "application/x-shockwave-flash", response.content_type
|
||||||
|
assert_match(/^FWS/, response.body)
|
||||||
|
assert_equal 80, response.body.length
|
||||||
|
|
||||||
|
get :trackpoints, :params => { :xmin => -1, :xmax => 1, :ymin => 51, :ymax => 52, :baselong => 0, :basey => 0, :masterscale => 1, :token => other_user.tokens.create.token }
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "application/x-shockwave-flash", response.content_type
|
||||||
|
assert_match(/^FWS/, response.body)
|
||||||
|
assert_equal 67, response.body.length
|
||||||
|
|
||||||
|
get :trackpoints, :params => { :xmin => -1, :xmax => 1, :ymin => 51, :ymax => 52, :baselong => 0, :basey => 0, :masterscale => 1, :token => user.tokens.create.token }
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "application/x-shockwave-flash", response.content_type
|
||||||
|
assert_match(/^FWS/, response.body)
|
||||||
|
assert_equal 74, response.body.length
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
357
test/controllers/api/traces_controller_test.rb
Normal file
357
test/controllers/api/traces_controller_test.rb
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
require "test_helper"
|
||||||
|
require "minitest/mock"
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class TracesControllerTest < ActionController::TestCase
|
||||||
|
def setup
|
||||||
|
@gpx_trace_dir = Object.send("remove_const", "GPX_TRACE_DIR")
|
||||||
|
Object.const_set("GPX_TRACE_DIR", Rails.root.join("test", "gpx", "traces"))
|
||||||
|
|
||||||
|
@gpx_image_dir = Object.send("remove_const", "GPX_IMAGE_DIR")
|
||||||
|
Object.const_set("GPX_IMAGE_DIR", Rails.root.join("test", "gpx", "images"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def teardown
|
||||||
|
File.unlink(*Dir.glob(File.join(GPX_TRACE_DIR, "*.gpx")))
|
||||||
|
File.unlink(*Dir.glob(File.join(GPX_IMAGE_DIR, "*.gif")))
|
||||||
|
|
||||||
|
Object.send("remove_const", "GPX_TRACE_DIR")
|
||||||
|
Object.const_set("GPX_TRACE_DIR", @gpx_trace_dir)
|
||||||
|
|
||||||
|
Object.send("remove_const", "GPX_IMAGE_DIR")
|
||||||
|
Object.const_set("GPX_IMAGE_DIR", @gpx_image_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test all routes which lead to this controller
|
||||||
|
def test_routes
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/gpx/create", :method => :post },
|
||||||
|
{ :controller => "api/traces", :action => "api_create" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/gpx/1", :method => :get },
|
||||||
|
{ :controller => "api/traces", :action => "api_read", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/gpx/1", :method => :put },
|
||||||
|
{ :controller => "api/traces", :action => "api_update", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/gpx/1", :method => :delete },
|
||||||
|
{ :controller => "api/traces", :action => "api_delete", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_recognizes(
|
||||||
|
{ :controller => "api/traces", :action => "api_read", :id => "1" },
|
||||||
|
{ :path => "/api/0.6/gpx/1/details", :method => :get }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/gpx/1/data", :method => :get },
|
||||||
|
{ :controller => "api/traces", :action => "api_data", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/gpx/1/data.xml", :method => :get },
|
||||||
|
{ :controller => "api/traces", :action => "api_data", :id => "1", :format => "xml" }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check getting a specific trace through the api
|
||||||
|
def test_api_read
|
||||||
|
public_trace_file = create(:trace, :visibility => "public")
|
||||||
|
|
||||||
|
# First with no auth
|
||||||
|
get :api_read, :params => { :id => public_trace_file.id }
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
# Now with some other user, which should work since the trace is public
|
||||||
|
basic_authorization create(:user).display_name, "test"
|
||||||
|
get :api_read, :params => { :id => public_trace_file.id }
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
# And finally we should be able to do it with the owner of the trace
|
||||||
|
basic_authorization public_trace_file.user.display_name, "test"
|
||||||
|
get :api_read, :params => { :id => public_trace_file.id }
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check an anoymous trace can't be specifically fetched by another user
|
||||||
|
def test_api_read_anon
|
||||||
|
anon_trace_file = create(:trace, :visibility => "private")
|
||||||
|
|
||||||
|
# First with no auth
|
||||||
|
get :api_read, :params => { :id => anon_trace_file.id }
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
# Now try with another user, which shouldn't work since the trace is anon
|
||||||
|
basic_authorization create(:user).display_name, "test"
|
||||||
|
get :api_read, :params => { :id => anon_trace_file.id }
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# And finally we should be able to get the trace details with the trace owner
|
||||||
|
basic_authorization anon_trace_file.user.display_name, "test"
|
||||||
|
get :api_read, :params => { :id => anon_trace_file.id }
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check the api details for a trace that doesn't exist
|
||||||
|
def test_api_read_not_found
|
||||||
|
deleted_trace_file = create(:trace, :deleted)
|
||||||
|
|
||||||
|
# Try first with no auth, as it should require it
|
||||||
|
get :api_read, :params => { :id => 0 }
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
# Login, and try again
|
||||||
|
basic_authorization deleted_trace_file.user.display_name, "test"
|
||||||
|
get :api_read, :params => { :id => 0 }
|
||||||
|
assert_response :not_found
|
||||||
|
|
||||||
|
# Now try a trace which did exist but has been deleted
|
||||||
|
basic_authorization deleted_trace_file.user.display_name, "test"
|
||||||
|
get :api_read, :params => { :id => deleted_trace_file.id }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
# Test downloading a trace through the api
|
||||||
|
def test_api_data
|
||||||
|
public_trace_file = create(:trace, :visibility => "public", :fixture => "a")
|
||||||
|
|
||||||
|
# First with no auth
|
||||||
|
get :api_data, :params => { :id => public_trace_file.id }
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
# Now with some other user, which should work since the trace is public
|
||||||
|
basic_authorization create(:user).display_name, "test"
|
||||||
|
get :api_data, :params => { :id => public_trace_file.id }
|
||||||
|
check_trace_data public_trace_file, "848caa72f2f456d1bd6a0fdf228aa1b9"
|
||||||
|
|
||||||
|
# And finally we should be able to do it with the owner of the trace
|
||||||
|
basic_authorization public_trace_file.user.display_name, "test"
|
||||||
|
get :api_data, :params => { :id => public_trace_file.id }
|
||||||
|
check_trace_data public_trace_file, "848caa72f2f456d1bd6a0fdf228aa1b9"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Test downloading a compressed trace through the api
|
||||||
|
def test_api_data_compressed
|
||||||
|
identifiable_trace_file = create(:trace, :visibility => "identifiable", :fixture => "d")
|
||||||
|
|
||||||
|
# Authenticate as the owner of the trace we will be using
|
||||||
|
basic_authorization identifiable_trace_file.user.display_name, "test"
|
||||||
|
|
||||||
|
# First get the data as is
|
||||||
|
get :api_data, :params => { :id => identifiable_trace_file.id }
|
||||||
|
check_trace_data identifiable_trace_file, "c6422a3d8750faae49ed70e7e8a51b93", "application/x-gzip", "gpx.gz"
|
||||||
|
|
||||||
|
# Now ask explicitly for XML format
|
||||||
|
get :api_data, :params => { :id => identifiable_trace_file.id, :format => "xml" }
|
||||||
|
check_trace_data identifiable_trace_file, "abd6675fdf3024a84fc0a1deac147c0d", "application/xml", "xml"
|
||||||
|
|
||||||
|
# Now ask explicitly for GPX format
|
||||||
|
get :api_data, :params => { :id => identifiable_trace_file.id, :format => "gpx" }
|
||||||
|
check_trace_data identifiable_trace_file, "abd6675fdf3024a84fc0a1deac147c0d"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check an anonymous trace can't be downloaded by another user through the api
|
||||||
|
def test_api_data_anon
|
||||||
|
anon_trace_file = create(:trace, :visibility => "private", :fixture => "b")
|
||||||
|
|
||||||
|
# First with no auth
|
||||||
|
get :api_data, :params => { :id => anon_trace_file.id }
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
# Now with some other user, which shouldn't work since the trace is anon
|
||||||
|
basic_authorization create(:user).display_name, "test"
|
||||||
|
get :api_data, :params => { :id => anon_trace_file.id }
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# And finally we should be able to do it with the owner of the trace
|
||||||
|
basic_authorization anon_trace_file.user.display_name, "test"
|
||||||
|
get :api_data, :params => { :id => anon_trace_file.id }
|
||||||
|
check_trace_data anon_trace_file, "66179ca44f1e93d8df62e2b88cbea732"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Test downloading a trace that doesn't exist through the api
|
||||||
|
def test_api_data_not_found
|
||||||
|
deleted_trace_file = create(:trace, :deleted)
|
||||||
|
|
||||||
|
# Try first with no auth, as it should require it
|
||||||
|
get :api_data, :params => { :id => 0 }
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
# Login, and try again
|
||||||
|
basic_authorization create(:user).display_name, "test"
|
||||||
|
get :api_data, :params => { :id => 0 }
|
||||||
|
assert_response :not_found
|
||||||
|
|
||||||
|
# Now try a trace which did exist but has been deleted
|
||||||
|
basic_authorization deleted_trace_file.user.display_name, "test"
|
||||||
|
get :api_data, :params => { :id => deleted_trace_file.id }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
# Test creating a trace through the api
|
||||||
|
def test_api_create
|
||||||
|
# Get file to use
|
||||||
|
fixture = Rails.root.join("test", "gpx", "fixtures", "a.gpx")
|
||||||
|
file = Rack::Test::UploadedFile.new(fixture, "application/gpx+xml")
|
||||||
|
user = create(:user)
|
||||||
|
|
||||||
|
# First with no auth
|
||||||
|
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :visibility => "trackable" }
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
# Rewind the file
|
||||||
|
file.rewind
|
||||||
|
|
||||||
|
# Now authenticated
|
||||||
|
create(:user_preference, :user => user, :k => "gps.trace.visibility", :v => "identifiable")
|
||||||
|
assert_not_equal "trackable", user.preferences.where(:k => "gps.trace.visibility").first.v
|
||||||
|
basic_authorization user.display_name, "test"
|
||||||
|
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :visibility => "trackable" }
|
||||||
|
assert_response :success
|
||||||
|
trace = Trace.find(response.body.to_i)
|
||||||
|
assert_equal "a.gpx", trace.name
|
||||||
|
assert_equal "New Trace", trace.description
|
||||||
|
assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
|
||||||
|
assert_equal "trackable", trace.visibility
|
||||||
|
assert_equal false, trace.inserted
|
||||||
|
assert_equal File.new(fixture).read, File.new(trace.trace_name).read
|
||||||
|
trace.destroy
|
||||||
|
assert_equal "trackable", user.preferences.where(:k => "gps.trace.visibility").first.v
|
||||||
|
|
||||||
|
# Rewind the file
|
||||||
|
file.rewind
|
||||||
|
|
||||||
|
# Now authenticated, with the legacy public flag
|
||||||
|
assert_not_equal "public", user.preferences.where(:k => "gps.trace.visibility").first.v
|
||||||
|
basic_authorization user.display_name, "test"
|
||||||
|
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :public => 1 }
|
||||||
|
assert_response :success
|
||||||
|
trace = Trace.find(response.body.to_i)
|
||||||
|
assert_equal "a.gpx", trace.name
|
||||||
|
assert_equal "New Trace", trace.description
|
||||||
|
assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
|
||||||
|
assert_equal "public", trace.visibility
|
||||||
|
assert_equal false, trace.inserted
|
||||||
|
assert_equal File.new(fixture).read, File.new(trace.trace_name).read
|
||||||
|
trace.destroy
|
||||||
|
assert_equal "public", user.preferences.where(:k => "gps.trace.visibility").first.v
|
||||||
|
|
||||||
|
# Rewind the file
|
||||||
|
file.rewind
|
||||||
|
|
||||||
|
# Now authenticated, with the legacy private flag
|
||||||
|
second_user = create(:user)
|
||||||
|
assert_nil second_user.preferences.where(:k => "gps.trace.visibility").first
|
||||||
|
basic_authorization second_user.display_name, "test"
|
||||||
|
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :public => 0 }
|
||||||
|
assert_response :success
|
||||||
|
trace = Trace.find(response.body.to_i)
|
||||||
|
assert_equal "a.gpx", trace.name
|
||||||
|
assert_equal "New Trace", trace.description
|
||||||
|
assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
|
||||||
|
assert_equal "private", trace.visibility
|
||||||
|
assert_equal false, trace.inserted
|
||||||
|
assert_equal File.new(fixture).read, File.new(trace.trace_name).read
|
||||||
|
trace.destroy
|
||||||
|
assert_equal "private", second_user.preferences.where(:k => "gps.trace.visibility").first.v
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check updating a trace through the api
|
||||||
|
def test_api_update
|
||||||
|
public_trace_file = create(:trace, :visibility => "public", :fixture => "a")
|
||||||
|
deleted_trace_file = create(:trace, :deleted)
|
||||||
|
anon_trace_file = create(:trace, :visibility => "private")
|
||||||
|
|
||||||
|
# First with no auth
|
||||||
|
put :api_update, :params => { :id => public_trace_file.id }, :body => public_trace_file.to_xml.to_s
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
# Now with some other user, which should fail
|
||||||
|
basic_authorization create(:user).display_name, "test"
|
||||||
|
put :api_update, :params => { :id => public_trace_file.id }, :body => public_trace_file.to_xml.to_s
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# Now with a trace which doesn't exist
|
||||||
|
basic_authorization create(:user).display_name, "test"
|
||||||
|
put :api_update, :params => { :id => 0 }, :body => public_trace_file.to_xml.to_s
|
||||||
|
assert_response :not_found
|
||||||
|
|
||||||
|
# Now with a trace which did exist but has been deleted
|
||||||
|
basic_authorization deleted_trace_file.user.display_name, "test"
|
||||||
|
put :api_update, :params => { :id => deleted_trace_file.id }, :body => deleted_trace_file.to_xml.to_s
|
||||||
|
assert_response :not_found
|
||||||
|
|
||||||
|
# Now try an update with the wrong ID
|
||||||
|
basic_authorization public_trace_file.user.display_name, "test"
|
||||||
|
put :api_update, :params => { :id => public_trace_file.id }, :body => anon_trace_file.to_xml.to_s
|
||||||
|
assert_response :bad_request,
|
||||||
|
"should not be able to update a trace with a different ID from the XML"
|
||||||
|
|
||||||
|
# And finally try an update that should work
|
||||||
|
basic_authorization public_trace_file.user.display_name, "test"
|
||||||
|
t = public_trace_file
|
||||||
|
t.description = "Changed description"
|
||||||
|
t.visibility = "private"
|
||||||
|
put :api_update, :params => { :id => t.id }, :body => t.to_xml.to_s
|
||||||
|
assert_response :success
|
||||||
|
nt = Trace.find(t.id)
|
||||||
|
assert_equal nt.description, t.description
|
||||||
|
assert_equal nt.visibility, t.visibility
|
||||||
|
end
|
||||||
|
|
||||||
|
# Test that updating a trace doesn't duplicate the tags
|
||||||
|
def test_api_update_tags
|
||||||
|
tracetag = create(:tracetag)
|
||||||
|
trace = tracetag.trace
|
||||||
|
basic_authorization trace.user.display_name, "test"
|
||||||
|
|
||||||
|
put :api_update, :params => { :id => trace.id }, :body => trace.to_xml.to_s
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
updated = Trace.find(trace.id)
|
||||||
|
# Ensure there's only one tag in the database after updating
|
||||||
|
assert_equal Tracetag.count, 1
|
||||||
|
# The new tag object might have a different id, so check the string representation
|
||||||
|
assert_equal trace.tagstring, updated.tagstring
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check deleting a trace through the api
|
||||||
|
def test_api_delete
|
||||||
|
public_trace_file = create(:trace, :visibility => "public")
|
||||||
|
|
||||||
|
# First with no auth
|
||||||
|
delete :api_delete, :params => { :id => public_trace_file.id }
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
# Now with some other user, which should fail
|
||||||
|
basic_authorization create(:user).display_name, "test"
|
||||||
|
delete :api_delete, :params => { :id => public_trace_file.id }
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# Now with a trace which doesn't exist
|
||||||
|
basic_authorization create(:user).display_name, "test"
|
||||||
|
delete :api_delete, :params => { :id => 0 }
|
||||||
|
assert_response :not_found
|
||||||
|
|
||||||
|
# And finally we should be able to do it with the owner of the trace
|
||||||
|
basic_authorization public_trace_file.user.display_name, "test"
|
||||||
|
delete :api_delete, :params => { :id => public_trace_file.id }
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
# Try it a second time, which should fail
|
||||||
|
basic_authorization public_trace_file.user.display_name, "test"
|
||||||
|
delete :api_delete, :params => { :id => public_trace_file.id }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_trace_data(trace, digest, content_type = "application/gpx+xml", extension = "gpx")
|
||||||
|
assert_response :success
|
||||||
|
assert_equal digest, Digest::MD5.hexdigest(response.body)
|
||||||
|
assert_equal content_type, response.content_type
|
||||||
|
assert_equal "attachment; filename=\"#{trace.id}.#{extension}\"", @response.header["Content-Disposition"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
246
test/controllers/api/user_preferences_controller_test.rb
Normal file
246
test/controllers/api/user_preferences_controller_test.rb
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class UserPreferencesControllerTest < ActionController::TestCase
|
||||||
|
##
|
||||||
|
# test all routes which lead to this controller
|
||||||
|
def test_routes
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/user/preferences", :method => :get },
|
||||||
|
{ :controller => "api/user_preferences", :action => "read" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/user/preferences", :method => :put },
|
||||||
|
{ :controller => "api/user_preferences", :action => "update" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/user/preferences/key", :method => :get },
|
||||||
|
{ :controller => "api/user_preferences", :action => "read_one", :preference_key => "key" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/user/preferences/key", :method => :put },
|
||||||
|
{ :controller => "api/user_preferences", :action => "update_one", :preference_key => "key" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/user/preferences/key", :method => :delete },
|
||||||
|
{ :controller => "api/user_preferences", :action => "delete_one", :preference_key => "key" }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test read action
|
||||||
|
def test_read
|
||||||
|
# first try without auth
|
||||||
|
get :read
|
||||||
|
assert_response :unauthorized, "should be authenticated"
|
||||||
|
|
||||||
|
# authenticate as a user with no preferences
|
||||||
|
basic_authorization create(:user).email, "test"
|
||||||
|
|
||||||
|
# try the read again
|
||||||
|
get :read
|
||||||
|
assert_select "osm" do
|
||||||
|
assert_select "preferences", :count => 1 do
|
||||||
|
assert_select "preference", :count => 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# authenticate as a user with preferences
|
||||||
|
user = create(:user)
|
||||||
|
user_preference = create(:user_preference, :user => user)
|
||||||
|
user_preference2 = create(:user_preference, :user => user)
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# try the read again
|
||||||
|
get :read
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "application/xml", @response.content_type
|
||||||
|
assert_select "osm" do
|
||||||
|
assert_select "preferences", :count => 1 do
|
||||||
|
assert_select "preference", :count => 2
|
||||||
|
assert_select "preference[k=\"#{user_preference.k}\"][v=\"#{user_preference.v}\"]", :count => 1
|
||||||
|
assert_select "preference[k=\"#{user_preference2.k}\"][v=\"#{user_preference2.v}\"]", :count => 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test read_one action
|
||||||
|
def test_read_one
|
||||||
|
user = create(:user)
|
||||||
|
create(:user_preference, :user => user, :k => "key", :v => "value")
|
||||||
|
|
||||||
|
# try a read without auth
|
||||||
|
get :read_one, :params => { :preference_key => "key" }
|
||||||
|
assert_response :unauthorized, "should be authenticated"
|
||||||
|
|
||||||
|
# authenticate as a user with preferences
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# try the read again
|
||||||
|
get :read_one, :params => { :preference_key => "key" }
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "text/plain", @response.content_type
|
||||||
|
assert_equal "value", @response.body
|
||||||
|
|
||||||
|
# try the read again for a non-existent key
|
||||||
|
get :read_one, :params => { :preference_key => "unknown_key" }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test update action
|
||||||
|
def test_update
|
||||||
|
user = create(:user)
|
||||||
|
create(:user_preference, :user => user, :k => "key", :v => "value")
|
||||||
|
create(:user_preference, :user => user, :k => "some_key", :v => "some_value")
|
||||||
|
|
||||||
|
# try a put without auth
|
||||||
|
assert_no_difference "UserPreference.count" do
|
||||||
|
put :update, :body => "<osm><preferences><preference k='key' v='new_value'/><preference k='new_key' v='value'/></preferences></osm>"
|
||||||
|
end
|
||||||
|
assert_response :unauthorized, "should be authenticated"
|
||||||
|
assert_equal "value", UserPreference.find([user.id, "key"]).v
|
||||||
|
assert_equal "some_value", UserPreference.find([user.id, "some_key"]).v
|
||||||
|
assert_raises ActiveRecord::RecordNotFound do
|
||||||
|
UserPreference.find([user.id, "new_key"])
|
||||||
|
end
|
||||||
|
|
||||||
|
# authenticate as a user with preferences
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# try the put again
|
||||||
|
assert_no_difference "UserPreference.count" do
|
||||||
|
put :update, :body => "<osm><preferences><preference k='key' v='new_value'/><preference k='new_key' v='value'/></preferences></osm>"
|
||||||
|
end
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "text/plain", @response.content_type
|
||||||
|
assert_equal "", @response.body
|
||||||
|
assert_equal "new_value", UserPreference.find([user.id, "key"]).v
|
||||||
|
assert_equal "value", UserPreference.find([user.id, "new_key"]).v
|
||||||
|
assert_raises ActiveRecord::RecordNotFound do
|
||||||
|
UserPreference.find([user.id, "some_key"])
|
||||||
|
end
|
||||||
|
|
||||||
|
# try a put with duplicate keys
|
||||||
|
assert_no_difference "UserPreference.count" do
|
||||||
|
put :update, :body => "<osm><preferences><preference k='key' v='value'/><preference k='key' v='newer_value'/></preferences></osm>"
|
||||||
|
end
|
||||||
|
assert_response :bad_request
|
||||||
|
assert_equal "text/plain", @response.content_type
|
||||||
|
assert_equal "Duplicate preferences with key key", @response.body
|
||||||
|
assert_equal "new_value", UserPreference.find([user.id, "key"]).v
|
||||||
|
|
||||||
|
# try a put with invalid content
|
||||||
|
assert_no_difference "UserPreference.count" do
|
||||||
|
put :update, :body => "nonsense"
|
||||||
|
end
|
||||||
|
assert_response :bad_request
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test update_one action
|
||||||
|
def test_update_one
|
||||||
|
user = create(:user)
|
||||||
|
create(:user_preference, :user => user)
|
||||||
|
|
||||||
|
# try a put without auth
|
||||||
|
assert_no_difference "UserPreference.count" do
|
||||||
|
put :update_one, :params => { :preference_key => "new_key" }, :body => "new_value"
|
||||||
|
end
|
||||||
|
assert_response :unauthorized, "should be authenticated"
|
||||||
|
assert_raises ActiveRecord::RecordNotFound do
|
||||||
|
UserPreference.find([user.id, "new_key"])
|
||||||
|
end
|
||||||
|
|
||||||
|
# authenticate as a user with preferences
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# try adding a new preference
|
||||||
|
assert_difference "UserPreference.count", 1 do
|
||||||
|
put :update_one, :params => { :preference_key => "new_key" }, :body => "new_value"
|
||||||
|
end
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "text/plain", @response.content_type
|
||||||
|
assert_equal "", @response.body
|
||||||
|
assert_equal "new_value", UserPreference.find([user.id, "new_key"]).v
|
||||||
|
|
||||||
|
# try changing the value of a preference
|
||||||
|
assert_no_difference "UserPreference.count" do
|
||||||
|
put :update_one, :params => { :preference_key => "new_key" }, :body => "newer_value"
|
||||||
|
end
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "text/plain", @response.content_type
|
||||||
|
assert_equal "", @response.body
|
||||||
|
assert_equal "newer_value", UserPreference.find([user.id, "new_key"]).v
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test delete_one action
|
||||||
|
def test_delete_one
|
||||||
|
user = create(:user)
|
||||||
|
create(:user_preference, :user => user, :k => "key", :v => "value")
|
||||||
|
|
||||||
|
# try a delete without auth
|
||||||
|
assert_no_difference "UserPreference.count" do
|
||||||
|
delete :delete_one, :params => { :preference_key => "key" }
|
||||||
|
end
|
||||||
|
assert_response :unauthorized, "should be authenticated"
|
||||||
|
assert_equal "value", UserPreference.find([user.id, "key"]).v
|
||||||
|
|
||||||
|
# authenticate as a user with preferences
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# try the delete again
|
||||||
|
assert_difference "UserPreference.count", -1 do
|
||||||
|
get :delete_one, :params => { :preference_key => "key" }
|
||||||
|
end
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "text/plain", @response.content_type
|
||||||
|
assert_equal "", @response.body
|
||||||
|
assert_raises ActiveRecord::RecordNotFound do
|
||||||
|
UserPreference.find([user.id, "key"])
|
||||||
|
end
|
||||||
|
|
||||||
|
# try the delete again for the same key
|
||||||
|
assert_no_difference "UserPreference.count" do
|
||||||
|
get :delete_one, :params => { :preference_key => "key" }
|
||||||
|
end
|
||||||
|
assert_response :not_found
|
||||||
|
assert_raises ActiveRecord::RecordNotFound do
|
||||||
|
UserPreference.find([user.id, "key"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Ensure that a valid access token with correct capabilities can be used to
|
||||||
|
# read preferences
|
||||||
|
def test_read_one_using_token
|
||||||
|
user = create(:user)
|
||||||
|
token = create(:access_token, :user => user, :allow_read_prefs => true)
|
||||||
|
create(:user_preference, :user => user, :k => "key", :v => "value")
|
||||||
|
|
||||||
|
# Hack together an oauth request - an alternative would be to sign the request properly
|
||||||
|
@request.env["oauth.version"] = 1
|
||||||
|
@request.env["oauth.strategies"] = [:token]
|
||||||
|
@request.env["oauth.token"] = token
|
||||||
|
|
||||||
|
get :read_one, :params => { :preference_key => "key" }
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
# Ensure that a valid access token with incorrect capabilities can't be used
|
||||||
|
# to read preferences even, though the owner of that token could read them
|
||||||
|
# by other methods.
|
||||||
|
def test_read_one_using_token_fail
|
||||||
|
user = create(:user)
|
||||||
|
token = create(:access_token, :user => user, :allow_read_prefs => false)
|
||||||
|
create(:user_preference, :user => user, :k => "key", :v => "value")
|
||||||
|
@request.env["oauth.version"] = 1
|
||||||
|
@request.env["oauth.strategies"] = [:token]
|
||||||
|
@request.env["oauth.token"] = token
|
||||||
|
|
||||||
|
get :read_one, :params => { :preference_key => "key" }
|
||||||
|
assert_response :forbidden
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
199
test/controllers/api/users_controller_test.rb
Normal file
199
test/controllers/api/users_controller_test.rb
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class UsersControllerTest < ActionController::TestCase
|
||||||
|
def setup
|
||||||
|
stub_hostip_requests
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test all routes which lead to this controller
|
||||||
|
def test_routes
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/user/1", :method => :get },
|
||||||
|
{ :controller => "api/users", :action => "api_read", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/user/details", :method => :get },
|
||||||
|
{ :controller => "api/users", :action => "api_details" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/user/gpx_files", :method => :get },
|
||||||
|
{ :controller => "api/users", :action => "api_gpx_files" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/users", :method => :get },
|
||||||
|
{ :controller => "api/users", :action => "api_users" }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_api_read
|
||||||
|
user = create(:user, :description => "test", :terms_agreed => Date.yesterday)
|
||||||
|
# check that a visible user is returned properly
|
||||||
|
get :api_read, :params => { :id => user.id }
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "text/xml", response.content_type
|
||||||
|
|
||||||
|
# check the data that is returned
|
||||||
|
assert_select "description", :count => 1, :text => "test"
|
||||||
|
assert_select "contributor-terms", :count => 1 do
|
||||||
|
assert_select "[agreed='true']"
|
||||||
|
end
|
||||||
|
assert_select "img", :count => 0
|
||||||
|
assert_select "roles", :count => 1 do
|
||||||
|
assert_select "role", :count => 0
|
||||||
|
end
|
||||||
|
assert_select "changesets", :count => 1 do
|
||||||
|
assert_select "[count='0']"
|
||||||
|
end
|
||||||
|
assert_select "traces", :count => 1 do
|
||||||
|
assert_select "[count='0']"
|
||||||
|
end
|
||||||
|
assert_select "blocks", :count => 1 do
|
||||||
|
assert_select "received", :count => 1 do
|
||||||
|
assert_select "[count='0'][active='0']"
|
||||||
|
end
|
||||||
|
assert_select "issued", :count => 0
|
||||||
|
end
|
||||||
|
|
||||||
|
# check that we aren't revealing private information
|
||||||
|
assert_select "contributor-terms[pd]", false
|
||||||
|
assert_select "home", false
|
||||||
|
assert_select "languages", false
|
||||||
|
assert_select "messages", false
|
||||||
|
|
||||||
|
# check that a suspended user is not returned
|
||||||
|
get :api_read, :params => { :id => create(:user, :suspended).id }
|
||||||
|
assert_response :gone
|
||||||
|
|
||||||
|
# check that a deleted user is not returned
|
||||||
|
get :api_read, :params => { :id => create(:user, :deleted).id }
|
||||||
|
assert_response :gone
|
||||||
|
|
||||||
|
# check that a non-existent user is not returned
|
||||||
|
get :api_read, :params => { :id => 0 }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_api_details
|
||||||
|
user = create(:user, :description => "test", :terms_agreed => Date.yesterday, :home_lat => 12.1, :home_lon => 12.1, :languages => ["en"])
|
||||||
|
create(:message, :read, :recipient => user)
|
||||||
|
create(:message, :sender => user)
|
||||||
|
|
||||||
|
# check that nothing is returned when not logged in
|
||||||
|
get :api_details
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
# check that we get a response when logged in
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
get :api_details
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "text/xml", response.content_type
|
||||||
|
|
||||||
|
# check the data that is returned
|
||||||
|
assert_select "description", :count => 1, :text => "test"
|
||||||
|
assert_select "contributor-terms", :count => 1 do
|
||||||
|
assert_select "[agreed='true'][pd='false']"
|
||||||
|
end
|
||||||
|
assert_select "img", :count => 0
|
||||||
|
assert_select "roles", :count => 1 do
|
||||||
|
assert_select "role", :count => 0
|
||||||
|
end
|
||||||
|
assert_select "changesets", :count => 1 do
|
||||||
|
assert_select "[count='0']", :count => 1
|
||||||
|
end
|
||||||
|
assert_select "traces", :count => 1 do
|
||||||
|
assert_select "[count='0']", :count => 1
|
||||||
|
end
|
||||||
|
assert_select "blocks", :count => 1 do
|
||||||
|
assert_select "received", :count => 1 do
|
||||||
|
assert_select "[count='0'][active='0']"
|
||||||
|
end
|
||||||
|
assert_select "issued", :count => 0
|
||||||
|
end
|
||||||
|
assert_select "home", :count => 1 do
|
||||||
|
assert_select "[lat='12.1'][lon='12.1'][zoom='3']"
|
||||||
|
end
|
||||||
|
assert_select "languages", :count => 1 do
|
||||||
|
assert_select "lang", :count => 1, :text => "en"
|
||||||
|
end
|
||||||
|
assert_select "messages", :count => 1 do
|
||||||
|
assert_select "received", :count => 1 do
|
||||||
|
assert_select "[count='1'][unread='0']"
|
||||||
|
end
|
||||||
|
assert_select "sent", :count => 1 do
|
||||||
|
assert_select "[count='1']"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_api_users
|
||||||
|
user1 = create(:user, :description => "test1", :terms_agreed => Date.yesterday)
|
||||||
|
user2 = create(:user, :description => "test2", :terms_agreed => Date.yesterday)
|
||||||
|
user3 = create(:user, :description => "test3", :terms_agreed => Date.yesterday)
|
||||||
|
|
||||||
|
get :api_users, :params => { :users => user1.id }
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "text/xml", response.content_type
|
||||||
|
assert_select "user", :count => 1 do
|
||||||
|
assert_select "user[id='#{user1.id}']", :count => 1
|
||||||
|
assert_select "user[id='#{user2.id}']", :count => 0
|
||||||
|
assert_select "user[id='#{user3.id}']", :count => 0
|
||||||
|
end
|
||||||
|
|
||||||
|
get :api_users, :params => { :users => user2.id }
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "text/xml", response.content_type
|
||||||
|
assert_select "user", :count => 1 do
|
||||||
|
assert_select "user[id='#{user1.id}']", :count => 0
|
||||||
|
assert_select "user[id='#{user2.id}']", :count => 1
|
||||||
|
assert_select "user[id='#{user3.id}']", :count => 0
|
||||||
|
end
|
||||||
|
|
||||||
|
get :api_users, :params => { :users => "#{user1.id},#{user3.id}" }
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "text/xml", response.content_type
|
||||||
|
assert_select "user", :count => 2 do
|
||||||
|
assert_select "user[id='#{user1.id}']", :count => 1
|
||||||
|
assert_select "user[id='#{user2.id}']", :count => 0
|
||||||
|
assert_select "user[id='#{user3.id}']", :count => 1
|
||||||
|
end
|
||||||
|
|
||||||
|
get :api_users, :params => { :users => create(:user, :suspended).id }
|
||||||
|
assert_response :not_found
|
||||||
|
|
||||||
|
get :api_users, :params => { :users => create(:user, :deleted).id }
|
||||||
|
assert_response :not_found
|
||||||
|
|
||||||
|
get :api_users, :params => { :users => 0 }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_api_gpx_files
|
||||||
|
user = create(:user)
|
||||||
|
trace1 = create(:trace, :user => user) do |trace|
|
||||||
|
create(:tracetag, :trace => trace, :tag => "London")
|
||||||
|
end
|
||||||
|
trace2 = create(:trace, :user => user) do |trace|
|
||||||
|
create(:tracetag, :trace => trace, :tag => "Birmingham")
|
||||||
|
end
|
||||||
|
# check that nothing is returned when not logged in
|
||||||
|
get :api_gpx_files
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
# check that we get a response when logged in
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
get :api_gpx_files
|
||||||
|
assert_response :success
|
||||||
|
assert_equal "application/xml", response.content_type
|
||||||
|
|
||||||
|
# check the data that is returned
|
||||||
|
assert_select "gpx_file[id='#{trace1.id}']", 1 do
|
||||||
|
assert_select "tag", "London"
|
||||||
|
end
|
||||||
|
assert_select "gpx_file[id='#{trace2.id}']", 1 do
|
||||||
|
assert_select "tag", "Birmingham"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
754
test/controllers/api/ways_controller_test.rb
Normal file
754
test/controllers/api/ways_controller_test.rb
Normal file
|
@ -0,0 +1,754 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
module Api
|
||||||
|
class WaysControllerTest < ActionController::TestCase
|
||||||
|
##
|
||||||
|
# test all routes which lead to this controller
|
||||||
|
def test_routes
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/way/create", :method => :put },
|
||||||
|
{ :controller => "api/ways", :action => "create" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/way/1/full", :method => :get },
|
||||||
|
{ :controller => "api/ways", :action => "full", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/way/1", :method => :get },
|
||||||
|
{ :controller => "api/ways", :action => "show", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/way/1", :method => :put },
|
||||||
|
{ :controller => "api/ways", :action => "update", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/way/1", :method => :delete },
|
||||||
|
{ :controller => "api/ways", :action => "delete", :id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/api/0.6/ways", :method => :get },
|
||||||
|
{ :controller => "api/ways", :action => "index" }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# -------------------------------------
|
||||||
|
# Test showing ways.
|
||||||
|
# -------------------------------------
|
||||||
|
|
||||||
|
def test_show
|
||||||
|
# check that a visible way is returned properly
|
||||||
|
get :show, :params => { :id => create(:way).id }
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
# check that an invisible way is not returned
|
||||||
|
get :show, :params => { :id => create(:way, :deleted).id }
|
||||||
|
assert_response :gone
|
||||||
|
|
||||||
|
# check chat a non-existent way is not returned
|
||||||
|
get :show, :params => { :id => 0 }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# check the "full" mode
|
||||||
|
def test_full
|
||||||
|
Way.all.each do |way|
|
||||||
|
get :full, :params => { :id => way.id }
|
||||||
|
|
||||||
|
# full call should say "gone" for non-visible ways...
|
||||||
|
unless way.visible
|
||||||
|
assert_response :gone
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
# otherwise it should say success
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
# Check the way is correctly returned
|
||||||
|
assert_select "osm way[id='#{way.id}'][version='#{way.version}'][visible='#{way.visible}']", 1
|
||||||
|
|
||||||
|
# check that each node in the way appears once in the output as a
|
||||||
|
# reference and as the node element.
|
||||||
|
way.nodes.each do |n|
|
||||||
|
count = (way.nodes - (way.nodes - [n])).length
|
||||||
|
assert_select "osm way nd[ref='#{n.id}']", count
|
||||||
|
assert_select "osm node[id='#{n.id}'][version='#{n.version}'][lat='#{format('%.7f', n.lat)}'][lon='#{format('%.7f', n.lon)}']", 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test fetching multiple ways
|
||||||
|
def test_index
|
||||||
|
way1 = create(:way)
|
||||||
|
way2 = create(:way, :deleted)
|
||||||
|
way3 = create(:way)
|
||||||
|
way4 = create(:way)
|
||||||
|
|
||||||
|
# check error when no parameter provided
|
||||||
|
get :index
|
||||||
|
assert_response :bad_request
|
||||||
|
|
||||||
|
# check error when no parameter value provided
|
||||||
|
get :index, :params => { :ways => "" }
|
||||||
|
assert_response :bad_request
|
||||||
|
|
||||||
|
# test a working call
|
||||||
|
get :index, :params => { :ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id}" }
|
||||||
|
assert_response :success
|
||||||
|
assert_select "osm" do
|
||||||
|
assert_select "way", :count => 4
|
||||||
|
assert_select "way[id='#{way1.id}'][visible='true']", :count => 1
|
||||||
|
assert_select "way[id='#{way2.id}'][visible='false']", :count => 1
|
||||||
|
assert_select "way[id='#{way3.id}'][visible='true']", :count => 1
|
||||||
|
assert_select "way[id='#{way4.id}'][visible='true']", :count => 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# check error when a non-existent way is included
|
||||||
|
get :index, :params => { :ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id},0" }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
# -------------------------------------
|
||||||
|
# Test simple way creation.
|
||||||
|
# -------------------------------------
|
||||||
|
|
||||||
|
def test_create
|
||||||
|
node1 = create(:node)
|
||||||
|
node2 = create(:node)
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_changeset = create(:changeset, :user => private_user)
|
||||||
|
user = create(:user)
|
||||||
|
changeset = create(:changeset, :user => user)
|
||||||
|
|
||||||
|
## First check that it fails when creating a way using a non-public user
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
# use the first user's open changeset
|
||||||
|
changeset_id = private_changeset.id
|
||||||
|
|
||||||
|
# create a way with pre-existing nodes
|
||||||
|
xml = "<osm><way changeset='#{changeset_id}'>" \
|
||||||
|
"<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
|
||||||
|
"<tag k='test' v='yes' /></way></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# hope for failure
|
||||||
|
assert_response :forbidden,
|
||||||
|
"way upload did not return forbidden status"
|
||||||
|
|
||||||
|
## Now use a public user
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# use the first user's open changeset
|
||||||
|
changeset_id = changeset.id
|
||||||
|
|
||||||
|
# create a way with pre-existing nodes
|
||||||
|
xml = "<osm><way changeset='#{changeset_id}'>" \
|
||||||
|
"<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
|
||||||
|
"<tag k='test' v='yes' /></way></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# hope for success
|
||||||
|
assert_response :success,
|
||||||
|
"way upload did not return success status"
|
||||||
|
# read id of created way and search for it
|
||||||
|
wayid = @response.body
|
||||||
|
checkway = Way.find(wayid)
|
||||||
|
assert_not_nil checkway,
|
||||||
|
"uploaded way not found in data base after upload"
|
||||||
|
# compare values
|
||||||
|
assert_equal checkway.nds.length, 2,
|
||||||
|
"saved way does not contain exactly one node"
|
||||||
|
assert_equal checkway.nds[0], node1.id,
|
||||||
|
"saved way does not contain the right node on pos 0"
|
||||||
|
assert_equal checkway.nds[1], node2.id,
|
||||||
|
"saved way does not contain the right node on pos 1"
|
||||||
|
assert_equal checkway.changeset_id, changeset_id,
|
||||||
|
"saved way does not belong to the correct changeset"
|
||||||
|
assert_equal user.id, checkway.changeset.user_id,
|
||||||
|
"saved way does not belong to user that created it"
|
||||||
|
assert_equal true, checkway.visible,
|
||||||
|
"saved way is not visible"
|
||||||
|
end
|
||||||
|
|
||||||
|
# -------------------------------------
|
||||||
|
# Test creating some invalid ways.
|
||||||
|
# -------------------------------------
|
||||||
|
|
||||||
|
def test_create_invalid
|
||||||
|
node = create(:node)
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_open_changeset = create(:changeset, :user => private_user)
|
||||||
|
private_closed_changeset = create(:changeset, :closed, :user => private_user)
|
||||||
|
user = create(:user)
|
||||||
|
open_changeset = create(:changeset, :user => user)
|
||||||
|
closed_changeset = create(:changeset, :closed, :user => user)
|
||||||
|
|
||||||
|
## First test with a private user to make sure that they are not authorized
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
# use the first user's open changeset
|
||||||
|
# create a way with non-existing node
|
||||||
|
xml = "<osm><way changeset='#{private_open_changeset.id}'>" \
|
||||||
|
"<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# expect failure
|
||||||
|
assert_response :forbidden,
|
||||||
|
"way upload with invalid node using a private user did not return 'forbidden'"
|
||||||
|
|
||||||
|
# create a way with no nodes
|
||||||
|
xml = "<osm><way changeset='#{private_open_changeset.id}'>" \
|
||||||
|
"<tag k='test' v='yes' /></way></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# expect failure
|
||||||
|
assert_response :forbidden,
|
||||||
|
"way upload with no node using a private userdid not return 'forbidden'"
|
||||||
|
|
||||||
|
# create a way inside a closed changeset
|
||||||
|
xml = "<osm><way changeset='#{private_closed_changeset.id}'>" \
|
||||||
|
"<nd ref='#{node.id}'/></way></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# expect failure
|
||||||
|
assert_response :forbidden,
|
||||||
|
"way upload to closed changeset with a private user did not return 'forbidden'"
|
||||||
|
|
||||||
|
## Now test with a public user
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# use the first user's open changeset
|
||||||
|
# create a way with non-existing node
|
||||||
|
xml = "<osm><way changeset='#{open_changeset.id}'>" \
|
||||||
|
"<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# expect failure
|
||||||
|
assert_response :precondition_failed,
|
||||||
|
"way upload with invalid node did not return 'precondition failed'"
|
||||||
|
assert_equal "Precondition failed: Way requires the nodes with id in (0), which either do not exist, or are not visible.", @response.body
|
||||||
|
|
||||||
|
# create a way with no nodes
|
||||||
|
xml = "<osm><way changeset='#{open_changeset.id}'>" \
|
||||||
|
"<tag k='test' v='yes' /></way></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# expect failure
|
||||||
|
assert_response :precondition_failed,
|
||||||
|
"way upload with no node did not return 'precondition failed'"
|
||||||
|
assert_equal "Precondition failed: Cannot create way: data is invalid.", @response.body
|
||||||
|
|
||||||
|
# create a way inside a closed changeset
|
||||||
|
xml = "<osm><way changeset='#{closed_changeset.id}'>" \
|
||||||
|
"<nd ref='#{node.id}'/></way></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# expect failure
|
||||||
|
assert_response :conflict,
|
||||||
|
"way upload to closed changeset did not return 'conflict'"
|
||||||
|
|
||||||
|
# create a way with a tag which is too long
|
||||||
|
xml = "<osm><way changeset='#{open_changeset.id}'>" \
|
||||||
|
"<nd ref='#{node.id}'/>" \
|
||||||
|
"<tag k='foo' v='#{'x' * 256}'/>" \
|
||||||
|
"</way></osm>"
|
||||||
|
put :create, :body => xml
|
||||||
|
# expect failure
|
||||||
|
assert_response :bad_request,
|
||||||
|
"way upload to with too long tag did not return 'bad_request'"
|
||||||
|
end
|
||||||
|
|
||||||
|
# -------------------------------------
|
||||||
|
# Test deleting ways.
|
||||||
|
# -------------------------------------
|
||||||
|
|
||||||
|
def test_delete
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_open_changeset = create(:changeset, :user => private_user)
|
||||||
|
private_closed_changeset = create(:changeset, :closed, :user => private_user)
|
||||||
|
private_way = create(:way, :changeset => private_open_changeset)
|
||||||
|
private_deleted_way = create(:way, :deleted, :changeset => private_open_changeset)
|
||||||
|
private_used_way = create(:way, :changeset => private_open_changeset)
|
||||||
|
create(:relation_member, :member => private_used_way)
|
||||||
|
user = create(:user)
|
||||||
|
open_changeset = create(:changeset, :user => user)
|
||||||
|
closed_changeset = create(:changeset, :closed, :user => user)
|
||||||
|
way = create(:way, :changeset => open_changeset)
|
||||||
|
deleted_way = create(:way, :deleted, :changeset => open_changeset)
|
||||||
|
used_way = create(:way, :changeset => open_changeset)
|
||||||
|
relation_member = create(:relation_member, :member => used_way)
|
||||||
|
relation = relation_member.relation
|
||||||
|
|
||||||
|
# first try to delete way without auth
|
||||||
|
delete :delete, :params => { :id => way.id }
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
# now set auth using the private user
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
# this shouldn't work as with the 0.6 api we need pay load to delete
|
||||||
|
delete :delete, :params => { :id => private_way.id }
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# Now try without having a changeset
|
||||||
|
xml = "<osm><way id='#{private_way.id}'/></osm>"
|
||||||
|
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# try to delete with an invalid (closed) changeset
|
||||||
|
xml = update_changeset(private_way.to_xml, private_closed_changeset.id)
|
||||||
|
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# try to delete with an invalid (non-existent) changeset
|
||||||
|
xml = update_changeset(private_way.to_xml, 0)
|
||||||
|
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# Now try with a valid changeset
|
||||||
|
xml = private_way.to_xml
|
||||||
|
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# check the returned value - should be the new version number
|
||||||
|
# valid delete should return the new version number, which should
|
||||||
|
# be greater than the old version number
|
||||||
|
# assert @response.body.to_i > current_ways(:visible_way).version,
|
||||||
|
# "delete request should return a new version number for way"
|
||||||
|
|
||||||
|
# this won't work since the way is already deleted
|
||||||
|
xml = private_deleted_way.to_xml
|
||||||
|
delete :delete, :params => { :id => private_deleted_way.id }, :body => xml.to_s
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
# this shouldn't work as the way is used in a relation
|
||||||
|
xml = private_used_way.to_xml
|
||||||
|
delete :delete, :params => { :id => private_used_way.id }, :body => xml.to_s
|
||||||
|
assert_response :forbidden,
|
||||||
|
"shouldn't be able to delete a way used in a relation (#{@response.body}), when done by a private user"
|
||||||
|
|
||||||
|
# this won't work since the way never existed
|
||||||
|
delete :delete, :params => { :id => 0 }
|
||||||
|
assert_response :forbidden
|
||||||
|
|
||||||
|
### Now check with a public user
|
||||||
|
# now set auth
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# this shouldn't work as with the 0.6 api we need pay load to delete
|
||||||
|
delete :delete, :params => { :id => way.id }
|
||||||
|
assert_response :bad_request
|
||||||
|
|
||||||
|
# Now try without having a changeset
|
||||||
|
xml = "<osm><way id='#{way.id}'/></osm>"
|
||||||
|
delete :delete, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :bad_request
|
||||||
|
|
||||||
|
# try to delete with an invalid (closed) changeset
|
||||||
|
xml = update_changeset(way.to_xml, closed_changeset.id)
|
||||||
|
delete :delete, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict
|
||||||
|
|
||||||
|
# try to delete with an invalid (non-existent) changeset
|
||||||
|
xml = update_changeset(way.to_xml, 0)
|
||||||
|
delete :delete, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict
|
||||||
|
|
||||||
|
# Now try with a valid changeset
|
||||||
|
xml = way.to_xml
|
||||||
|
delete :delete, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
# check the returned value - should be the new version number
|
||||||
|
# valid delete should return the new version number, which should
|
||||||
|
# be greater than the old version number
|
||||||
|
assert @response.body.to_i > way.version,
|
||||||
|
"delete request should return a new version number for way"
|
||||||
|
|
||||||
|
# this won't work since the way is already deleted
|
||||||
|
xml = deleted_way.to_xml
|
||||||
|
delete :delete, :params => { :id => deleted_way.id }, :body => xml.to_s
|
||||||
|
assert_response :gone
|
||||||
|
|
||||||
|
# this shouldn't work as the way is used in a relation
|
||||||
|
xml = used_way.to_xml
|
||||||
|
delete :delete, :params => { :id => used_way.id }, :body => xml.to_s
|
||||||
|
assert_response :precondition_failed,
|
||||||
|
"shouldn't be able to delete a way used in a relation (#{@response.body})"
|
||||||
|
assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
|
||||||
|
|
||||||
|
# this won't work since the way never existed
|
||||||
|
delete :delete, :params => { :id => 0 }
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# tests whether the API works and prevents incorrect use while trying
|
||||||
|
# to update ways.
|
||||||
|
def test_update
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_way = create(:way, :changeset => create(:changeset, :user => private_user))
|
||||||
|
user = create(:user)
|
||||||
|
way = create(:way, :changeset => create(:changeset, :user => user))
|
||||||
|
node = create(:node)
|
||||||
|
create(:way_node, :way => private_way, :node => node)
|
||||||
|
create(:way_node, :way => way, :node => node)
|
||||||
|
|
||||||
|
## First test with no user credentials
|
||||||
|
# try and update a way without authorisation
|
||||||
|
xml = way.to_xml
|
||||||
|
put :update, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :unauthorized
|
||||||
|
|
||||||
|
## Second test with the private user
|
||||||
|
|
||||||
|
# setup auth
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
## trying to break changesets
|
||||||
|
|
||||||
|
# try and update in someone else's changeset
|
||||||
|
xml = update_changeset(private_way.to_xml,
|
||||||
|
create(:changeset).id)
|
||||||
|
put :update, :params => { :id => private_way.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "update with other user's changeset should be forbidden when date isn't public"
|
||||||
|
|
||||||
|
# try and update in a closed changeset
|
||||||
|
xml = update_changeset(private_way.to_xml,
|
||||||
|
create(:changeset, :closed, :user => private_user).id)
|
||||||
|
put :update, :params => { :id => private_way.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
|
||||||
|
|
||||||
|
# try and update in a non-existant changeset
|
||||||
|
xml = update_changeset(private_way.to_xml, 0)
|
||||||
|
put :update, :params => { :id => private_way.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data("update with changeset=0 should be forbidden, when data isn't public")
|
||||||
|
|
||||||
|
## try and submit invalid updates
|
||||||
|
xml = xml_replace_node(private_way.to_xml, node.id, 9999)
|
||||||
|
put :update, :params => { :id => private_way.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "way with non-existent node should be forbidden, when data isn't public"
|
||||||
|
|
||||||
|
xml = xml_replace_node(private_way.to_xml, node.id, create(:node, :deleted).id)
|
||||||
|
put :update, :params => { :id => private_way.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "way with deleted node should be forbidden, when data isn't public"
|
||||||
|
|
||||||
|
## finally, produce a good request which will still not work
|
||||||
|
xml = private_way.to_xml
|
||||||
|
put :update, :params => { :id => private_way.id }, :body => xml.to_s
|
||||||
|
assert_require_public_data "should have failed with a forbidden when data isn't public"
|
||||||
|
|
||||||
|
## Finally test with the public user
|
||||||
|
|
||||||
|
# setup auth
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
## trying to break changesets
|
||||||
|
|
||||||
|
# try and update in someone else's changeset
|
||||||
|
xml = update_changeset(way.to_xml,
|
||||||
|
create(:changeset).id)
|
||||||
|
put :update, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict, "update with other user's changeset should be rejected"
|
||||||
|
|
||||||
|
# try and update in a closed changeset
|
||||||
|
xml = update_changeset(way.to_xml,
|
||||||
|
create(:changeset, :closed, :user => user).id)
|
||||||
|
put :update, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict, "update with closed changeset should be rejected"
|
||||||
|
|
||||||
|
# try and update in a non-existant changeset
|
||||||
|
xml = update_changeset(way.to_xml, 0)
|
||||||
|
put :update, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict, "update with changeset=0 should be rejected"
|
||||||
|
|
||||||
|
## try and submit invalid updates
|
||||||
|
xml = xml_replace_node(way.to_xml, node.id, 9999)
|
||||||
|
put :update, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :precondition_failed, "way with non-existent node should be rejected"
|
||||||
|
|
||||||
|
xml = xml_replace_node(way.to_xml, node.id, create(:node, :deleted).id)
|
||||||
|
put :update, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :precondition_failed, "way with deleted node should be rejected"
|
||||||
|
|
||||||
|
## next, attack the versioning
|
||||||
|
current_way_version = way.version
|
||||||
|
|
||||||
|
# try and submit a version behind
|
||||||
|
xml = xml_attr_rewrite(way.to_xml,
|
||||||
|
"version", current_way_version - 1)
|
||||||
|
put :update, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict, "should have failed on old version number"
|
||||||
|
|
||||||
|
# try and submit a version ahead
|
||||||
|
xml = xml_attr_rewrite(way.to_xml,
|
||||||
|
"version", current_way_version + 1)
|
||||||
|
put :update, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict, "should have failed on skipped version number"
|
||||||
|
|
||||||
|
# try and submit total crap in the version field
|
||||||
|
xml = xml_attr_rewrite(way.to_xml,
|
||||||
|
"version", "p1r4t3s!")
|
||||||
|
put :update, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :conflict,
|
||||||
|
"should not be able to put 'p1r4at3s!' in the version field"
|
||||||
|
|
||||||
|
## try an update with the wrong ID
|
||||||
|
xml = create(:way).to_xml
|
||||||
|
put :update, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :bad_request,
|
||||||
|
"should not be able to update a way with a different ID from the XML"
|
||||||
|
|
||||||
|
## try an update with a minimal valid XML doc which isn't a well-formed OSM doc.
|
||||||
|
xml = "<update/>"
|
||||||
|
put :update, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :bad_request,
|
||||||
|
"should not be able to update a way with non-OSM XML doc."
|
||||||
|
|
||||||
|
## finally, produce a good request which should work
|
||||||
|
xml = way.to_xml
|
||||||
|
put :update, :params => { :id => way.id }, :body => xml.to_s
|
||||||
|
assert_response :success, "a valid update request failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# test tags handling
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
##
|
||||||
|
# Try adding a new tag to a way
|
||||||
|
def test_add_tags
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => private_user))
|
||||||
|
user = create(:user)
|
||||||
|
way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => user))
|
||||||
|
|
||||||
|
## Try with the non-public user
|
||||||
|
# setup auth
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
# add an identical tag to the way
|
||||||
|
tag_xml = XML::Node.new("tag")
|
||||||
|
tag_xml["k"] = "new"
|
||||||
|
tag_xml["v"] = "yes"
|
||||||
|
|
||||||
|
# add the tag into the existing xml
|
||||||
|
way_xml = private_way.to_xml
|
||||||
|
way_xml.find("//osm/way").first << tag_xml
|
||||||
|
|
||||||
|
# try and upload it
|
||||||
|
put :update, :params => { :id => private_way.id }, :body => way_xml.to_s
|
||||||
|
assert_response :forbidden,
|
||||||
|
"adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
|
||||||
|
|
||||||
|
## Now try with the public user
|
||||||
|
# setup auth
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# add an identical tag to the way
|
||||||
|
tag_xml = XML::Node.new("tag")
|
||||||
|
tag_xml["k"] = "new"
|
||||||
|
tag_xml["v"] = "yes"
|
||||||
|
|
||||||
|
# add the tag into the existing xml
|
||||||
|
way_xml = way.to_xml
|
||||||
|
way_xml.find("//osm/way").first << tag_xml
|
||||||
|
|
||||||
|
# try and upload it
|
||||||
|
put :update, :params => { :id => way.id }, :body => way_xml.to_s
|
||||||
|
assert_response :success,
|
||||||
|
"adding a new tag to a way should succeed"
|
||||||
|
assert_equal way.version + 1, @response.body.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Try adding a duplicate of an existing tag to a way
|
||||||
|
def test_add_duplicate_tags
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_way = create(:way, :changeset => create(:changeset, :user => private_user))
|
||||||
|
private_existing_tag = create(:way_tag, :way => private_way)
|
||||||
|
user = create(:user)
|
||||||
|
way = create(:way, :changeset => create(:changeset, :user => user))
|
||||||
|
existing_tag = create(:way_tag, :way => way)
|
||||||
|
|
||||||
|
## Try with the non-public user
|
||||||
|
# setup auth
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
# add an identical tag to the way
|
||||||
|
tag_xml = XML::Node.new("tag")
|
||||||
|
tag_xml["k"] = private_existing_tag.k
|
||||||
|
tag_xml["v"] = private_existing_tag.v
|
||||||
|
|
||||||
|
# add the tag into the existing xml
|
||||||
|
way_xml = private_way.to_xml
|
||||||
|
way_xml.find("//osm/way").first << tag_xml
|
||||||
|
|
||||||
|
# try and upload it
|
||||||
|
put :update, :params => { :id => private_way.id }, :body => way_xml.to_s
|
||||||
|
assert_response :forbidden,
|
||||||
|
"adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
|
||||||
|
|
||||||
|
## Now try with the public user
|
||||||
|
# setup auth
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# add an identical tag to the way
|
||||||
|
tag_xml = XML::Node.new("tag")
|
||||||
|
tag_xml["k"] = existing_tag.k
|
||||||
|
tag_xml["v"] = existing_tag.v
|
||||||
|
|
||||||
|
# add the tag into the existing xml
|
||||||
|
way_xml = way.to_xml
|
||||||
|
way_xml.find("//osm/way").first << tag_xml
|
||||||
|
|
||||||
|
# try and upload it
|
||||||
|
put :update, :params => { :id => way.id }, :body => way_xml.to_s
|
||||||
|
assert_response :bad_request,
|
||||||
|
"adding a duplicate tag to a way should fail with 'bad request'"
|
||||||
|
assert_equal "Element way/#{way.id} has duplicate tags with key #{existing_tag.k}", @response.body
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Try adding a new duplicate tags to a way
|
||||||
|
def test_new_duplicate_tags
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_way = create(:way, :changeset => create(:changeset, :user => private_user))
|
||||||
|
user = create(:user)
|
||||||
|
way = create(:way, :changeset => create(:changeset, :user => user))
|
||||||
|
|
||||||
|
## First test with the non-public user so should be rejected
|
||||||
|
# setup auth
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
# create duplicate tag
|
||||||
|
tag_xml = XML::Node.new("tag")
|
||||||
|
tag_xml["k"] = "i_am_a_duplicate"
|
||||||
|
tag_xml["v"] = "foobar"
|
||||||
|
|
||||||
|
# add the tag into the existing xml
|
||||||
|
way_xml = private_way.to_xml
|
||||||
|
|
||||||
|
# add two copies of the tag
|
||||||
|
way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
|
||||||
|
|
||||||
|
# try and upload it
|
||||||
|
put :update, :params => { :id => private_way.id }, :body => way_xml.to_s
|
||||||
|
assert_response :forbidden,
|
||||||
|
"adding new duplicate tags to a way using a non-public user should fail with 'forbidden'"
|
||||||
|
|
||||||
|
## Now test with the public user
|
||||||
|
# setup auth
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# create duplicate tag
|
||||||
|
tag_xml = XML::Node.new("tag")
|
||||||
|
tag_xml["k"] = "i_am_a_duplicate"
|
||||||
|
tag_xml["v"] = "foobar"
|
||||||
|
|
||||||
|
# add the tag into the existing xml
|
||||||
|
way_xml = way.to_xml
|
||||||
|
|
||||||
|
# add two copies of the tag
|
||||||
|
way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
|
||||||
|
|
||||||
|
# try and upload it
|
||||||
|
put :update, :params => { :id => way.id }, :body => way_xml.to_s
|
||||||
|
assert_response :bad_request,
|
||||||
|
"adding new duplicate tags to a way should fail with 'bad request'"
|
||||||
|
assert_equal "Element way/#{way.id} has duplicate tags with key i_am_a_duplicate", @response.body
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Try adding a new duplicate tags to a way.
|
||||||
|
# But be a bit subtle - use unicode decoding ambiguities to use different
|
||||||
|
# binary strings which have the same decoding.
|
||||||
|
def test_invalid_duplicate_tags
|
||||||
|
private_user = create(:user, :data_public => false)
|
||||||
|
private_changeset = create(:changeset, :user => private_user)
|
||||||
|
user = create(:user)
|
||||||
|
changeset = create(:changeset, :user => user)
|
||||||
|
|
||||||
|
## First make sure that you can't with a non-public user
|
||||||
|
# setup auth
|
||||||
|
basic_authorization private_user.email, "test"
|
||||||
|
|
||||||
|
# add the tag into the existing xml
|
||||||
|
way_str = "<osm><way changeset='#{private_changeset.id}'>"
|
||||||
|
way_str << "<tag k='addr:housenumber' v='1'/>"
|
||||||
|
way_str << "<tag k='addr:housenumber' v='2'/>"
|
||||||
|
way_str << "</way></osm>"
|
||||||
|
|
||||||
|
# try and upload it
|
||||||
|
put :create, :body => way_str
|
||||||
|
assert_response :forbidden,
|
||||||
|
"adding new duplicate tags to a way with a non-public user should fail with 'forbidden'"
|
||||||
|
|
||||||
|
## Now do it with a public user
|
||||||
|
# setup auth
|
||||||
|
basic_authorization user.email, "test"
|
||||||
|
|
||||||
|
# add the tag into the existing xml
|
||||||
|
way_str = "<osm><way changeset='#{changeset.id}'>"
|
||||||
|
way_str << "<tag k='addr:housenumber' v='1'/>"
|
||||||
|
way_str << "<tag k='addr:housenumber' v='2'/>"
|
||||||
|
way_str << "</way></osm>"
|
||||||
|
|
||||||
|
# try and upload it
|
||||||
|
put :create, :body => way_str
|
||||||
|
assert_response :bad_request,
|
||||||
|
"adding new duplicate tags to a way should fail with 'bad request'"
|
||||||
|
assert_equal "Element way/ has duplicate tags with key addr:housenumber", @response.body
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# test that a call to ways_for_node returns all ways that contain the node
|
||||||
|
# and none that don't.
|
||||||
|
def test_ways_for_node
|
||||||
|
node = create(:node)
|
||||||
|
way1 = create(:way)
|
||||||
|
way2 = create(:way)
|
||||||
|
create(:way_node, :way => way1, :node => node)
|
||||||
|
create(:way_node, :way => way2, :node => node)
|
||||||
|
# create an unrelated way
|
||||||
|
create(:way_with_nodes, :nodes_count => 2)
|
||||||
|
# create a way which used to use the node
|
||||||
|
way3_v1 = create(:old_way, :version => 1)
|
||||||
|
_way3_v2 = create(:old_way, :current_way => way3_v1.current_way, :version => 2)
|
||||||
|
create(:old_way_node, :old_way => way3_v1, :node => node)
|
||||||
|
|
||||||
|
get :ways_for_node, :params => { :id => node.id }
|
||||||
|
assert_response :success
|
||||||
|
ways_xml = XML::Parser.string(@response.body).parse
|
||||||
|
assert_not_nil ways_xml, "failed to parse ways_for_node response"
|
||||||
|
|
||||||
|
# check that the set of IDs match expectations
|
||||||
|
expected_way_ids = [way1.id,
|
||||||
|
way2.id]
|
||||||
|
found_way_ids = ways_xml.find("//osm/way").collect { |w| w["id"].to_i }
|
||||||
|
assert_equal expected_way_ids.sort, found_way_ids.sort,
|
||||||
|
"expected ways for node #{node.id} did not match found"
|
||||||
|
|
||||||
|
# check the full ways to ensure we're not missing anything
|
||||||
|
expected_way_ids.each do |id|
|
||||||
|
way_xml = ways_xml.find("//osm/way[@id='#{id}']").first
|
||||||
|
assert_ways_are_equal(Way.find(id),
|
||||||
|
Way.from_xml_node(way_xml))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# update the changeset_id of a way element
|
||||||
|
def update_changeset(xml, changeset_id)
|
||||||
|
xml_attr_rewrite(xml, "changeset", changeset_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# update an attribute in the way element
|
||||||
|
def xml_attr_rewrite(xml, name, value)
|
||||||
|
xml.find("//osm/way").first[name] = value.to_s
|
||||||
|
xml
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# replace a node in a way element
|
||||||
|
def xml_replace_node(xml, old_node, new_node)
|
||||||
|
xml.find("//osm/way/nd[@ref='#{old_node}']").first["ref"] = new_node.to_s
|
||||||
|
xml
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,18 +4,6 @@ class ChangesetCommentsControllerTest < ActionController::TestCase
|
||||||
##
|
##
|
||||||
# test all routes which lead to this controller
|
# test all routes which lead to this controller
|
||||||
def test_routes
|
def test_routes
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/changeset/1/comment", :method => :post },
|
|
||||||
{ :controller => "changeset_comments", :action => "create", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/changeset/comment/1/hide", :method => :post },
|
|
||||||
{ :controller => "changeset_comments", :action => "destroy", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/changeset/comment/1/unhide", :method => :post },
|
|
||||||
{ :controller => "changeset_comments", :action => "restore", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
assert_routing(
|
||||||
{ :path => "/changeset/1/comments/feed", :method => :get },
|
{ :path => "/changeset/1/comments/feed", :method => :get },
|
||||||
{ :controller => "changeset_comments", :action => "index", :id => "1", :format => "rss" }
|
{ :controller => "changeset_comments", :action => "index", :id => "1", :format => "rss" }
|
||||||
|
@ -26,185 +14,6 @@ class ChangesetCommentsControllerTest < ActionController::TestCase
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
|
||||||
# create comment success
|
|
||||||
def test_create_comment_success
|
|
||||||
user = create(:user)
|
|
||||||
user2 = create(:user)
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
suspended_user = create(:user, :suspended)
|
|
||||||
deleted_user = create(:user, :deleted)
|
|
||||||
private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
|
|
||||||
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
assert_difference "ChangesetComment.count", 1 do
|
|
||||||
assert_no_difference "ActionMailer::Base.deliveries.size" do
|
|
||||||
perform_enqueued_jobs do
|
|
||||||
post :create, :params => { :id => private_user_closed_changeset.id, :text => "This is a comment" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
changeset = create(:changeset, :closed, :user => private_user)
|
|
||||||
changeset.subscribers.push(private_user)
|
|
||||||
changeset.subscribers.push(user)
|
|
||||||
changeset.subscribers.push(suspended_user)
|
|
||||||
changeset.subscribers.push(deleted_user)
|
|
||||||
|
|
||||||
assert_difference "ChangesetComment.count", 1 do
|
|
||||||
assert_difference "ActionMailer::Base.deliveries.size", 1 do
|
|
||||||
perform_enqueued_jobs do
|
|
||||||
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
email = ActionMailer::Base.deliveries.first
|
|
||||||
assert_equal 1, email.to.length
|
|
||||||
assert_equal "[OpenStreetMap] #{user.display_name} has commented on one of your changesets", email.subject
|
|
||||||
assert_equal private_user.email, email.to.first
|
|
||||||
|
|
||||||
ActionMailer::Base.deliveries.clear
|
|
||||||
|
|
||||||
basic_authorization user2.email, "test"
|
|
||||||
|
|
||||||
assert_difference "ChangesetComment.count", 1 do
|
|
||||||
assert_difference "ActionMailer::Base.deliveries.size", 2 do
|
|
||||||
perform_enqueued_jobs do
|
|
||||||
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
email = ActionMailer::Base.deliveries.find { |e| e.to.first == private_user.email }
|
|
||||||
assert_not_nil email
|
|
||||||
assert_equal 1, email.to.length
|
|
||||||
assert_equal "[OpenStreetMap] #{user2.display_name} has commented on one of your changesets", email.subject
|
|
||||||
|
|
||||||
email = ActionMailer::Base.deliveries.find { |e| e.to.first == user.email }
|
|
||||||
assert_not_nil email
|
|
||||||
assert_equal 1, email.to.length
|
|
||||||
assert_equal "[OpenStreetMap] #{user2.display_name} has commented on a changeset you are interested in", email.subject
|
|
||||||
|
|
||||||
ActionMailer::Base.deliveries.clear
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# create comment fail
|
|
||||||
def test_create_comment_fail
|
|
||||||
# unauthorized
|
|
||||||
post :create, :params => { :id => create(:changeset, :closed).id, :text => "This is a comment" }
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
# bad changeset id
|
|
||||||
assert_no_difference "ChangesetComment.count" do
|
|
||||||
post :create, :params => { :id => 999111, :text => "This is a comment" }
|
|
||||||
end
|
|
||||||
assert_response :not_found
|
|
||||||
|
|
||||||
# not closed changeset
|
|
||||||
assert_no_difference "ChangesetComment.count" do
|
|
||||||
post :create, :params => { :id => create(:changeset).id, :text => "This is a comment" }
|
|
||||||
end
|
|
||||||
assert_response :conflict
|
|
||||||
|
|
||||||
# no text
|
|
||||||
assert_no_difference "ChangesetComment.count" do
|
|
||||||
post :create, :params => { :id => create(:changeset, :closed).id }
|
|
||||||
end
|
|
||||||
assert_response :bad_request
|
|
||||||
|
|
||||||
# empty text
|
|
||||||
assert_no_difference "ChangesetComment.count" do
|
|
||||||
post :create, :params => { :id => create(:changeset, :closed).id, :text => "" }
|
|
||||||
end
|
|
||||||
assert_response :bad_request
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test hide comment fail
|
|
||||||
def test_destroy_comment_fail
|
|
||||||
# unauthorized
|
|
||||||
comment = create(:changeset_comment)
|
|
||||||
assert_equal true, comment.visible
|
|
||||||
|
|
||||||
post :destroy, :params => { :id => comment.id }
|
|
||||||
assert_response :unauthorized
|
|
||||||
assert_equal true, comment.reload.visible
|
|
||||||
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
# not a moderator
|
|
||||||
post :destroy, :params => { :id => comment.id }
|
|
||||||
assert_response :forbidden
|
|
||||||
assert_equal true, comment.reload.visible
|
|
||||||
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
# bad comment id
|
|
||||||
post :destroy, :params => { :id => 999111 }
|
|
||||||
assert_response :not_found
|
|
||||||
assert_equal true, comment.reload.visible
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test hide comment succes
|
|
||||||
def test_hide_comment_success
|
|
||||||
comment = create(:changeset_comment)
|
|
||||||
assert_equal true, comment.visible
|
|
||||||
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
post :destroy, :params => { :id => comment.id }
|
|
||||||
assert_response :success
|
|
||||||
assert_equal false, comment.reload.visible
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test unhide comment fail
|
|
||||||
def test_restore_comment_fail
|
|
||||||
# unauthorized
|
|
||||||
comment = create(:changeset_comment, :visible => false)
|
|
||||||
assert_equal false, comment.visible
|
|
||||||
|
|
||||||
post :restore, :params => { :id => comment.id }
|
|
||||||
assert_response :unauthorized
|
|
||||||
assert_equal false, comment.reload.visible
|
|
||||||
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
# not a moderator
|
|
||||||
post :restore, :params => { :id => comment.id }
|
|
||||||
assert_response :forbidden
|
|
||||||
assert_equal false, comment.reload.visible
|
|
||||||
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
# bad comment id
|
|
||||||
post :restore, :params => { :id => 999111 }
|
|
||||||
assert_response :not_found
|
|
||||||
assert_equal false, comment.reload.visible
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test unhide comment succes
|
|
||||||
def test_unhide_comment_success
|
|
||||||
comment = create(:changeset_comment, :visible => false)
|
|
||||||
assert_equal false, comment.visible
|
|
||||||
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
post :restore, :params => { :id => comment.id }
|
|
||||||
assert_response :success
|
|
||||||
assert_equal true, comment.reload.visible
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# test comments feed
|
# test comments feed
|
||||||
def test_feed
|
def test_feed
|
||||||
|
@ -248,57 +57,4 @@ class ChangesetCommentsControllerTest < ActionController::TestCase
|
||||||
get :index, :params => { :format => "rss", :limit => 100001 }
|
get :index, :params => { :format => "rss", :limit => 100001 }
|
||||||
assert_response :bad_request
|
assert_response :bad_request
|
||||||
end
|
end
|
||||||
|
|
||||||
# This test ensures that token capabilities behave correctly for a method that
|
|
||||||
# requires the terms to have been agreed.
|
|
||||||
# (This would be better as an integration or system testcase, since the changeset_comment
|
|
||||||
# create method is simply a stand-in for any method that requires terms agreement.
|
|
||||||
# But writing oauth tests is hard, and so it's easier to put in a controller test.)
|
|
||||||
def test_api_write_and_terms_agreed_via_token
|
|
||||||
user = create(:user, :terms_agreed => nil)
|
|
||||||
token = create(:access_token, :user => user, :allow_write_api => true)
|
|
||||||
changeset = create(:changeset, :closed)
|
|
||||||
|
|
||||||
# Hack together an oauth request - an alternative would be to sign the request properly
|
|
||||||
@request.env["oauth.version"] = 1
|
|
||||||
@request.env["oauth.strategies"] = [:token]
|
|
||||||
@request.env["oauth.token"] = token
|
|
||||||
|
|
||||||
assert_difference "ChangesetComment.count", 0 do
|
|
||||||
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
|
|
||||||
end
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# Try again, after agreement with the terms
|
|
||||||
user.terms_agreed = Time.now
|
|
||||||
user.save!
|
|
||||||
|
|
||||||
assert_difference "ChangesetComment.count", 1 do
|
|
||||||
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
|
|
||||||
end
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
|
|
||||||
# This test does the same as above, but with basic auth, to similarly test that the
|
|
||||||
# abilities take into account terms agreement too.
|
|
||||||
def test_api_write_and_terms_agreed_via_basic_auth
|
|
||||||
user = create(:user, :terms_agreed => nil)
|
|
||||||
changeset = create(:changeset, :closed)
|
|
||||||
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
assert_difference "ChangesetComment.count", 0 do
|
|
||||||
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
|
|
||||||
end
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# Try again, after agreement with the terms
|
|
||||||
user.terms_agreed = Time.now
|
|
||||||
user.save!
|
|
||||||
|
|
||||||
assert_difference "ChangesetComment.count", 1 do
|
|
||||||
post :create, :params => { :id => changeset.id, :text => "This is a comment" }
|
|
||||||
end
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,550 +0,0 @@
|
||||||
require "test_helper"
|
|
||||||
|
|
||||||
class NodesControllerTest < ActionController::TestCase
|
|
||||||
##
|
|
||||||
# test all routes which lead to this controller
|
|
||||||
def test_routes
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/node/create", :method => :put },
|
|
||||||
{ :controller => "nodes", :action => "create" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/node/1", :method => :get },
|
|
||||||
{ :controller => "nodes", :action => "show", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/node/1", :method => :put },
|
|
||||||
{ :controller => "nodes", :action => "update", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/node/1", :method => :delete },
|
|
||||||
{ :controller => "nodes", :action => "delete", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/nodes", :method => :get },
|
|
||||||
{ :controller => "nodes", :action => "index" }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_create
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_changeset = create(:changeset, :user => private_user)
|
|
||||||
user = create(:user)
|
|
||||||
changeset = create(:changeset, :user => user)
|
|
||||||
|
|
||||||
# create a node with random lat/lon
|
|
||||||
lat = rand(-50..50) + rand
|
|
||||||
lon = rand(-50..50) + rand
|
|
||||||
|
|
||||||
## First try with no auth
|
|
||||||
# create a minimal xml file
|
|
||||||
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
|
|
||||||
assert_difference("OldNode.count", 0) do
|
|
||||||
put :create, :body => xml
|
|
||||||
end
|
|
||||||
# hope for unauthorized
|
|
||||||
assert_response :unauthorized, "node upload did not return unauthorized status"
|
|
||||||
|
|
||||||
## Now try with the user which doesn't have their data public
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
# create a minimal xml file
|
|
||||||
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{private_changeset.id}'/></osm>"
|
|
||||||
assert_difference("Node.count", 0) do
|
|
||||||
put :create, :body => xml
|
|
||||||
end
|
|
||||||
# hope for success
|
|
||||||
assert_require_public_data "node create did not return forbidden status"
|
|
||||||
|
|
||||||
## Now try with the user that has the public data
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# create a minimal xml file
|
|
||||||
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# hope for success
|
|
||||||
assert_response :success, "node upload did not return success status"
|
|
||||||
|
|
||||||
# read id of created node and search for it
|
|
||||||
nodeid = @response.body
|
|
||||||
checknode = Node.find(nodeid)
|
|
||||||
assert_not_nil checknode, "uploaded node not found in data base after upload"
|
|
||||||
# compare values
|
|
||||||
assert_in_delta lat * 10000000, checknode.latitude, 1, "saved node does not match requested latitude"
|
|
||||||
assert_in_delta lon * 10000000, checknode.longitude, 1, "saved node does not match requested longitude"
|
|
||||||
assert_equal changeset.id, checknode.changeset_id, "saved node does not belong to changeset that it was created in"
|
|
||||||
assert_equal true, checknode.visible, "saved node is not visible"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_create_invalid_xml
|
|
||||||
## Only test public user here, as test_create should cover what's the forbiddens
|
|
||||||
## that would occur here
|
|
||||||
|
|
||||||
user = create(:user)
|
|
||||||
changeset = create(:changeset, :user => user)
|
|
||||||
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
lat = 3.434
|
|
||||||
lon = 3.23
|
|
||||||
|
|
||||||
# test that the upload is rejected when xml is valid, but osm doc isn't
|
|
||||||
xml = "<create/>"
|
|
||||||
put :create, :body => xml
|
|
||||||
assert_response :bad_request, "node upload did not return bad_request status"
|
|
||||||
assert_equal "Cannot parse valid node from xml string <create/>. XML doesn't contain an osm/node element.", @response.body
|
|
||||||
|
|
||||||
# test that the upload is rejected when no lat is supplied
|
|
||||||
# create a minimal xml file
|
|
||||||
xml = "<osm><node lon='#{lon}' changeset='#{changeset.id}'/></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# hope for success
|
|
||||||
assert_response :bad_request, "node upload did not return bad_request status"
|
|
||||||
assert_equal "Cannot parse valid node from xml string <node lon=\"3.23\" changeset=\"#{changeset.id}\"/>. lat missing", @response.body
|
|
||||||
|
|
||||||
# test that the upload is rejected when no lon is supplied
|
|
||||||
# create a minimal xml file
|
|
||||||
xml = "<osm><node lat='#{lat}' changeset='#{changeset.id}'/></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# hope for success
|
|
||||||
assert_response :bad_request, "node upload did not return bad_request status"
|
|
||||||
assert_equal "Cannot parse valid node from xml string <node lat=\"3.434\" changeset=\"#{changeset.id}\"/>. lon missing", @response.body
|
|
||||||
|
|
||||||
# test that the upload is rejected when lat is non-numeric
|
|
||||||
# create a minimal xml file
|
|
||||||
xml = "<osm><node lat='abc' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# hope for success
|
|
||||||
assert_response :bad_request, "node upload did not return bad_request status"
|
|
||||||
assert_equal "Cannot parse valid node from xml string <node lat=\"abc\" lon=\"#{lon}\" changeset=\"#{changeset.id}\"/>. lat not a number", @response.body
|
|
||||||
|
|
||||||
# test that the upload is rejected when lon is non-numeric
|
|
||||||
# create a minimal xml file
|
|
||||||
xml = "<osm><node lat='#{lat}' lon='abc' changeset='#{changeset.id}'/></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# hope for success
|
|
||||||
assert_response :bad_request, "node upload did not return bad_request status"
|
|
||||||
assert_equal "Cannot parse valid node from xml string <node lat=\"#{lat}\" lon=\"abc\" changeset=\"#{changeset.id}\"/>. lon not a number", @response.body
|
|
||||||
|
|
||||||
# test that the upload is rejected when we have a tag which is too long
|
|
||||||
xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'><tag k='foo' v='#{'x' * 256}'/></node></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
assert_response :bad_request, "node upload did not return bad_request status"
|
|
||||||
assert_equal ["NodeTag ", " v: is too long (maximum is 255 characters) (\"#{'x' * 256}\")"], @response.body.split(/[0-9]+,foo:/)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_show
|
|
||||||
# check that a visible node is returned properly
|
|
||||||
get :show, :params => { :id => create(:node).id }
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# check that an deleted node is not returned
|
|
||||||
get :show, :params => { :id => create(:node, :deleted).id }
|
|
||||||
assert_response :gone
|
|
||||||
|
|
||||||
# check chat a non-existent node is not returned
|
|
||||||
get :show, :params => { :id => 0 }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
# this tests deletion restrictions - basic deletion is tested in the unit
|
|
||||||
# tests for node!
|
|
||||||
def test_delete
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_user_changeset = create(:changeset, :user => private_user)
|
|
||||||
private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
|
|
||||||
private_node = create(:node, :changeset => private_user_changeset)
|
|
||||||
private_deleted_node = create(:node, :deleted, :changeset => private_user_changeset)
|
|
||||||
|
|
||||||
## first try to delete node without auth
|
|
||||||
delete :delete, :params => { :id => private_node.id }
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
## now set auth for the non-data public user
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
# try to delete with an invalid (closed) changeset
|
|
||||||
xml = update_changeset(private_node.to_xml, private_user_closed_changeset.id)
|
|
||||||
delete :delete, :params => { :id => private_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data("non-public user shouldn't be able to delete node")
|
|
||||||
|
|
||||||
# try to delete with an invalid (non-existent) changeset
|
|
||||||
xml = update_changeset(private_node.to_xml, 0)
|
|
||||||
delete :delete, :params => { :id => private_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data("shouldn't be able to delete node, when user's data is private")
|
|
||||||
|
|
||||||
# valid delete now takes a payload
|
|
||||||
xml = private_node.to_xml
|
|
||||||
delete :delete, :params => { :id => private_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data("shouldn't be able to delete node when user's data isn't public'")
|
|
||||||
|
|
||||||
# this won't work since the node is already deleted
|
|
||||||
xml = private_deleted_node.to_xml
|
|
||||||
delete :delete, :params => { :id => private_deleted_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data
|
|
||||||
|
|
||||||
# this won't work since the node never existed
|
|
||||||
delete :delete, :params => { :id => 0 }
|
|
||||||
assert_require_public_data
|
|
||||||
|
|
||||||
## these test whether nodes which are in-use can be deleted:
|
|
||||||
# in a way...
|
|
||||||
private_used_node = create(:node, :changeset => private_user_changeset)
|
|
||||||
create(:way_node, :node => private_used_node)
|
|
||||||
|
|
||||||
xml = private_used_node.to_xml
|
|
||||||
delete :delete, :params => { :id => private_used_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "shouldn't be able to delete a node used in a way (#{@response.body})"
|
|
||||||
|
|
||||||
# in a relation...
|
|
||||||
private_used_node2 = create(:node, :changeset => private_user_changeset)
|
|
||||||
create(:relation_member, :member => private_used_node2)
|
|
||||||
|
|
||||||
xml = private_used_node2.to_xml
|
|
||||||
delete :delete, :params => { :id => private_used_node2.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "shouldn't be able to delete a node used in a relation (#{@response.body})"
|
|
||||||
|
|
||||||
## now setup for the public data user
|
|
||||||
user = create(:user, :data_public => true)
|
|
||||||
changeset = create(:changeset, :user => user)
|
|
||||||
closed_changeset = create(:changeset, :closed, :user => user)
|
|
||||||
node = create(:node, :changeset => changeset)
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# try to delete with an invalid (closed) changeset
|
|
||||||
xml = update_changeset(node.to_xml, closed_changeset.id)
|
|
||||||
delete :delete, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict
|
|
||||||
|
|
||||||
# try to delete with an invalid (non-existent) changeset
|
|
||||||
xml = update_changeset(node.to_xml, 0)
|
|
||||||
delete :delete, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict
|
|
||||||
|
|
||||||
# try to delete a node with a different ID
|
|
||||||
other_node = create(:node)
|
|
||||||
xml = other_node.to_xml
|
|
||||||
delete :delete, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :bad_request,
|
|
||||||
"should not be able to delete a node with a different ID from the XML"
|
|
||||||
|
|
||||||
# try to delete a node rubbish in the payloads
|
|
||||||
xml = "<delete/>"
|
|
||||||
delete :delete, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :bad_request,
|
|
||||||
"should not be able to delete a node without a valid XML payload"
|
|
||||||
|
|
||||||
# valid delete now takes a payload
|
|
||||||
xml = node.to_xml
|
|
||||||
delete :delete, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# valid delete should return the new version number, which should
|
|
||||||
# be greater than the old version number
|
|
||||||
assert @response.body.to_i > node.version,
|
|
||||||
"delete request should return a new version number for node"
|
|
||||||
|
|
||||||
# deleting the same node twice doesn't work
|
|
||||||
xml = node.to_xml
|
|
||||||
delete :delete, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :gone
|
|
||||||
|
|
||||||
# this won't work since the node never existed
|
|
||||||
delete :delete, :params => { :id => 0 }
|
|
||||||
assert_response :not_found
|
|
||||||
|
|
||||||
## these test whether nodes which are in-use can be deleted:
|
|
||||||
# in a way...
|
|
||||||
used_node = create(:node, :changeset => create(:changeset, :user => user))
|
|
||||||
way_node = create(:way_node, :node => used_node)
|
|
||||||
way_node2 = create(:way_node, :node => used_node)
|
|
||||||
|
|
||||||
xml = used_node.to_xml
|
|
||||||
delete :delete, :params => { :id => used_node.id }, :body => xml.to_s
|
|
||||||
assert_response :precondition_failed,
|
|
||||||
"shouldn't be able to delete a node used in a way (#{@response.body})"
|
|
||||||
assert_equal "Precondition failed: Node #{used_node.id} is still used by ways #{way_node.way.id},#{way_node2.way.id}.", @response.body
|
|
||||||
|
|
||||||
# in a relation...
|
|
||||||
used_node2 = create(:node, :changeset => create(:changeset, :user => user))
|
|
||||||
relation_member = create(:relation_member, :member => used_node2)
|
|
||||||
relation_member2 = create(:relation_member, :member => used_node2)
|
|
||||||
|
|
||||||
xml = used_node2.to_xml
|
|
||||||
delete :delete, :params => { :id => used_node2.id }, :body => xml.to_s
|
|
||||||
assert_response :precondition_failed,
|
|
||||||
"shouldn't be able to delete a node used in a relation (#{@response.body})"
|
|
||||||
assert_equal "Precondition failed: Node #{used_node2.id} is still used by relations #{relation_member.relation.id},#{relation_member2.relation.id}.", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# tests whether the API works and prevents incorrect use while trying
|
|
||||||
# to update nodes.
|
|
||||||
def test_update
|
|
||||||
## First test with no user credentials
|
|
||||||
# try and update a node without authorisation
|
|
||||||
# first try to delete node without auth
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_node = create(:node, :changeset => create(:changeset, :user => private_user))
|
|
||||||
user = create(:user)
|
|
||||||
node = create(:node, :changeset => create(:changeset, :user => user))
|
|
||||||
|
|
||||||
xml = node.to_xml
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
## Second test with the private user
|
|
||||||
|
|
||||||
# setup auth
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
## trying to break changesets
|
|
||||||
|
|
||||||
# try and update in someone else's changeset
|
|
||||||
xml = update_changeset(private_node.to_xml,
|
|
||||||
create(:changeset).id)
|
|
||||||
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "update with other user's changeset should be forbidden when data isn't public"
|
|
||||||
|
|
||||||
# try and update in a closed changeset
|
|
||||||
xml = update_changeset(private_node.to_xml,
|
|
||||||
create(:changeset, :closed, :user => private_user).id)
|
|
||||||
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
|
|
||||||
|
|
||||||
# try and update in a non-existant changeset
|
|
||||||
xml = update_changeset(private_node.to_xml, 0)
|
|
||||||
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "update with changeset=0 should be forbidden, when data isn't public"
|
|
||||||
|
|
||||||
## try and submit invalid updates
|
|
||||||
xml = xml_attr_rewrite(private_node.to_xml, "lat", 91.0)
|
|
||||||
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "node at lat=91 should be forbidden, when data isn't public"
|
|
||||||
|
|
||||||
xml = xml_attr_rewrite(private_node.to_xml, "lat", -91.0)
|
|
||||||
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "node at lat=-91 should be forbidden, when data isn't public"
|
|
||||||
|
|
||||||
xml = xml_attr_rewrite(private_node.to_xml, "lon", 181.0)
|
|
||||||
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "node at lon=181 should be forbidden, when data isn't public"
|
|
||||||
|
|
||||||
xml = xml_attr_rewrite(private_node.to_xml, "lon", -181.0)
|
|
||||||
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "node at lon=-181 should be forbidden, when data isn't public"
|
|
||||||
|
|
||||||
## finally, produce a good request which still won't work
|
|
||||||
xml = private_node.to_xml
|
|
||||||
put :update, :params => { :id => private_node.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "should have failed with a forbidden when data isn't public"
|
|
||||||
|
|
||||||
## Finally test with the public user
|
|
||||||
|
|
||||||
# try and update a node without authorisation
|
|
||||||
# first try to update node without auth
|
|
||||||
xml = node.to_xml
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# setup auth
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
## trying to break changesets
|
|
||||||
|
|
||||||
# try and update in someone else's changeset
|
|
||||||
xml = update_changeset(node.to_xml,
|
|
||||||
create(:changeset).id)
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict, "update with other user's changeset should be rejected"
|
|
||||||
|
|
||||||
# try and update in a closed changeset
|
|
||||||
xml = update_changeset(node.to_xml,
|
|
||||||
create(:changeset, :closed, :user => user).id)
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict, "update with closed changeset should be rejected"
|
|
||||||
|
|
||||||
# try and update in a non-existant changeset
|
|
||||||
xml = update_changeset(node.to_xml, 0)
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict, "update with changeset=0 should be rejected"
|
|
||||||
|
|
||||||
## try and submit invalid updates
|
|
||||||
xml = xml_attr_rewrite(node.to_xml, "lat", 91.0)
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :bad_request, "node at lat=91 should be rejected"
|
|
||||||
|
|
||||||
xml = xml_attr_rewrite(node.to_xml, "lat", -91.0)
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :bad_request, "node at lat=-91 should be rejected"
|
|
||||||
|
|
||||||
xml = xml_attr_rewrite(node.to_xml, "lon", 181.0)
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :bad_request, "node at lon=181 should be rejected"
|
|
||||||
|
|
||||||
xml = xml_attr_rewrite(node.to_xml, "lon", -181.0)
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :bad_request, "node at lon=-181 should be rejected"
|
|
||||||
|
|
||||||
## next, attack the versioning
|
|
||||||
current_node_version = node.version
|
|
||||||
|
|
||||||
# try and submit a version behind
|
|
||||||
xml = xml_attr_rewrite(node.to_xml,
|
|
||||||
"version", current_node_version - 1)
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict, "should have failed on old version number"
|
|
||||||
|
|
||||||
# try and submit a version ahead
|
|
||||||
xml = xml_attr_rewrite(node.to_xml,
|
|
||||||
"version", current_node_version + 1)
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict, "should have failed on skipped version number"
|
|
||||||
|
|
||||||
# try and submit total crap in the version field
|
|
||||||
xml = xml_attr_rewrite(node.to_xml,
|
|
||||||
"version", "p1r4t3s!")
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict,
|
|
||||||
"should not be able to put 'p1r4at3s!' in the version field"
|
|
||||||
|
|
||||||
## try an update with the wrong ID
|
|
||||||
xml = create(:node).to_xml
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :bad_request,
|
|
||||||
"should not be able to update a node with a different ID from the XML"
|
|
||||||
|
|
||||||
## try an update with a minimal valid XML doc which isn't a well-formed OSM doc.
|
|
||||||
xml = "<update/>"
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :bad_request,
|
|
||||||
"should not be able to update a node with non-OSM XML doc."
|
|
||||||
|
|
||||||
## finally, produce a good request which should work
|
|
||||||
xml = node.to_xml
|
|
||||||
put :update, :params => { :id => node.id }, :body => xml.to_s
|
|
||||||
assert_response :success, "a valid update request failed"
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test fetching multiple nodes
|
|
||||||
def test_index
|
|
||||||
node1 = create(:node)
|
|
||||||
node2 = create(:node, :deleted)
|
|
||||||
node3 = create(:node)
|
|
||||||
node4 = create(:node, :with_history, :version => 2)
|
|
||||||
node5 = create(:node, :deleted, :with_history, :version => 2)
|
|
||||||
|
|
||||||
# check error when no parameter provided
|
|
||||||
get :index
|
|
||||||
assert_response :bad_request
|
|
||||||
|
|
||||||
# check error when no parameter value provided
|
|
||||||
get :index, :params => { :nodes => "" }
|
|
||||||
assert_response :bad_request
|
|
||||||
|
|
||||||
# test a working call
|
|
||||||
get :index, :params => { :nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id}" }
|
|
||||||
assert_response :success
|
|
||||||
assert_select "osm" do
|
|
||||||
assert_select "node", :count => 5
|
|
||||||
assert_select "node[id='#{node1.id}'][visible='true']", :count => 1
|
|
||||||
assert_select "node[id='#{node2.id}'][visible='false']", :count => 1
|
|
||||||
assert_select "node[id='#{node3.id}'][visible='true']", :count => 1
|
|
||||||
assert_select "node[id='#{node4.id}'][visible='true']", :count => 1
|
|
||||||
assert_select "node[id='#{node5.id}'][visible='false']", :count => 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# check error when a non-existent node is included
|
|
||||||
get :index, :params => { :nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id},0" }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test adding tags to a node
|
|
||||||
def test_duplicate_tags
|
|
||||||
existing_tag = create(:node_tag)
|
|
||||||
assert_equal true, existing_tag.node.changeset.user.data_public
|
|
||||||
# setup auth
|
|
||||||
basic_authorization existing_tag.node.changeset.user.email, "test"
|
|
||||||
|
|
||||||
# add an identical tag to the node
|
|
||||||
tag_xml = XML::Node.new("tag")
|
|
||||||
tag_xml["k"] = existing_tag.k
|
|
||||||
tag_xml["v"] = existing_tag.v
|
|
||||||
|
|
||||||
# add the tag into the existing xml
|
|
||||||
node_xml = existing_tag.node.to_xml
|
|
||||||
node_xml.find("//osm/node").first << tag_xml
|
|
||||||
|
|
||||||
# try and upload it
|
|
||||||
put :update, :params => { :id => existing_tag.node.id }, :body => node_xml.to_s
|
|
||||||
assert_response :bad_request,
|
|
||||||
"adding duplicate tags to a node should fail with 'bad request'"
|
|
||||||
assert_equal "Element node/#{existing_tag.node.id} has duplicate tags with key #{existing_tag.k}", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
# test whether string injection is possible
|
|
||||||
def test_string_injection
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_changeset = create(:changeset, :user => private_user)
|
|
||||||
user = create(:user)
|
|
||||||
changeset = create(:changeset, :user => user)
|
|
||||||
|
|
||||||
## First try with the non-data public user
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
# try and put something into a string that the API might
|
|
||||||
# use unquoted and therefore allow code injection...
|
|
||||||
xml = "<osm><node lat='0' lon='0' changeset='#{private_changeset.id}'>" \
|
|
||||||
'<tag k="#{@user.inspect}" v="0"/>' \
|
|
||||||
"</node></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
assert_require_public_data "Shouldn't be able to create with non-public user"
|
|
||||||
|
|
||||||
## Then try with the public data user
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# try and put something into a string that the API might
|
|
||||||
# use unquoted and therefore allow code injection...
|
|
||||||
xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'>" \
|
|
||||||
'<tag k="#{@user.inspect}" v="0"/>' \
|
|
||||||
"</node></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
assert_response :success
|
|
||||||
nodeid = @response.body
|
|
||||||
|
|
||||||
# find the node in the database
|
|
||||||
checknode = Node.find(nodeid)
|
|
||||||
assert_not_nil checknode, "node not found in data base after upload"
|
|
||||||
|
|
||||||
# and grab it using the api
|
|
||||||
get :show, :params => { :id => nodeid }
|
|
||||||
assert_response :success
|
|
||||||
apinode = Node.from_xml(@response.body)
|
|
||||||
assert_not_nil apinode, "downloaded node is nil, but shouldn't be"
|
|
||||||
|
|
||||||
# check the tags are not corrupted
|
|
||||||
assert_equal checknode.tags, apinode.tags
|
|
||||||
assert apinode.tags.include?("\#{@user.inspect}")
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# update the changeset_id of a node element
|
|
||||||
def update_changeset(xml, changeset_id)
|
|
||||||
xml_attr_rewrite(xml, "changeset", changeset_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# update an attribute in the node element
|
|
||||||
def xml_attr_rewrite(xml, name, value)
|
|
||||||
xml.find("//osm/node").first[name] = value.to_s
|
|
||||||
xml
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# parse some xml
|
|
||||||
def xml_parse(xml)
|
|
||||||
parser = XML::Parser.string(xml)
|
|
||||||
parser.parse
|
|
||||||
end
|
|
||||||
end
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,428 +0,0 @@
|
||||||
require "test_helper"
|
|
||||||
|
|
||||||
class OldNodesControllerTest < ActionController::TestCase
|
|
||||||
#
|
|
||||||
# TODO: test history
|
|
||||||
#
|
|
||||||
|
|
||||||
##
|
|
||||||
# test all routes which lead to this controller
|
|
||||||
def test_routes
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/node/1/history", :method => :get },
|
|
||||||
{ :controller => "old_nodes", :action => "history", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/node/1/2", :method => :get },
|
|
||||||
{ :controller => "old_nodes", :action => "version", :id => "1", :version => "2" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/node/1/2/redact", :method => :post },
|
|
||||||
{ :controller => "old_nodes", :action => "redact", :id => "1", :version => "2" }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the version call by submitting several revisions of a new node
|
|
||||||
# to the API and ensuring that later calls to version return the
|
|
||||||
# matching versions of the object.
|
|
||||||
#
|
|
||||||
##
|
|
||||||
# FIXME: Move this test to being an integration test since it spans multiple controllers
|
|
||||||
def test_version
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_node = create(:node, :with_history, :version => 4, :changeset => create(:changeset, :user => private_user))
|
|
||||||
user = create(:user)
|
|
||||||
node = create(:node, :with_history, :version => 4, :changeset => create(:changeset, :user => user))
|
|
||||||
create_list(:node_tag, 2, :node => node)
|
|
||||||
# Ensure that the current tags are propagated to the history too
|
|
||||||
propagate_tags(node, node.old_nodes.last)
|
|
||||||
|
|
||||||
## First try this with a non-public user
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
# setup a simple XML node
|
|
||||||
xml_doc = private_node.to_xml
|
|
||||||
xml_node = xml_doc.find("//osm/node").first
|
|
||||||
nodeid = private_node.id
|
|
||||||
|
|
||||||
# keep a hash of the versions => string, as we'll need something
|
|
||||||
# to test against later
|
|
||||||
versions = {}
|
|
||||||
|
|
||||||
# save a version for later checking
|
|
||||||
versions[xml_node["version"]] = xml_doc.to_s
|
|
||||||
|
|
||||||
# randomly move the node about
|
|
||||||
3.times do
|
|
||||||
# move the node somewhere else
|
|
||||||
xml_node["lat"] = precision(rand * 180 - 90).to_s
|
|
||||||
xml_node["lon"] = precision(rand * 360 - 180).to_s
|
|
||||||
with_controller(NodesController.new) do
|
|
||||||
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
|
|
||||||
assert_response :forbidden, "Should have rejected node update"
|
|
||||||
xml_node["version"] = @response.body.to_s
|
|
||||||
end
|
|
||||||
# save a version for later checking
|
|
||||||
versions[xml_node["version"]] = xml_doc.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# add a bunch of random tags
|
|
||||||
3.times do
|
|
||||||
xml_tag = XML::Node.new("tag")
|
|
||||||
xml_tag["k"] = random_string
|
|
||||||
xml_tag["v"] = random_string
|
|
||||||
xml_node << xml_tag
|
|
||||||
with_controller(NodesController.new) do
|
|
||||||
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
|
|
||||||
assert_response :forbidden,
|
|
||||||
"should have rejected node #{nodeid} (#{@response.body}) with forbidden"
|
|
||||||
xml_node["version"] = @response.body.to_s
|
|
||||||
end
|
|
||||||
# save a version for later checking
|
|
||||||
versions[xml_node["version"]] = xml_doc.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# probably should check that they didn't get written to the database
|
|
||||||
|
|
||||||
## Now do it with the public user
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# setup a simple XML node
|
|
||||||
|
|
||||||
xml_doc = node.to_xml
|
|
||||||
xml_node = xml_doc.find("//osm/node").first
|
|
||||||
nodeid = node.id
|
|
||||||
|
|
||||||
# keep a hash of the versions => string, as we'll need something
|
|
||||||
# to test against later
|
|
||||||
versions = {}
|
|
||||||
|
|
||||||
# save a version for later checking
|
|
||||||
versions[xml_node["version"]] = xml_doc.to_s
|
|
||||||
|
|
||||||
# randomly move the node about
|
|
||||||
3.times do
|
|
||||||
# move the node somewhere else
|
|
||||||
xml_node["lat"] = precision(rand * 180 - 90).to_s
|
|
||||||
xml_node["lon"] = precision(rand * 360 - 180).to_s
|
|
||||||
with_controller(NodesController.new) do
|
|
||||||
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
|
|
||||||
assert_response :success
|
|
||||||
xml_node["version"] = @response.body.to_s
|
|
||||||
end
|
|
||||||
# save a version for later checking
|
|
||||||
versions[xml_node["version"]] = xml_doc.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# add a bunch of random tags
|
|
||||||
3.times do
|
|
||||||
xml_tag = XML::Node.new("tag")
|
|
||||||
xml_tag["k"] = random_string
|
|
||||||
xml_tag["v"] = random_string
|
|
||||||
xml_node << xml_tag
|
|
||||||
with_controller(NodesController.new) do
|
|
||||||
put :update, :params => { :id => nodeid }, :body => xml_doc.to_s
|
|
||||||
assert_response :success,
|
|
||||||
"couldn't update node #{nodeid} (#{@response.body})"
|
|
||||||
xml_node["version"] = @response.body.to_s
|
|
||||||
end
|
|
||||||
# save a version for later checking
|
|
||||||
versions[xml_node["version"]] = xml_doc.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# check all the versions
|
|
||||||
versions.each_key do |key|
|
|
||||||
get :version, :params => { :id => nodeid, :version => key.to_i }
|
|
||||||
|
|
||||||
assert_response :success,
|
|
||||||
"couldn't get version #{key.to_i} of node #{nodeid}"
|
|
||||||
|
|
||||||
check_node = Node.from_xml(versions[key])
|
|
||||||
api_node = Node.from_xml(@response.body.to_s)
|
|
||||||
|
|
||||||
assert_nodes_are_equal check_node, api_node
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_not_found_version
|
|
||||||
check_not_found_id_version(70000, 312344)
|
|
||||||
check_not_found_id_version(-1, -13)
|
|
||||||
check_not_found_id_version(create(:node).id, 24354)
|
|
||||||
check_not_found_id_version(24356, create(:node).version)
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_not_found_id_version(id, version)
|
|
||||||
get :version, :params => { :id => id, :version => version }
|
|
||||||
assert_response :not_found
|
|
||||||
rescue ActionController::UrlGenerationError => ex
|
|
||||||
assert_match(/No route matches/, ex.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Test that getting the current version is identical to picking
|
|
||||||
# that version with the version URI call.
|
|
||||||
def test_current_version
|
|
||||||
node = create(:node, :with_history)
|
|
||||||
used_node = create(:node, :with_history)
|
|
||||||
create(:way_node, :node => used_node)
|
|
||||||
node_used_by_relationship = create(:node, :with_history)
|
|
||||||
create(:relation_member, :member => node_used_by_relationship)
|
|
||||||
node_with_versions = create(:node, :with_history, :version => 4)
|
|
||||||
|
|
||||||
create(:node_tag, :node => node)
|
|
||||||
create(:node_tag, :node => used_node)
|
|
||||||
create(:node_tag, :node => node_used_by_relationship)
|
|
||||||
create(:node_tag, :node => node_with_versions)
|
|
||||||
propagate_tags(node, node.old_nodes.last)
|
|
||||||
propagate_tags(used_node, used_node.old_nodes.last)
|
|
||||||
propagate_tags(node_used_by_relationship, node_used_by_relationship.old_nodes.last)
|
|
||||||
propagate_tags(node_with_versions, node_with_versions.old_nodes.last)
|
|
||||||
|
|
||||||
check_current_version(node)
|
|
||||||
check_current_version(used_node)
|
|
||||||
check_current_version(node_used_by_relationship)
|
|
||||||
check_current_version(node_with_versions)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the redaction of an old version of a node, while not being
|
|
||||||
# authorised.
|
|
||||||
def test_redact_node_unauthorised
|
|
||||||
node = create(:node, :with_history, :version => 4)
|
|
||||||
node_v3 = node.old_nodes.find_by(:version => 3)
|
|
||||||
|
|
||||||
do_redact_node(node_v3,
|
|
||||||
create(:redaction))
|
|
||||||
assert_response :unauthorized, "should need to be authenticated to redact."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the redaction of an old version of a node, while being
|
|
||||||
# authorised as a normal user.
|
|
||||||
def test_redact_node_normal_user
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
node = create(:node, :with_history, :version => 4)
|
|
||||||
node_v3 = node.old_nodes.find_by(:version => 3)
|
|
||||||
|
|
||||||
do_redact_node(node_v3,
|
|
||||||
create(:redaction))
|
|
||||||
assert_response :forbidden, "should need to be moderator to redact."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test that, even as moderator, the current version of a node
|
|
||||||
# can't be redacted.
|
|
||||||
def test_redact_node_current_version
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
node = create(:node, :with_history, :version => 4)
|
|
||||||
node_v4 = node.old_nodes.find_by(:version => 4)
|
|
||||||
|
|
||||||
do_redact_node(node_v4,
|
|
||||||
create(:redaction))
|
|
||||||
assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test that redacted nodes aren't visible, regardless of
|
|
||||||
# authorisation except as moderator...
|
|
||||||
def test_version_redacted
|
|
||||||
node = create(:node, :with_history, :version => 2)
|
|
||||||
node_v1 = node.old_nodes.find_by(:version => 1)
|
|
||||||
node_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
|
||||||
assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
|
|
||||||
|
|
||||||
# not even to a logged-in user
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
|
||||||
assert_response :forbidden, "Redacted node shouldn't be visible via the version API, even when logged in."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test that redacted nodes aren't visible in the history
|
|
||||||
def test_history_redacted
|
|
||||||
node = create(:node, :with_history, :version => 2)
|
|
||||||
node_v1 = node.old_nodes.find_by(:version => 1)
|
|
||||||
node_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
get :history, :params => { :id => node_v1.node_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 0, "redacted node #{node_v1.node_id} version #{node_v1.version} shouldn't be present in the history."
|
|
||||||
|
|
||||||
# not even to a logged-in user
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
get :history, :params => { :id => node_v1.node_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 0, "redacted node #{node_v1.node_id} version #{node_v1.version} shouldn't be present in the history, even when logged in."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the redaction of an old version of a node, while being
|
|
||||||
# authorised as a moderator.
|
|
||||||
def test_redact_node_moderator
|
|
||||||
node = create(:node, :with_history, :version => 4)
|
|
||||||
node_v3 = node.old_nodes.find_by(:version => 3)
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
do_redact_node(node_v3, create(:redaction))
|
|
||||||
assert_response :success, "should be OK to redact old version as moderator."
|
|
||||||
|
|
||||||
# check moderator can still see the redacted data, when passing
|
|
||||||
# the appropriate flag
|
|
||||||
get :version, :params => { :id => node_v3.node_id, :version => node_v3.version }
|
|
||||||
assert_response :forbidden, "After redaction, node should be gone for moderator, when flag not passed."
|
|
||||||
get :version, :params => { :id => node_v3.node_id, :version => node_v3.version, :show_redactions => "true" }
|
|
||||||
assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
|
|
||||||
|
|
||||||
# and when accessed via history
|
|
||||||
get :history, :params => { :id => node_v3.node_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 0, "node #{node_v3.node_id} version #{node_v3.version} should not be present in the history for moderators when not passing flag."
|
|
||||||
get :history, :params => { :id => node_v3.node_id, :show_redactions => "true" }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 1, "node #{node_v3.node_id} version #{node_v3.version} should still be present in the history for moderators when passing flag."
|
|
||||||
end
|
|
||||||
|
|
||||||
# testing that if the moderator drops auth, he can't see the
|
|
||||||
# redacted stuff any more.
|
|
||||||
def test_redact_node_is_redacted
|
|
||||||
node = create(:node, :with_history, :version => 4)
|
|
||||||
node_v3 = node.old_nodes.find_by(:version => 3)
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
do_redact_node(node_v3, create(:redaction))
|
|
||||||
assert_response :success, "should be OK to redact old version as moderator."
|
|
||||||
|
|
||||||
# re-auth as non-moderator
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
# check can't see the redacted data
|
|
||||||
get :version, :params => { :id => node_v3.node_id, :version => node_v3.version }
|
|
||||||
assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
|
|
||||||
|
|
||||||
# and when accessed via history
|
|
||||||
get :history, :params => { :id => node_v3.node_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm node[id='#{node_v3.node_id}'][version='#{node_v3.version}']", 0, "redacted node #{node_v3.node_id} version #{node_v3.version} shouldn't be present in the history."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the unredaction of an old version of a node, while not being
|
|
||||||
# authorised.
|
|
||||||
def test_unredact_node_unauthorised
|
|
||||||
node = create(:node, :with_history, :version => 2)
|
|
||||||
node_v1 = node.old_nodes.find_by(:version => 1)
|
|
||||||
node_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
post :redact, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
|
||||||
assert_response :unauthorized, "should need to be authenticated to unredact."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the unredaction of an old version of a node, while being
|
|
||||||
# authorised as a normal user.
|
|
||||||
def test_unredact_node_normal_user
|
|
||||||
user = create(:user)
|
|
||||||
node = create(:node, :with_history, :version => 2)
|
|
||||||
node_v1 = node.old_nodes.find_by(:version => 1)
|
|
||||||
node_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
post :redact, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
|
||||||
assert_response :forbidden, "should need to be moderator to unredact."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the unredaction of an old version of a node, while being
|
|
||||||
# authorised as a moderator.
|
|
||||||
def test_unredact_node_moderator
|
|
||||||
moderator_user = create(:moderator_user)
|
|
||||||
node = create(:node, :with_history, :version => 2)
|
|
||||||
node_v1 = node.old_nodes.find_by(:version => 1)
|
|
||||||
node_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
basic_authorization moderator_user.email, "test"
|
|
||||||
|
|
||||||
post :redact, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
|
||||||
assert_response :success, "should be OK to unredact old version as moderator."
|
|
||||||
|
|
||||||
# check moderator can now see the redacted data, when not
|
|
||||||
# passing the aspecial flag
|
|
||||||
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
|
||||||
assert_response :success, "After unredaction, node should not be gone for moderator."
|
|
||||||
|
|
||||||
# and when accessed via history
|
|
||||||
get :history, :params => { :id => node_v1.node_id }
|
|
||||||
assert_response :success, "Unredaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 1, "node #{node_v1.node_id} version #{node_v1.version} should now be present in the history for moderators without passing flag."
|
|
||||||
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
# check normal user can now see the redacted data
|
|
||||||
get :version, :params => { :id => node_v1.node_id, :version => node_v1.version }
|
|
||||||
assert_response :success, "After unredaction, node should be visible to normal users."
|
|
||||||
|
|
||||||
# and when accessed via history
|
|
||||||
get :history, :params => { :id => node_v1.node_id }
|
|
||||||
assert_response :success, "Unredaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm node[id='#{node_v1.node_id}'][version='#{node_v1.version}']", 1, "node #{node_v1.node_id} version #{node_v1.version} should now be present in the history for normal users without passing flag."
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def do_redact_node(node, redaction)
|
|
||||||
get :version, :params => { :id => node.node_id, :version => node.version }
|
|
||||||
assert_response :success, "should be able to get version #{node.version} of node #{node.node_id}."
|
|
||||||
|
|
||||||
# now redact it
|
|
||||||
post :redact, :params => { :id => node.node_id, :version => node.version, :redaction => redaction.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_current_version(node_id)
|
|
||||||
# get the current version of the node
|
|
||||||
current_node = with_controller(NodesController.new) do
|
|
||||||
get :show, :params => { :id => node_id }
|
|
||||||
assert_response :success, "cant get current node #{node_id}"
|
|
||||||
Node.from_xml(@response.body)
|
|
||||||
end
|
|
||||||
assert_not_nil current_node, "getting node #{node_id} returned nil"
|
|
||||||
|
|
||||||
# get the "old" version of the node from the old_node interface
|
|
||||||
get :version, :params => { :id => node_id, :version => current_node.version }
|
|
||||||
assert_response :success, "cant get old node #{node_id}, v#{current_node.version}"
|
|
||||||
old_node = Node.from_xml(@response.body)
|
|
||||||
|
|
||||||
# check the nodes are the same
|
|
||||||
assert_nodes_are_equal current_node, old_node
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# returns a 16 character long string with some nasty characters in it.
|
|
||||||
# this ought to stress-test the tag handling as well as the versioning.
|
|
||||||
def random_string
|
|
||||||
letters = [["!", '"', "$", "&", ";", "@"],
|
|
||||||
("a".."z").to_a,
|
|
||||||
("A".."Z").to_a,
|
|
||||||
("0".."9").to_a].flatten
|
|
||||||
(1..16).map { |_i| letters[rand(letters.length)] }.join
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# truncate a floating point number to the scale that it is stored in
|
|
||||||
# the database. otherwise rounding errors can produce failing unit
|
|
||||||
# tests when they shouldn't.
|
|
||||||
def precision(f)
|
|
||||||
(f * GeoRecord::SCALE).round.to_f / GeoRecord::SCALE
|
|
||||||
end
|
|
||||||
|
|
||||||
def propagate_tags(node, old_node)
|
|
||||||
node.tags.each do |k, v|
|
|
||||||
create(:old_node_tag, :old_node => old_node, :k => k, :v => v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,272 +0,0 @@
|
||||||
require "test_helper"
|
|
||||||
|
|
||||||
class OldRelationsControllerTest < ActionController::TestCase
|
|
||||||
##
|
|
||||||
# test all routes which lead to this controller
|
|
||||||
def test_routes
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/relation/1/history", :method => :get },
|
|
||||||
{ :controller => "old_relations", :action => "history", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/relation/1/2", :method => :get },
|
|
||||||
{ :controller => "old_relations", :action => "version", :id => "1", :version => "2" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/relation/1/2/redact", :method => :post },
|
|
||||||
{ :controller => "old_relations", :action => "redact", :id => "1", :version => "2" }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
# -------------------------------------
|
|
||||||
# Test reading old relations.
|
|
||||||
# -------------------------------------
|
|
||||||
def test_history
|
|
||||||
# check that a visible relations is returned properly
|
|
||||||
get :history, :params => { :id => create(:relation, :with_history).id }
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# check chat a non-existent relations is not returned
|
|
||||||
get :history, :params => { :id => 0 }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the redaction of an old version of a relation, while not being
|
|
||||||
# authorised.
|
|
||||||
def test_redact_relation_unauthorised
|
|
||||||
relation = create(:relation, :with_history, :version => 4)
|
|
||||||
relation_v3 = relation.old_relations.find_by(:version => 3)
|
|
||||||
|
|
||||||
do_redact_relation(relation_v3, create(:redaction))
|
|
||||||
assert_response :unauthorized, "should need to be authenticated to redact."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the redaction of an old version of a relation, while being
|
|
||||||
# authorised as a normal user.
|
|
||||||
def test_redact_relation_normal_user
|
|
||||||
relation = create(:relation, :with_history, :version => 4)
|
|
||||||
relation_v3 = relation.old_relations.find_by(:version => 3)
|
|
||||||
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
do_redact_relation(relation_v3, create(:redaction))
|
|
||||||
assert_response :forbidden, "should need to be moderator to redact."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test that, even as moderator, the current version of a relation
|
|
||||||
# can't be redacted.
|
|
||||||
def test_redact_relation_current_version
|
|
||||||
relation = create(:relation, :with_history, :version => 4)
|
|
||||||
relation_latest = relation.old_relations.last
|
|
||||||
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
do_redact_relation(relation_latest, create(:redaction))
|
|
||||||
assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test that redacted relations aren't visible, regardless of
|
|
||||||
# authorisation except as moderator...
|
|
||||||
def test_version_redacted
|
|
||||||
relation = create(:relation, :with_history, :version => 2)
|
|
||||||
relation_v1 = relation.old_relations.find_by(:version => 1)
|
|
||||||
relation_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
|
||||||
assert_response :forbidden, "Redacted relation shouldn't be visible via the version API."
|
|
||||||
|
|
||||||
# not even to a logged-in user
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
|
||||||
assert_response :forbidden, "Redacted relation shouldn't be visible via the version API, even when logged in."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test that redacted relations aren't visible in the history
|
|
||||||
def test_history_redacted
|
|
||||||
relation = create(:relation, :with_history, :version => 2)
|
|
||||||
relation_v1 = relation.old_relations.find_by(:version => 1)
|
|
||||||
relation_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
get :history, :params => { :id => relation_v1.relation_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 0, "redacted relation #{relation_v1.relation_id} version #{relation_v1.version} shouldn't be present in the history."
|
|
||||||
|
|
||||||
# not even to a logged-in user
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
|
||||||
get :history, :params => { :id => relation_v1.relation_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 0, "redacted relation #{relation_v1.relation_id} version #{relation_v1.version} shouldn't be present in the history, even when logged in."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the redaction of an old version of a relation, while being
|
|
||||||
# authorised as a moderator.
|
|
||||||
def test_redact_relation_moderator
|
|
||||||
relation = create(:relation, :with_history, :version => 4)
|
|
||||||
relation_v3 = relation.old_relations.find_by(:version => 3)
|
|
||||||
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
do_redact_relation(relation_v3, create(:redaction))
|
|
||||||
assert_response :success, "should be OK to redact old version as moderator."
|
|
||||||
|
|
||||||
# check moderator can still see the redacted data, when passing
|
|
||||||
# the appropriate flag
|
|
||||||
get :version, :params => { :id => relation_v3.relation_id, :version => relation_v3.version }
|
|
||||||
assert_response :forbidden, "After redaction, relation should be gone for moderator, when flag not passed."
|
|
||||||
get :version, :params => { :id => relation_v3.relation_id, :version => relation_v3.version, :show_redactions => "true" }
|
|
||||||
assert_response :success, "After redaction, relation should not be gone for moderator, when flag passed."
|
|
||||||
|
|
||||||
# and when accessed via history
|
|
||||||
get :history, :params => { :id => relation_v3.relation_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm relation[id='#{relation_v3.relation_id}'][version='#{relation_v3.version}']", 0, "relation #{relation_v3.relation_id} version #{relation_v3.version} should not be present in the history for moderators when not passing flag."
|
|
||||||
get :history, :params => { :id => relation_v3.relation_id, :show_redactions => "true" }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm relation[id='#{relation_v3.relation_id}'][version='#{relation_v3.version}']", 1, "relation #{relation_v3.relation_id} version #{relation_v3.version} should still be present in the history for moderators when passing flag."
|
|
||||||
end
|
|
||||||
|
|
||||||
# testing that if the moderator drops auth, he can't see the
|
|
||||||
# redacted stuff any more.
|
|
||||||
def test_redact_relation_is_redacted
|
|
||||||
relation = create(:relation, :with_history, :version => 4)
|
|
||||||
relation_v3 = relation.old_relations.find_by(:version => 3)
|
|
||||||
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
do_redact_relation(relation_v3, create(:redaction))
|
|
||||||
assert_response :success, "should be OK to redact old version as moderator."
|
|
||||||
|
|
||||||
# re-auth as non-moderator
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
# check can't see the redacted data
|
|
||||||
get :version, :params => { :id => relation_v3.relation_id, :version => relation_v3.version }
|
|
||||||
assert_response :forbidden, "Redacted relation shouldn't be visible via the version API."
|
|
||||||
|
|
||||||
# and when accessed via history
|
|
||||||
get :history, :params => { :id => relation_v3.relation_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm relation[id='#{relation_v3.relation_id}'][version='#{relation_v3.version}']", 0, "redacted relation #{relation_v3.relation_id} version #{relation_v3.version} shouldn't be present in the history."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the unredaction of an old version of a relation, while not being
|
|
||||||
# authorised.
|
|
||||||
def test_unredact_relation_unauthorised
|
|
||||||
relation = create(:relation, :with_history, :version => 2)
|
|
||||||
relation_v1 = relation.old_relations.find_by(:version => 1)
|
|
||||||
relation_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
post :redact, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
|
||||||
assert_response :unauthorized, "should need to be authenticated to unredact."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the unredaction of an old version of a relation, while being
|
|
||||||
# authorised as a normal user.
|
|
||||||
def test_unredact_relation_normal_user
|
|
||||||
relation = create(:relation, :with_history, :version => 2)
|
|
||||||
relation_v1 = relation.old_relations.find_by(:version => 1)
|
|
||||||
relation_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
post :redact, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
|
||||||
assert_response :forbidden, "should need to be moderator to unredact."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the unredaction of an old version of a relation, while being
|
|
||||||
# authorised as a moderator.
|
|
||||||
def test_unredact_relation_moderator
|
|
||||||
relation = create(:relation, :with_history, :version => 2)
|
|
||||||
relation_v1 = relation.old_relations.find_by(:version => 1)
|
|
||||||
relation_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
post :redact, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
|
||||||
assert_response :success, "should be OK to unredact old version as moderator."
|
|
||||||
|
|
||||||
# check moderator can still see the redacted data, without passing
|
|
||||||
# the appropriate flag
|
|
||||||
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
|
||||||
assert_response :success, "After unredaction, relation should not be gone for moderator."
|
|
||||||
|
|
||||||
# and when accessed via history
|
|
||||||
get :history, :params => { :id => relation_v1.relation_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 1, "relation #{relation_v1.relation_id} version #{relation_v1.version} should still be present in the history for moderators."
|
|
||||||
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
# check normal user can now see the redacted data
|
|
||||||
get :version, :params => { :id => relation_v1.relation_id, :version => relation_v1.version }
|
|
||||||
assert_response :success, "After redaction, node should not be gone for normal user."
|
|
||||||
|
|
||||||
# and when accessed via history
|
|
||||||
get :history, :params => { :id => relation_v1.relation_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm relation[id='#{relation_v1.relation_id}'][version='#{relation_v1.version}']", 1, "relation #{relation_v1.relation_id} version #{relation_v1.version} should still be present in the history for normal users."
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
##
|
|
||||||
# check that the current version of a relation is equivalent to the
|
|
||||||
# version which we're getting from the versions call.
|
|
||||||
def check_current_version(relation_id)
|
|
||||||
# get the current version
|
|
||||||
current_relation = with_controller(RelationsController.new) do
|
|
||||||
get :show, :params => { :id => relation_id }
|
|
||||||
assert_response :success, "can't get current relation #{relation_id}"
|
|
||||||
Relation.from_xml(@response.body)
|
|
||||||
end
|
|
||||||
assert_not_nil current_relation, "getting relation #{relation_id} returned nil"
|
|
||||||
|
|
||||||
# get the "old" version of the relation from the version method
|
|
||||||
get :version, :params => { :id => relation_id, :version => current_relation.version }
|
|
||||||
assert_response :success, "can't get old relation #{relation_id}, v#{current_relation.version}"
|
|
||||||
old_relation = Relation.from_xml(@response.body)
|
|
||||||
|
|
||||||
# check that the relations are identical
|
|
||||||
assert_relations_are_equal current_relation, old_relation
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# look at all the versions of the relation in the history and get each version from
|
|
||||||
# the versions call. check that they're the same.
|
|
||||||
def check_history_equals_versions(relation_id)
|
|
||||||
get :history, :params => { :id => relation_id }
|
|
||||||
assert_response :success, "can't get relation #{relation_id} from API"
|
|
||||||
history_doc = XML::Parser.string(@response.body).parse
|
|
||||||
assert_not_nil history_doc, "parsing relation #{relation_id} history failed"
|
|
||||||
|
|
||||||
history_doc.find("//osm/relation").each do |relation_doc|
|
|
||||||
history_relation = Relation.from_xml_node(relation_doc)
|
|
||||||
assert_not_nil history_relation, "parsing relation #{relation_id} version failed"
|
|
||||||
|
|
||||||
get :version, :params => { :id => relation_id, :version => history_relation.version }
|
|
||||||
assert_response :success, "couldn't get relation #{relation_id}, v#{history_relation.version}"
|
|
||||||
version_relation = Relation.from_xml(@response.body)
|
|
||||||
assert_not_nil version_relation, "failed to parse #{relation_id}, v#{history_relation.version}"
|
|
||||||
|
|
||||||
assert_relations_are_equal history_relation, version_relation
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def do_redact_relation(relation, redaction)
|
|
||||||
get :version, :params => { :id => relation.relation_id, :version => relation.version }
|
|
||||||
assert_response :success, "should be able to get version #{relation.version} of relation #{relation.relation_id}."
|
|
||||||
|
|
||||||
# now redact it
|
|
||||||
post :redact, :params => { :id => relation.relation_id, :version => relation.version, :redaction => redaction.id }
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,318 +0,0 @@
|
||||||
require "test_helper"
|
|
||||||
|
|
||||||
class OldWaysControllerTest < ActionController::TestCase
|
|
||||||
##
|
|
||||||
# test all routes which lead to this controller
|
|
||||||
def test_routes
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/way/1/history", :method => :get },
|
|
||||||
{ :controller => "old_ways", :action => "history", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/way/1/2", :method => :get },
|
|
||||||
{ :controller => "old_ways", :action => "version", :id => "1", :version => "2" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/way/1/2/redact", :method => :post },
|
|
||||||
{ :controller => "old_ways", :action => "redact", :id => "1", :version => "2" }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
# -------------------------------------
|
|
||||||
# Test reading old ways.
|
|
||||||
# -------------------------------------
|
|
||||||
|
|
||||||
def test_history_visible
|
|
||||||
# check that a visible way is returned properly
|
|
||||||
get :history, :params => { :id => create(:way, :with_history).id }
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_history_invisible
|
|
||||||
# check that an invisible way's history is returned properly
|
|
||||||
get :history, :params => { :id => create(:way, :with_history, :deleted).id }
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_history_invalid
|
|
||||||
# check chat a non-existent way is not returned
|
|
||||||
get :history, :params => { :id => 0 }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# check that we can retrieve versions of a way
|
|
||||||
def test_version
|
|
||||||
way = create(:way, :with_history)
|
|
||||||
used_way = create(:way, :with_history)
|
|
||||||
create(:relation_member, :member => used_way)
|
|
||||||
way_with_versions = create(:way, :with_history, :version => 4)
|
|
||||||
|
|
||||||
create(:way_tag, :way => way)
|
|
||||||
create(:way_tag, :way => used_way)
|
|
||||||
create(:way_tag, :way => way_with_versions)
|
|
||||||
propagate_tags(way, way.old_ways.last)
|
|
||||||
propagate_tags(used_way, used_way.old_ways.last)
|
|
||||||
propagate_tags(way_with_versions, way_with_versions.old_ways.last)
|
|
||||||
|
|
||||||
check_current_version(way.id)
|
|
||||||
check_current_version(used_way.id)
|
|
||||||
check_current_version(way_with_versions.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# check that returned history is the same as getting all
|
|
||||||
# versions of a way from the api.
|
|
||||||
def test_history_equals_versions
|
|
||||||
way = create(:way, :with_history)
|
|
||||||
used_way = create(:way, :with_history)
|
|
||||||
create(:relation_member, :member => used_way)
|
|
||||||
way_with_versions = create(:way, :with_history, :version => 4)
|
|
||||||
|
|
||||||
check_history_equals_versions(way.id)
|
|
||||||
check_history_equals_versions(used_way.id)
|
|
||||||
check_history_equals_versions(way_with_versions.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the redaction of an old version of a way, while not being
|
|
||||||
# authorised.
|
|
||||||
def test_redact_way_unauthorised
|
|
||||||
way = create(:way, :with_history, :version => 4)
|
|
||||||
way_v3 = way.old_ways.find_by(:version => 3)
|
|
||||||
|
|
||||||
do_redact_way(way_v3, create(:redaction))
|
|
||||||
assert_response :unauthorized, "should need to be authenticated to redact."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the redaction of an old version of a way, while being
|
|
||||||
# authorised as a normal user.
|
|
||||||
def test_redact_way_normal_user
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
way = create(:way, :with_history, :version => 4)
|
|
||||||
way_v3 = way.old_ways.find_by(:version => 3)
|
|
||||||
|
|
||||||
do_redact_way(way_v3, create(:redaction))
|
|
||||||
assert_response :forbidden, "should need to be moderator to redact."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test that, even as moderator, the current version of a way
|
|
||||||
# can't be redacted.
|
|
||||||
def test_redact_way_current_version
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
way = create(:way, :with_history, :version => 4)
|
|
||||||
way_latest = way.old_ways.last
|
|
||||||
|
|
||||||
do_redact_way(way_latest, create(:redaction))
|
|
||||||
assert_response :bad_request, "shouldn't be OK to redact current version as moderator."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test that redacted ways aren't visible, regardless of
|
|
||||||
# authorisation except as moderator...
|
|
||||||
def test_version_redacted
|
|
||||||
way = create(:way, :with_history, :version => 2)
|
|
||||||
way_v1 = way.old_ways.find_by(:version => 1)
|
|
||||||
way_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
|
||||||
assert_response :forbidden, "Redacted way shouldn't be visible via the version API."
|
|
||||||
|
|
||||||
# not even to a logged-in user
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
|
||||||
assert_response :forbidden, "Redacted way shouldn't be visible via the version API, even when logged in."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test that redacted ways aren't visible in the history
|
|
||||||
def test_history_redacted
|
|
||||||
way = create(:way, :with_history, :version => 2)
|
|
||||||
way_v1 = way.old_ways.find_by(:version => 1)
|
|
||||||
way_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
get :history, :params => { :id => way_v1.way_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 0, "redacted way #{way_v1.way_id} version #{way_v1.version} shouldn't be present in the history."
|
|
||||||
|
|
||||||
# not even to a logged-in user
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
|
||||||
get :history, :params => { :id => way_v1.way_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 0, "redacted node #{way_v1.way_id} version #{way_v1.version} shouldn't be present in the history, even when logged in."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the redaction of an old version of a way, while being
|
|
||||||
# authorised as a moderator.
|
|
||||||
def test_redact_way_moderator
|
|
||||||
way = create(:way, :with_history, :version => 4)
|
|
||||||
way_v3 = way.old_ways.find_by(:version => 3)
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
do_redact_way(way_v3, create(:redaction))
|
|
||||||
assert_response :success, "should be OK to redact old version as moderator."
|
|
||||||
|
|
||||||
# check moderator can still see the redacted data, when passing
|
|
||||||
# the appropriate flag
|
|
||||||
get :version, :params => { :id => way_v3.way_id, :version => way_v3.version }
|
|
||||||
assert_response :forbidden, "After redaction, node should be gone for moderator, when flag not passed."
|
|
||||||
get :version, :params => { :id => way_v3.way_id, :version => way_v3.version, :show_redactions => "true" }
|
|
||||||
assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
|
|
||||||
|
|
||||||
# and when accessed via history
|
|
||||||
get :history, :params => { :id => way_v3.way_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm way[id='#{way_v3.way_id}'][version='#{way_v3.version}']", 0, "way #{way_v3.way_id} version #{way_v3.version} should not be present in the history for moderators when not passing flag."
|
|
||||||
get :history, :params => { :id => way_v3.way_id, :show_redactions => "true" }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm way[id='#{way_v3.way_id}'][version='#{way_v3.version}']", 1, "way #{way_v3.way_id} version #{way_v3.version} should still be present in the history for moderators when passing flag."
|
|
||||||
end
|
|
||||||
|
|
||||||
# testing that if the moderator drops auth, he can't see the
|
|
||||||
# redacted stuff any more.
|
|
||||||
def test_redact_way_is_redacted
|
|
||||||
way = create(:way, :with_history, :version => 4)
|
|
||||||
way_v3 = way.old_ways.find_by(:version => 3)
|
|
||||||
basic_authorization create(:moderator_user).email, "test"
|
|
||||||
|
|
||||||
do_redact_way(way_v3, create(:redaction))
|
|
||||||
assert_response :success, "should be OK to redact old version as moderator."
|
|
||||||
|
|
||||||
# re-auth as non-moderator
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
# check can't see the redacted data
|
|
||||||
get :version, :params => { :id => way_v3.way_id, :version => way_v3.version }
|
|
||||||
assert_response :forbidden, "Redacted node shouldn't be visible via the version API."
|
|
||||||
|
|
||||||
# and when accessed via history
|
|
||||||
get :history, :params => { :id => way_v3.way_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm way[id='#{way_v3.way_id}'][version='#{way_v3.version}']", 0, "redacted way #{way_v3.way_id} version #{way_v3.version} shouldn't be present in the history."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the unredaction of an old version of a way, while not being
|
|
||||||
# authorised.
|
|
||||||
def test_unredact_way_unauthorised
|
|
||||||
way = create(:way, :with_history, :version => 2)
|
|
||||||
way_v1 = way.old_ways.find_by(:version => 1)
|
|
||||||
way_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
post :redact, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
|
||||||
assert_response :unauthorized, "should need to be authenticated to unredact."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the unredaction of an old version of a way, while being
|
|
||||||
# authorised as a normal user.
|
|
||||||
def test_unredact_way_normal_user
|
|
||||||
way = create(:way, :with_history, :version => 2)
|
|
||||||
way_v1 = way.old_ways.find_by(:version => 1)
|
|
||||||
way_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
post :redact, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
|
||||||
assert_response :forbidden, "should need to be moderator to unredact."
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test the unredaction of an old version of a way, while being
|
|
||||||
# authorised as a moderator.
|
|
||||||
def test_unredact_way_moderator
|
|
||||||
moderator_user = create(:moderator_user)
|
|
||||||
way = create(:way, :with_history, :version => 2)
|
|
||||||
way_v1 = way.old_ways.find_by(:version => 1)
|
|
||||||
way_v1.redact!(create(:redaction))
|
|
||||||
|
|
||||||
basic_authorization moderator_user.email, "test"
|
|
||||||
|
|
||||||
post :redact, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
|
||||||
assert_response :success, "should be OK to unredact old version as moderator."
|
|
||||||
|
|
||||||
# check moderator can still see the unredacted data, without passing
|
|
||||||
# the appropriate flag
|
|
||||||
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
|
||||||
assert_response :success, "After unredaction, node should not be gone for moderator."
|
|
||||||
|
|
||||||
# and when accessed via history
|
|
||||||
get :history, :params => { :id => way_v1.way_id }
|
|
||||||
assert_response :success, "Unredaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 1, "way #{way_v1.way_id} version #{way_v1.version} should still be present in the history for moderators."
|
|
||||||
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
# check normal user can now see the unredacted data
|
|
||||||
get :version, :params => { :id => way_v1.way_id, :version => way_v1.version }
|
|
||||||
assert_response :success, "After redaction, node should not be gone for moderator, when flag passed."
|
|
||||||
|
|
||||||
# and when accessed via history
|
|
||||||
get :history, :params => { :id => way_v1.way_id }
|
|
||||||
assert_response :success, "Redaction shouldn't have stopped history working."
|
|
||||||
assert_select "osm way[id='#{way_v1.way_id}'][version='#{way_v1.version}']", 1, "way #{way_v1.way_id} version #{way_v1.version} should still be present in the history for normal users."
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
##
|
|
||||||
# check that the current version of a way is equivalent to the
|
|
||||||
# version which we're getting from the versions call.
|
|
||||||
def check_current_version(way_id)
|
|
||||||
# get the current version
|
|
||||||
current_way = with_controller(WaysController.new) do
|
|
||||||
get :show, :params => { :id => way_id }
|
|
||||||
assert_response :success, "can't get current way #{way_id}"
|
|
||||||
Way.from_xml(@response.body)
|
|
||||||
end
|
|
||||||
assert_not_nil current_way, "getting way #{way_id} returned nil"
|
|
||||||
|
|
||||||
# get the "old" version of the way from the version method
|
|
||||||
get :version, :params => { :id => way_id, :version => current_way.version }
|
|
||||||
assert_response :success, "can't get old way #{way_id}, v#{current_way.version}"
|
|
||||||
old_way = Way.from_xml(@response.body)
|
|
||||||
|
|
||||||
# check that the ways are identical
|
|
||||||
assert_ways_are_equal current_way, old_way
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# look at all the versions of the way in the history and get each version from
|
|
||||||
# the versions call. check that they're the same.
|
|
||||||
def check_history_equals_versions(way_id)
|
|
||||||
get :history, :params => { :id => way_id }
|
|
||||||
assert_response :success, "can't get way #{way_id} from API"
|
|
||||||
history_doc = XML::Parser.string(@response.body).parse
|
|
||||||
assert_not_nil history_doc, "parsing way #{way_id} history failed"
|
|
||||||
|
|
||||||
history_doc.find("//osm/way").each do |way_doc|
|
|
||||||
history_way = Way.from_xml_node(way_doc)
|
|
||||||
assert_not_nil history_way, "parsing way #{way_id} version failed"
|
|
||||||
|
|
||||||
get :version, :params => { :id => way_id, :version => history_way.version }
|
|
||||||
assert_response :success, "couldn't get way #{way_id}, v#{history_way.version}"
|
|
||||||
version_way = Way.from_xml(@response.body)
|
|
||||||
assert_not_nil version_way, "failed to parse #{way_id}, v#{history_way.version}"
|
|
||||||
|
|
||||||
assert_ways_are_equal history_way, version_way
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def do_redact_way(way, redaction)
|
|
||||||
get :version, :params => { :id => way.way_id, :version => way.version }
|
|
||||||
assert_response :success, "should be able to get version #{way.version} of way #{way.way_id}."
|
|
||||||
|
|
||||||
# now redact it
|
|
||||||
post :redact, :params => { :id => way.way_id, :version => way.version, :redaction => redaction.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
def propagate_tags(way, old_way)
|
|
||||||
way.tags.each do |k, v|
|
|
||||||
create(:old_way_tag, :old_way => old_way, :k => k, :v => v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,106 +0,0 @@
|
||||||
require "test_helper"
|
|
||||||
|
|
||||||
class SearchControllerTest < ActionController::TestCase
|
|
||||||
##
|
|
||||||
# test all routes which lead to this controller
|
|
||||||
def test_routes
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/search", :method => :get },
|
|
||||||
{ :controller => "search", :action => "search_all" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/nodes/search", :method => :get },
|
|
||||||
{ :controller => "search", :action => "search_nodes" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/ways/search", :method => :get },
|
|
||||||
{ :controller => "search", :action => "search_ways" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/relations/search", :method => :get },
|
|
||||||
{ :controller => "search", :action => "search_relations" }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test searching nodes
|
|
||||||
def test_search_nodes
|
|
||||||
get :search_nodes, :params => { :type => "test" }
|
|
||||||
assert_response :service_unavailable
|
|
||||||
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
|
|
||||||
|
|
||||||
get :search_nodes, :params => { :type => "test", :value => "yes" }
|
|
||||||
assert_response :service_unavailable
|
|
||||||
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
|
|
||||||
|
|
||||||
get :search_nodes, :params => { :name => "Test Node" }
|
|
||||||
assert_response :service_unavailable
|
|
||||||
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test searching ways
|
|
||||||
def test_search_ways
|
|
||||||
first_way = create(:way_with_nodes, :nodes_count => 2)
|
|
||||||
deleted_way = create(:way_with_nodes, :deleted, :nodes_count => 2)
|
|
||||||
third_way = create(:way_with_nodes, :nodes_count => 2)
|
|
||||||
|
|
||||||
[first_way, deleted_way, third_way].each do |way|
|
|
||||||
create(:way_tag, :way => way, :k => "test", :v => "yes")
|
|
||||||
end
|
|
||||||
create(:way_tag, :way => third_way, :k => "name", :v => "Test Way")
|
|
||||||
|
|
||||||
get :search_ways, :params => { :type => "test" }
|
|
||||||
assert_response :service_unavailable
|
|
||||||
assert_equal "Searching for a key without value is currently unavailable", response.headers["Error"]
|
|
||||||
|
|
||||||
get :search_ways, :params => { :type => "test", :value => "yes" }
|
|
||||||
assert_response :success
|
|
||||||
assert_select "way", 3
|
|
||||||
|
|
||||||
get :search_ways, :params => { :name => "Test Way" }
|
|
||||||
assert_response :success
|
|
||||||
assert_select "way", 1
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test searching relations
|
|
||||||
def test_search_relations
|
|
||||||
first_relation = create(:relation)
|
|
||||||
deleted_relation = create(:relation)
|
|
||||||
third_relation = create(:relation)
|
|
||||||
|
|
||||||
[first_relation, deleted_relation, third_relation].each do |relation|
|
|
||||||
create(:relation_tag, :relation => relation, :k => "test", :v => "yes")
|
|
||||||
end
|
|
||||||
create(:relation_tag, :relation => third_relation, :k => "name", :v => "Test Relation")
|
|
||||||
|
|
||||||
get :search_relations, :params => { :type => "test" }
|
|
||||||
assert_response :service_unavailable
|
|
||||||
assert_equal "Searching for a key without value is currently unavailable", response.headers["Error"]
|
|
||||||
|
|
||||||
get :search_relations, :params => { :type => "test", :value => "yes" }
|
|
||||||
assert_response :success
|
|
||||||
assert_select "relation", 3
|
|
||||||
|
|
||||||
get :search_relations, :params => { :name => "Test Relation" }
|
|
||||||
assert_response :success
|
|
||||||
assert_select "relation", 1
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test searching nodes, ways and relations
|
|
||||||
def test_search_all
|
|
||||||
get :search_all, :params => { :type => "test" }
|
|
||||||
assert_response :service_unavailable
|
|
||||||
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
|
|
||||||
|
|
||||||
get :search_all, :params => { :type => "test", :value => "yes" }
|
|
||||||
assert_response :service_unavailable
|
|
||||||
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
|
|
||||||
|
|
||||||
get :search_all, :params => { :name => "Test" }
|
|
||||||
assert_response :service_unavailable
|
|
||||||
assert_equal "Searching of nodes is currently unavailable", response.headers["Error"]
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,44 +0,0 @@
|
||||||
require "test_helper"
|
|
||||||
|
|
||||||
class SwfControllerTest < ActionController::TestCase
|
|
||||||
##
|
|
||||||
# test all routes which lead to this controller
|
|
||||||
def test_routes
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/swf/trackpoints", :method => :get },
|
|
||||||
{ :controller => "swf", :action => "trackpoints" }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# basic test that trackpoints at least returns some sort of flash movie
|
|
||||||
def test_trackpoints
|
|
||||||
user = create(:user)
|
|
||||||
other_user = create(:user)
|
|
||||||
create(:trace, :visibility => "trackable", :latitude => 51.51, :longitude => -0.14, :user => user) do |trace|
|
|
||||||
create(:tracepoint, :trace => trace, :trackid => 1, :latitude => (51.510 * GeoRecord::SCALE).to_i, :longitude => (-0.140 * GeoRecord::SCALE).to_i)
|
|
||||||
create(:tracepoint, :trace => trace, :trackid => 2, :latitude => (51.511 * GeoRecord::SCALE).to_i, :longitude => (-0.141 * GeoRecord::SCALE).to_i)
|
|
||||||
end
|
|
||||||
create(:trace, :visibility => "identifiable", :latitude => 51.512, :longitude => 0.142) do |trace|
|
|
||||||
create(:tracepoint, :trace => trace, :latitude => (51.512 * GeoRecord::SCALE).to_i, :longitude => (0.142 * GeoRecord::SCALE).to_i)
|
|
||||||
end
|
|
||||||
|
|
||||||
get :trackpoints, :params => { :xmin => -1, :xmax => 1, :ymin => 51, :ymax => 52, :baselong => 0, :basey => 0, :masterscale => 1 }
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "application/x-shockwave-flash", response.content_type
|
|
||||||
assert_match(/^FWS/, response.body)
|
|
||||||
assert_equal 80, response.body.length
|
|
||||||
|
|
||||||
get :trackpoints, :params => { :xmin => -1, :xmax => 1, :ymin => 51, :ymax => 52, :baselong => 0, :basey => 0, :masterscale => 1, :token => other_user.tokens.create.token }
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "application/x-shockwave-flash", response.content_type
|
|
||||||
assert_match(/^FWS/, response.body)
|
|
||||||
assert_equal 67, response.body.length
|
|
||||||
|
|
||||||
get :trackpoints, :params => { :xmin => -1, :xmax => 1, :ymin => 51, :ymax => 52, :baselong => 0, :basey => 0, :masterscale => 1, :token => user.tokens.create.token }
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "application/x-shockwave-flash", response.content_type
|
|
||||||
assert_match(/^FWS/, response.body)
|
|
||||||
assert_equal 74, response.body.length
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -24,35 +24,6 @@ class TracesControllerTest < ActionController::TestCase
|
||||||
##
|
##
|
||||||
# test all routes which lead to this controller
|
# test all routes which lead to this controller
|
||||||
def test_routes
|
def test_routes
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/gpx/create", :method => :post },
|
|
||||||
{ :controller => "traces", :action => "api_create" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/gpx/1", :method => :get },
|
|
||||||
{ :controller => "traces", :action => "api_read", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/gpx/1", :method => :put },
|
|
||||||
{ :controller => "traces", :action => "api_update", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/gpx/1", :method => :delete },
|
|
||||||
{ :controller => "traces", :action => "api_delete", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_recognizes(
|
|
||||||
{ :controller => "traces", :action => "api_read", :id => "1" },
|
|
||||||
{ :path => "/api/0.6/gpx/1/details", :method => :get }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/gpx/1/data", :method => :get },
|
|
||||||
{ :controller => "traces", :action => "api_data", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/gpx/1/data.xml", :method => :get },
|
|
||||||
{ :controller => "traces", :action => "api_data", :id => "1", :format => "xml" }
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_routing(
|
assert_routing(
|
||||||
{ :path => "/traces", :method => :get },
|
{ :path => "/traces", :method => :get },
|
||||||
{ :controller => "traces", :action => "index" }
|
{ :controller => "traces", :action => "index" }
|
||||||
|
@ -719,296 +690,6 @@ class TracesControllerTest < ActionController::TestCase
|
||||||
assert_equal false, trace.visible
|
assert_equal false, trace.visible
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check getting a specific trace through the api
|
|
||||||
def test_api_read
|
|
||||||
public_trace_file = create(:trace, :visibility => "public")
|
|
||||||
|
|
||||||
# First with no auth
|
|
||||||
get :api_read, :params => { :id => public_trace_file.id }
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
# Now with some other user, which should work since the trace is public
|
|
||||||
basic_authorization create(:user).display_name, "test"
|
|
||||||
get :api_read, :params => { :id => public_trace_file.id }
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# And finally we should be able to do it with the owner of the trace
|
|
||||||
basic_authorization public_trace_file.user.display_name, "test"
|
|
||||||
get :api_read, :params => { :id => public_trace_file.id }
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check an anoymous trace can't be specifically fetched by another user
|
|
||||||
def test_api_read_anon
|
|
||||||
anon_trace_file = create(:trace, :visibility => "private")
|
|
||||||
|
|
||||||
# First with no auth
|
|
||||||
get :api_read, :params => { :id => anon_trace_file.id }
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
# Now try with another user, which shouldn't work since the trace is anon
|
|
||||||
basic_authorization create(:user).display_name, "test"
|
|
||||||
get :api_read, :params => { :id => anon_trace_file.id }
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# And finally we should be able to get the trace details with the trace owner
|
|
||||||
basic_authorization anon_trace_file.user.display_name, "test"
|
|
||||||
get :api_read, :params => { :id => anon_trace_file.id }
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check the api details for a trace that doesn't exist
|
|
||||||
def test_api_read_not_found
|
|
||||||
deleted_trace_file = create(:trace, :deleted)
|
|
||||||
|
|
||||||
# Try first with no auth, as it should require it
|
|
||||||
get :api_read, :params => { :id => 0 }
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
# Login, and try again
|
|
||||||
basic_authorization deleted_trace_file.user.display_name, "test"
|
|
||||||
get :api_read, :params => { :id => 0 }
|
|
||||||
assert_response :not_found
|
|
||||||
|
|
||||||
# Now try a trace which did exist but has been deleted
|
|
||||||
basic_authorization deleted_trace_file.user.display_name, "test"
|
|
||||||
get :api_read, :params => { :id => deleted_trace_file.id }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test downloading a trace through the api
|
|
||||||
def test_api_data
|
|
||||||
public_trace_file = create(:trace, :visibility => "public", :fixture => "a")
|
|
||||||
|
|
||||||
# First with no auth
|
|
||||||
get :api_data, :params => { :id => public_trace_file.id }
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
# Now with some other user, which should work since the trace is public
|
|
||||||
basic_authorization create(:user).display_name, "test"
|
|
||||||
get :api_data, :params => { :id => public_trace_file.id }
|
|
||||||
check_trace_data public_trace_file, "848caa72f2f456d1bd6a0fdf228aa1b9"
|
|
||||||
|
|
||||||
# And finally we should be able to do it with the owner of the trace
|
|
||||||
basic_authorization public_trace_file.user.display_name, "test"
|
|
||||||
get :api_data, :params => { :id => public_trace_file.id }
|
|
||||||
check_trace_data public_trace_file, "848caa72f2f456d1bd6a0fdf228aa1b9"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test downloading a compressed trace through the api
|
|
||||||
def test_api_data_compressed
|
|
||||||
identifiable_trace_file = create(:trace, :visibility => "identifiable", :fixture => "d")
|
|
||||||
|
|
||||||
# Authenticate as the owner of the trace we will be using
|
|
||||||
basic_authorization identifiable_trace_file.user.display_name, "test"
|
|
||||||
|
|
||||||
# First get the data as is
|
|
||||||
get :api_data, :params => { :id => identifiable_trace_file.id }
|
|
||||||
check_trace_data identifiable_trace_file, "c6422a3d8750faae49ed70e7e8a51b93", "application/x-gzip", "gpx.gz"
|
|
||||||
|
|
||||||
# Now ask explicitly for XML format
|
|
||||||
get :api_data, :params => { :id => identifiable_trace_file.id, :format => "xml" }
|
|
||||||
check_trace_data identifiable_trace_file, "abd6675fdf3024a84fc0a1deac147c0d", "application/xml", "xml"
|
|
||||||
|
|
||||||
# Now ask explicitly for GPX format
|
|
||||||
get :api_data, :params => { :id => identifiable_trace_file.id, :format => "gpx" }
|
|
||||||
check_trace_data identifiable_trace_file, "abd6675fdf3024a84fc0a1deac147c0d"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check an anonymous trace can't be downloaded by another user through the api
|
|
||||||
def test_api_data_anon
|
|
||||||
anon_trace_file = create(:trace, :visibility => "private", :fixture => "b")
|
|
||||||
|
|
||||||
# First with no auth
|
|
||||||
get :api_data, :params => { :id => anon_trace_file.id }
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
# Now with some other user, which shouldn't work since the trace is anon
|
|
||||||
basic_authorization create(:user).display_name, "test"
|
|
||||||
get :api_data, :params => { :id => anon_trace_file.id }
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# And finally we should be able to do it with the owner of the trace
|
|
||||||
basic_authorization anon_trace_file.user.display_name, "test"
|
|
||||||
get :api_data, :params => { :id => anon_trace_file.id }
|
|
||||||
check_trace_data anon_trace_file, "66179ca44f1e93d8df62e2b88cbea732"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test downloading a trace that doesn't exist through the api
|
|
||||||
def test_api_data_not_found
|
|
||||||
deleted_trace_file = create(:trace, :deleted)
|
|
||||||
|
|
||||||
# Try first with no auth, as it should require it
|
|
||||||
get :api_data, :params => { :id => 0 }
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
# Login, and try again
|
|
||||||
basic_authorization create(:user).display_name, "test"
|
|
||||||
get :api_data, :params => { :id => 0 }
|
|
||||||
assert_response :not_found
|
|
||||||
|
|
||||||
# Now try a trace which did exist but has been deleted
|
|
||||||
basic_authorization deleted_trace_file.user.display_name, "test"
|
|
||||||
get :api_data, :params => { :id => deleted_trace_file.id }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test creating a trace through the api
|
|
||||||
def test_api_create
|
|
||||||
# Get file to use
|
|
||||||
fixture = Rails.root.join("test", "gpx", "fixtures", "a.gpx")
|
|
||||||
file = Rack::Test::UploadedFile.new(fixture, "application/gpx+xml")
|
|
||||||
user = create(:user)
|
|
||||||
|
|
||||||
# First with no auth
|
|
||||||
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :visibility => "trackable" }
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
# Rewind the file
|
|
||||||
file.rewind
|
|
||||||
|
|
||||||
# Now authenticated
|
|
||||||
create(:user_preference, :user => user, :k => "gps.trace.visibility", :v => "identifiable")
|
|
||||||
assert_not_equal "trackable", user.preferences.where(:k => "gps.trace.visibility").first.v
|
|
||||||
basic_authorization user.display_name, "test"
|
|
||||||
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :visibility => "trackable" }
|
|
||||||
assert_response :success
|
|
||||||
trace = Trace.find(response.body.to_i)
|
|
||||||
assert_equal "a.gpx", trace.name
|
|
||||||
assert_equal "New Trace", trace.description
|
|
||||||
assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
|
|
||||||
assert_equal "trackable", trace.visibility
|
|
||||||
assert_equal false, trace.inserted
|
|
||||||
assert_equal File.new(fixture).read, File.new(trace.trace_name).read
|
|
||||||
trace.destroy
|
|
||||||
assert_equal "trackable", user.preferences.where(:k => "gps.trace.visibility").first.v
|
|
||||||
|
|
||||||
# Rewind the file
|
|
||||||
file.rewind
|
|
||||||
|
|
||||||
# Now authenticated, with the legacy public flag
|
|
||||||
assert_not_equal "public", user.preferences.where(:k => "gps.trace.visibility").first.v
|
|
||||||
basic_authorization user.display_name, "test"
|
|
||||||
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :public => 1 }
|
|
||||||
assert_response :success
|
|
||||||
trace = Trace.find(response.body.to_i)
|
|
||||||
assert_equal "a.gpx", trace.name
|
|
||||||
assert_equal "New Trace", trace.description
|
|
||||||
assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
|
|
||||||
assert_equal "public", trace.visibility
|
|
||||||
assert_equal false, trace.inserted
|
|
||||||
assert_equal File.new(fixture).read, File.new(trace.trace_name).read
|
|
||||||
trace.destroy
|
|
||||||
assert_equal "public", user.preferences.where(:k => "gps.trace.visibility").first.v
|
|
||||||
|
|
||||||
# Rewind the file
|
|
||||||
file.rewind
|
|
||||||
|
|
||||||
# Now authenticated, with the legacy private flag
|
|
||||||
second_user = create(:user)
|
|
||||||
assert_nil second_user.preferences.where(:k => "gps.trace.visibility").first
|
|
||||||
basic_authorization second_user.display_name, "test"
|
|
||||||
post :api_create, :params => { :file => file, :description => "New Trace", :tags => "new,trace", :public => 0 }
|
|
||||||
assert_response :success
|
|
||||||
trace = Trace.find(response.body.to_i)
|
|
||||||
assert_equal "a.gpx", trace.name
|
|
||||||
assert_equal "New Trace", trace.description
|
|
||||||
assert_equal %w[new trace], trace.tags.order(:tag).collect(&:tag)
|
|
||||||
assert_equal "private", trace.visibility
|
|
||||||
assert_equal false, trace.inserted
|
|
||||||
assert_equal File.new(fixture).read, File.new(trace.trace_name).read
|
|
||||||
trace.destroy
|
|
||||||
assert_equal "private", second_user.preferences.where(:k => "gps.trace.visibility").first.v
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check updating a trace through the api
|
|
||||||
def test_api_update
|
|
||||||
public_trace_file = create(:trace, :visibility => "public", :fixture => "a")
|
|
||||||
deleted_trace_file = create(:trace, :deleted)
|
|
||||||
anon_trace_file = create(:trace, :visibility => "private")
|
|
||||||
|
|
||||||
# First with no auth
|
|
||||||
put :api_update, :params => { :id => public_trace_file.id }, :body => public_trace_file.to_xml.to_s
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
# Now with some other user, which should fail
|
|
||||||
basic_authorization create(:user).display_name, "test"
|
|
||||||
put :api_update, :params => { :id => public_trace_file.id }, :body => public_trace_file.to_xml.to_s
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# Now with a trace which doesn't exist
|
|
||||||
basic_authorization create(:user).display_name, "test"
|
|
||||||
put :api_update, :params => { :id => 0 }, :body => public_trace_file.to_xml.to_s
|
|
||||||
assert_response :not_found
|
|
||||||
|
|
||||||
# Now with a trace which did exist but has been deleted
|
|
||||||
basic_authorization deleted_trace_file.user.display_name, "test"
|
|
||||||
put :api_update, :params => { :id => deleted_trace_file.id }, :body => deleted_trace_file.to_xml.to_s
|
|
||||||
assert_response :not_found
|
|
||||||
|
|
||||||
# Now try an update with the wrong ID
|
|
||||||
basic_authorization public_trace_file.user.display_name, "test"
|
|
||||||
put :api_update, :params => { :id => public_trace_file.id }, :body => anon_trace_file.to_xml.to_s
|
|
||||||
assert_response :bad_request,
|
|
||||||
"should not be able to update a trace with a different ID from the XML"
|
|
||||||
|
|
||||||
# And finally try an update that should work
|
|
||||||
basic_authorization public_trace_file.user.display_name, "test"
|
|
||||||
t = public_trace_file
|
|
||||||
t.description = "Changed description"
|
|
||||||
t.visibility = "private"
|
|
||||||
put :api_update, :params => { :id => t.id }, :body => t.to_xml.to_s
|
|
||||||
assert_response :success
|
|
||||||
nt = Trace.find(t.id)
|
|
||||||
assert_equal nt.description, t.description
|
|
||||||
assert_equal nt.visibility, t.visibility
|
|
||||||
end
|
|
||||||
|
|
||||||
# Test that updating a trace doesn't duplicate the tags
|
|
||||||
def test_api_update_tags
|
|
||||||
tracetag = create(:tracetag)
|
|
||||||
trace = tracetag.trace
|
|
||||||
basic_authorization trace.user.display_name, "test"
|
|
||||||
|
|
||||||
put :api_update, :params => { :id => trace.id }, :body => trace.to_xml.to_s
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
updated = Trace.find(trace.id)
|
|
||||||
# Ensure there's only one tag in the database after updating
|
|
||||||
assert_equal Tracetag.count, 1
|
|
||||||
# The new tag object might have a different id, so check the string representation
|
|
||||||
assert_equal trace.tagstring, updated.tagstring
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check deleting a trace through the api
|
|
||||||
def test_api_delete
|
|
||||||
public_trace_file = create(:trace, :visibility => "public")
|
|
||||||
|
|
||||||
# First with no auth
|
|
||||||
delete :api_delete, :params => { :id => public_trace_file.id }
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
# Now with some other user, which should fail
|
|
||||||
basic_authorization create(:user).display_name, "test"
|
|
||||||
delete :api_delete, :params => { :id => public_trace_file.id }
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# Now with a trace which doesn't exist
|
|
||||||
basic_authorization create(:user).display_name, "test"
|
|
||||||
delete :api_delete, :params => { :id => 0 }
|
|
||||||
assert_response :not_found
|
|
||||||
|
|
||||||
# And finally we should be able to do it with the owner of the trace
|
|
||||||
basic_authorization public_trace_file.user.display_name, "test"
|
|
||||||
delete :api_delete, :params => { :id => public_trace_file.id }
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# Try it a second time, which should fail
|
|
||||||
basic_authorization public_trace_file.user.display_name, "test"
|
|
||||||
delete :api_delete, :params => { :id => public_trace_file.id }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def check_trace_feed(traces)
|
def check_trace_feed(traces)
|
||||||
|
|
|
@ -1,244 +0,0 @@
|
||||||
require "test_helper"
|
|
||||||
|
|
||||||
class UserPreferencesControllerTest < ActionController::TestCase
|
|
||||||
##
|
|
||||||
# test all routes which lead to this controller
|
|
||||||
def test_routes
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/user/preferences", :method => :get },
|
|
||||||
{ :controller => "user_preferences", :action => "read" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/user/preferences", :method => :put },
|
|
||||||
{ :controller => "user_preferences", :action => "update" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/user/preferences/key", :method => :get },
|
|
||||||
{ :controller => "user_preferences", :action => "read_one", :preference_key => "key" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/user/preferences/key", :method => :put },
|
|
||||||
{ :controller => "user_preferences", :action => "update_one", :preference_key => "key" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/user/preferences/key", :method => :delete },
|
|
||||||
{ :controller => "user_preferences", :action => "delete_one", :preference_key => "key" }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test read action
|
|
||||||
def test_read
|
|
||||||
# first try without auth
|
|
||||||
get :read
|
|
||||||
assert_response :unauthorized, "should be authenticated"
|
|
||||||
|
|
||||||
# authenticate as a user with no preferences
|
|
||||||
basic_authorization create(:user).email, "test"
|
|
||||||
|
|
||||||
# try the read again
|
|
||||||
get :read
|
|
||||||
assert_select "osm" do
|
|
||||||
assert_select "preferences", :count => 1 do
|
|
||||||
assert_select "preference", :count => 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# authenticate as a user with preferences
|
|
||||||
user = create(:user)
|
|
||||||
user_preference = create(:user_preference, :user => user)
|
|
||||||
user_preference2 = create(:user_preference, :user => user)
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# try the read again
|
|
||||||
get :read
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "application/xml", @response.content_type
|
|
||||||
assert_select "osm" do
|
|
||||||
assert_select "preferences", :count => 1 do
|
|
||||||
assert_select "preference", :count => 2
|
|
||||||
assert_select "preference[k=\"#{user_preference.k}\"][v=\"#{user_preference.v}\"]", :count => 1
|
|
||||||
assert_select "preference[k=\"#{user_preference2.k}\"][v=\"#{user_preference2.v}\"]", :count => 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test read_one action
|
|
||||||
def test_read_one
|
|
||||||
user = create(:user)
|
|
||||||
create(:user_preference, :user => user, :k => "key", :v => "value")
|
|
||||||
|
|
||||||
# try a read without auth
|
|
||||||
get :read_one, :params => { :preference_key => "key" }
|
|
||||||
assert_response :unauthorized, "should be authenticated"
|
|
||||||
|
|
||||||
# authenticate as a user with preferences
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# try the read again
|
|
||||||
get :read_one, :params => { :preference_key => "key" }
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "text/plain", @response.content_type
|
|
||||||
assert_equal "value", @response.body
|
|
||||||
|
|
||||||
# try the read again for a non-existent key
|
|
||||||
get :read_one, :params => { :preference_key => "unknown_key" }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test update action
|
|
||||||
def test_update
|
|
||||||
user = create(:user)
|
|
||||||
create(:user_preference, :user => user, :k => "key", :v => "value")
|
|
||||||
create(:user_preference, :user => user, :k => "some_key", :v => "some_value")
|
|
||||||
|
|
||||||
# try a put without auth
|
|
||||||
assert_no_difference "UserPreference.count" do
|
|
||||||
put :update, :body => "<osm><preferences><preference k='key' v='new_value'/><preference k='new_key' v='value'/></preferences></osm>"
|
|
||||||
end
|
|
||||||
assert_response :unauthorized, "should be authenticated"
|
|
||||||
assert_equal "value", UserPreference.find([user.id, "key"]).v
|
|
||||||
assert_equal "some_value", UserPreference.find([user.id, "some_key"]).v
|
|
||||||
assert_raises ActiveRecord::RecordNotFound do
|
|
||||||
UserPreference.find([user.id, "new_key"])
|
|
||||||
end
|
|
||||||
|
|
||||||
# authenticate as a user with preferences
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# try the put again
|
|
||||||
assert_no_difference "UserPreference.count" do
|
|
||||||
put :update, :body => "<osm><preferences><preference k='key' v='new_value'/><preference k='new_key' v='value'/></preferences></osm>"
|
|
||||||
end
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "text/plain", @response.content_type
|
|
||||||
assert_equal "", @response.body
|
|
||||||
assert_equal "new_value", UserPreference.find([user.id, "key"]).v
|
|
||||||
assert_equal "value", UserPreference.find([user.id, "new_key"]).v
|
|
||||||
assert_raises ActiveRecord::RecordNotFound do
|
|
||||||
UserPreference.find([user.id, "some_key"])
|
|
||||||
end
|
|
||||||
|
|
||||||
# try a put with duplicate keys
|
|
||||||
assert_no_difference "UserPreference.count" do
|
|
||||||
put :update, :body => "<osm><preferences><preference k='key' v='value'/><preference k='key' v='newer_value'/></preferences></osm>"
|
|
||||||
end
|
|
||||||
assert_response :bad_request
|
|
||||||
assert_equal "text/plain", @response.content_type
|
|
||||||
assert_equal "Duplicate preferences with key key", @response.body
|
|
||||||
assert_equal "new_value", UserPreference.find([user.id, "key"]).v
|
|
||||||
|
|
||||||
# try a put with invalid content
|
|
||||||
assert_no_difference "UserPreference.count" do
|
|
||||||
put :update, :body => "nonsense"
|
|
||||||
end
|
|
||||||
assert_response :bad_request
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test update_one action
|
|
||||||
def test_update_one
|
|
||||||
user = create(:user)
|
|
||||||
create(:user_preference, :user => user)
|
|
||||||
|
|
||||||
# try a put without auth
|
|
||||||
assert_no_difference "UserPreference.count" do
|
|
||||||
put :update_one, :params => { :preference_key => "new_key" }, :body => "new_value"
|
|
||||||
end
|
|
||||||
assert_response :unauthorized, "should be authenticated"
|
|
||||||
assert_raises ActiveRecord::RecordNotFound do
|
|
||||||
UserPreference.find([user.id, "new_key"])
|
|
||||||
end
|
|
||||||
|
|
||||||
# authenticate as a user with preferences
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# try adding a new preference
|
|
||||||
assert_difference "UserPreference.count", 1 do
|
|
||||||
put :update_one, :params => { :preference_key => "new_key" }, :body => "new_value"
|
|
||||||
end
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "text/plain", @response.content_type
|
|
||||||
assert_equal "", @response.body
|
|
||||||
assert_equal "new_value", UserPreference.find([user.id, "new_key"]).v
|
|
||||||
|
|
||||||
# try changing the value of a preference
|
|
||||||
assert_no_difference "UserPreference.count" do
|
|
||||||
put :update_one, :params => { :preference_key => "new_key" }, :body => "newer_value"
|
|
||||||
end
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "text/plain", @response.content_type
|
|
||||||
assert_equal "", @response.body
|
|
||||||
assert_equal "newer_value", UserPreference.find([user.id, "new_key"]).v
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test delete_one action
|
|
||||||
def test_delete_one
|
|
||||||
user = create(:user)
|
|
||||||
create(:user_preference, :user => user, :k => "key", :v => "value")
|
|
||||||
|
|
||||||
# try a delete without auth
|
|
||||||
assert_no_difference "UserPreference.count" do
|
|
||||||
delete :delete_one, :params => { :preference_key => "key" }
|
|
||||||
end
|
|
||||||
assert_response :unauthorized, "should be authenticated"
|
|
||||||
assert_equal "value", UserPreference.find([user.id, "key"]).v
|
|
||||||
|
|
||||||
# authenticate as a user with preferences
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# try the delete again
|
|
||||||
assert_difference "UserPreference.count", -1 do
|
|
||||||
get :delete_one, :params => { :preference_key => "key" }
|
|
||||||
end
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "text/plain", @response.content_type
|
|
||||||
assert_equal "", @response.body
|
|
||||||
assert_raises ActiveRecord::RecordNotFound do
|
|
||||||
UserPreference.find([user.id, "key"])
|
|
||||||
end
|
|
||||||
|
|
||||||
# try the delete again for the same key
|
|
||||||
assert_no_difference "UserPreference.count" do
|
|
||||||
get :delete_one, :params => { :preference_key => "key" }
|
|
||||||
end
|
|
||||||
assert_response :not_found
|
|
||||||
assert_raises ActiveRecord::RecordNotFound do
|
|
||||||
UserPreference.find([user.id, "key"])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Ensure that a valid access token with correct capabilities can be used to
|
|
||||||
# read preferences
|
|
||||||
def test_read_one_using_token
|
|
||||||
user = create(:user)
|
|
||||||
token = create(:access_token, :user => user, :allow_read_prefs => true)
|
|
||||||
create(:user_preference, :user => user, :k => "key", :v => "value")
|
|
||||||
|
|
||||||
# Hack together an oauth request - an alternative would be to sign the request properly
|
|
||||||
@request.env["oauth.version"] = 1
|
|
||||||
@request.env["oauth.strategies"] = [:token]
|
|
||||||
@request.env["oauth.token"] = token
|
|
||||||
|
|
||||||
get :read_one, :params => { :preference_key => "key" }
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
|
|
||||||
# Ensure that a valid access token with incorrect capabilities can't be used
|
|
||||||
# to read preferences even, though the owner of that token could read them
|
|
||||||
# by other methods.
|
|
||||||
def test_read_one_using_token_fail
|
|
||||||
user = create(:user)
|
|
||||||
token = create(:access_token, :user => user, :allow_read_prefs => false)
|
|
||||||
create(:user_preference, :user => user, :k => "key", :v => "value")
|
|
||||||
@request.env["oauth.version"] = 1
|
|
||||||
@request.env["oauth.strategies"] = [:token]
|
|
||||||
@request.env["oauth.token"] = token
|
|
||||||
|
|
||||||
get :read_one, :params => { :preference_key => "key" }
|
|
||||||
assert_response :forbidden
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -8,23 +8,6 @@ class UsersControllerTest < ActionController::TestCase
|
||||||
##
|
##
|
||||||
# test all routes which lead to this controller
|
# test all routes which lead to this controller
|
||||||
def test_routes
|
def test_routes
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/user/1", :method => :get },
|
|
||||||
{ :controller => "users", :action => "api_read", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/user/details", :method => :get },
|
|
||||||
{ :controller => "users", :action => "api_details" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/user/gpx_files", :method => :get },
|
|
||||||
{ :controller => "users", :action => "api_gpx_files" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/users", :method => :get },
|
|
||||||
{ :controller => "users", :action => "api_users" }
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_routing(
|
assert_routing(
|
||||||
{ :path => "/login", :method => :get },
|
{ :path => "/login", :method => :get },
|
||||||
{ :controller => "users", :action => "login" }
|
{ :controller => "users", :action => "login" }
|
||||||
|
@ -1123,175 +1106,6 @@ class UsersControllerTest < ActionController::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_api_read
|
|
||||||
user = create(:user, :description => "test", :terms_agreed => Date.yesterday)
|
|
||||||
# check that a visible user is returned properly
|
|
||||||
get :api_read, :params => { :id => user.id }
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "text/xml", response.content_type
|
|
||||||
|
|
||||||
# check the data that is returned
|
|
||||||
assert_select "description", :count => 1, :text => "test"
|
|
||||||
assert_select "contributor-terms", :count => 1 do
|
|
||||||
assert_select "[agreed='true']"
|
|
||||||
end
|
|
||||||
assert_select "img", :count => 0
|
|
||||||
assert_select "roles", :count => 1 do
|
|
||||||
assert_select "role", :count => 0
|
|
||||||
end
|
|
||||||
assert_select "changesets", :count => 1 do
|
|
||||||
assert_select "[count='0']"
|
|
||||||
end
|
|
||||||
assert_select "traces", :count => 1 do
|
|
||||||
assert_select "[count='0']"
|
|
||||||
end
|
|
||||||
assert_select "blocks", :count => 1 do
|
|
||||||
assert_select "received", :count => 1 do
|
|
||||||
assert_select "[count='0'][active='0']"
|
|
||||||
end
|
|
||||||
assert_select "issued", :count => 0
|
|
||||||
end
|
|
||||||
|
|
||||||
# check that we aren't revealing private information
|
|
||||||
assert_select "contributor-terms[pd]", false
|
|
||||||
assert_select "home", false
|
|
||||||
assert_select "languages", false
|
|
||||||
assert_select "messages", false
|
|
||||||
|
|
||||||
# check that a suspended user is not returned
|
|
||||||
get :api_read, :params => { :id => create(:user, :suspended).id }
|
|
||||||
assert_response :gone
|
|
||||||
|
|
||||||
# check that a deleted user is not returned
|
|
||||||
get :api_read, :params => { :id => create(:user, :deleted).id }
|
|
||||||
assert_response :gone
|
|
||||||
|
|
||||||
# check that a non-existent user is not returned
|
|
||||||
get :api_read, :params => { :id => 0 }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_api_details
|
|
||||||
user = create(:user, :description => "test", :terms_agreed => Date.yesterday, :home_lat => 12.1, :home_lon => 12.1, :languages => ["en"])
|
|
||||||
create(:message, :read, :recipient => user)
|
|
||||||
create(:message, :sender => user)
|
|
||||||
|
|
||||||
# check that nothing is returned when not logged in
|
|
||||||
get :api_details
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
# check that we get a response when logged in
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
get :api_details
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "text/xml", response.content_type
|
|
||||||
|
|
||||||
# check the data that is returned
|
|
||||||
assert_select "description", :count => 1, :text => "test"
|
|
||||||
assert_select "contributor-terms", :count => 1 do
|
|
||||||
assert_select "[agreed='true'][pd='false']"
|
|
||||||
end
|
|
||||||
assert_select "img", :count => 0
|
|
||||||
assert_select "roles", :count => 1 do
|
|
||||||
assert_select "role", :count => 0
|
|
||||||
end
|
|
||||||
assert_select "changesets", :count => 1 do
|
|
||||||
assert_select "[count='0']", :count => 1
|
|
||||||
end
|
|
||||||
assert_select "traces", :count => 1 do
|
|
||||||
assert_select "[count='0']", :count => 1
|
|
||||||
end
|
|
||||||
assert_select "blocks", :count => 1 do
|
|
||||||
assert_select "received", :count => 1 do
|
|
||||||
assert_select "[count='0'][active='0']"
|
|
||||||
end
|
|
||||||
assert_select "issued", :count => 0
|
|
||||||
end
|
|
||||||
assert_select "home", :count => 1 do
|
|
||||||
assert_select "[lat='12.1'][lon='12.1'][zoom='3']"
|
|
||||||
end
|
|
||||||
assert_select "languages", :count => 1 do
|
|
||||||
assert_select "lang", :count => 1, :text => "en"
|
|
||||||
end
|
|
||||||
assert_select "messages", :count => 1 do
|
|
||||||
assert_select "received", :count => 1 do
|
|
||||||
assert_select "[count='1'][unread='0']"
|
|
||||||
end
|
|
||||||
assert_select "sent", :count => 1 do
|
|
||||||
assert_select "[count='1']"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_api_users
|
|
||||||
user1 = create(:user, :description => "test1", :terms_agreed => Date.yesterday)
|
|
||||||
user2 = create(:user, :description => "test2", :terms_agreed => Date.yesterday)
|
|
||||||
user3 = create(:user, :description => "test3", :terms_agreed => Date.yesterday)
|
|
||||||
|
|
||||||
get :api_users, :params => { :users => user1.id }
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "text/xml", response.content_type
|
|
||||||
assert_select "user", :count => 1 do
|
|
||||||
assert_select "user[id='#{user1.id}']", :count => 1
|
|
||||||
assert_select "user[id='#{user2.id}']", :count => 0
|
|
||||||
assert_select "user[id='#{user3.id}']", :count => 0
|
|
||||||
end
|
|
||||||
|
|
||||||
get :api_users, :params => { :users => user2.id }
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "text/xml", response.content_type
|
|
||||||
assert_select "user", :count => 1 do
|
|
||||||
assert_select "user[id='#{user1.id}']", :count => 0
|
|
||||||
assert_select "user[id='#{user2.id}']", :count => 1
|
|
||||||
assert_select "user[id='#{user3.id}']", :count => 0
|
|
||||||
end
|
|
||||||
|
|
||||||
get :api_users, :params => { :users => "#{user1.id},#{user3.id}" }
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "text/xml", response.content_type
|
|
||||||
assert_select "user", :count => 2 do
|
|
||||||
assert_select "user[id='#{user1.id}']", :count => 1
|
|
||||||
assert_select "user[id='#{user2.id}']", :count => 0
|
|
||||||
assert_select "user[id='#{user3.id}']", :count => 1
|
|
||||||
end
|
|
||||||
|
|
||||||
get :api_users, :params => { :users => create(:user, :suspended).id }
|
|
||||||
assert_response :not_found
|
|
||||||
|
|
||||||
get :api_users, :params => { :users => create(:user, :deleted).id }
|
|
||||||
assert_response :not_found
|
|
||||||
|
|
||||||
get :api_users, :params => { :users => 0 }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_api_gpx_files
|
|
||||||
user = create(:user)
|
|
||||||
trace1 = create(:trace, :user => user) do |trace|
|
|
||||||
create(:tracetag, :trace => trace, :tag => "London")
|
|
||||||
end
|
|
||||||
trace2 = create(:trace, :user => user) do |trace|
|
|
||||||
create(:tracetag, :trace => trace, :tag => "Birmingham")
|
|
||||||
end
|
|
||||||
# check that nothing is returned when not logged in
|
|
||||||
get :api_gpx_files
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
# check that we get a response when logged in
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
get :api_gpx_files
|
|
||||||
assert_response :success
|
|
||||||
assert_equal "application/xml", response.content_type
|
|
||||||
|
|
||||||
# check the data that is returned
|
|
||||||
assert_select "gpx_file[id='#{trace1.id}']", 1 do
|
|
||||||
assert_select "tag", "London"
|
|
||||||
end
|
|
||||||
assert_select "gpx_file[id='#{trace2.id}']", 1 do
|
|
||||||
assert_select "tag", "Birmingham"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_make_friend
|
def test_make_friend
|
||||||
# Get users to work with
|
# Get users to work with
|
||||||
user = create(:user)
|
user = create(:user)
|
||||||
|
|
|
@ -1,752 +0,0 @@
|
||||||
require "test_helper"
|
|
||||||
|
|
||||||
class WaysControllerTest < ActionController::TestCase
|
|
||||||
##
|
|
||||||
# test all routes which lead to this controller
|
|
||||||
def test_routes
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/way/create", :method => :put },
|
|
||||||
{ :controller => "ways", :action => "create" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/way/1/full", :method => :get },
|
|
||||||
{ :controller => "ways", :action => "full", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/way/1", :method => :get },
|
|
||||||
{ :controller => "ways", :action => "show", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/way/1", :method => :put },
|
|
||||||
{ :controller => "ways", :action => "update", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/way/1", :method => :delete },
|
|
||||||
{ :controller => "ways", :action => "delete", :id => "1" }
|
|
||||||
)
|
|
||||||
assert_routing(
|
|
||||||
{ :path => "/api/0.6/ways", :method => :get },
|
|
||||||
{ :controller => "ways", :action => "index" }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
# -------------------------------------
|
|
||||||
# Test showing ways.
|
|
||||||
# -------------------------------------
|
|
||||||
|
|
||||||
def test_show
|
|
||||||
# check that a visible way is returned properly
|
|
||||||
get :show, :params => { :id => create(:way).id }
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# check that an invisible way is not returned
|
|
||||||
get :show, :params => { :id => create(:way, :deleted).id }
|
|
||||||
assert_response :gone
|
|
||||||
|
|
||||||
# check chat a non-existent way is not returned
|
|
||||||
get :show, :params => { :id => 0 }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# check the "full" mode
|
|
||||||
def test_full
|
|
||||||
Way.all.each do |way|
|
|
||||||
get :full, :params => { :id => way.id }
|
|
||||||
|
|
||||||
# full call should say "gone" for non-visible ways...
|
|
||||||
unless way.visible
|
|
||||||
assert_response :gone
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
# otherwise it should say success
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# Check the way is correctly returned
|
|
||||||
assert_select "osm way[id='#{way.id}'][version='#{way.version}'][visible='#{way.visible}']", 1
|
|
||||||
|
|
||||||
# check that each node in the way appears once in the output as a
|
|
||||||
# reference and as the node element.
|
|
||||||
way.nodes.each do |n|
|
|
||||||
count = (way.nodes - (way.nodes - [n])).length
|
|
||||||
assert_select "osm way nd[ref='#{n.id}']", count
|
|
||||||
assert_select "osm node[id='#{n.id}'][version='#{n.version}'][lat='#{format('%.7f', n.lat)}'][lon='#{format('%.7f', n.lon)}']", 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test fetching multiple ways
|
|
||||||
def test_index
|
|
||||||
way1 = create(:way)
|
|
||||||
way2 = create(:way, :deleted)
|
|
||||||
way3 = create(:way)
|
|
||||||
way4 = create(:way)
|
|
||||||
|
|
||||||
# check error when no parameter provided
|
|
||||||
get :index
|
|
||||||
assert_response :bad_request
|
|
||||||
|
|
||||||
# check error when no parameter value provided
|
|
||||||
get :index, :params => { :ways => "" }
|
|
||||||
assert_response :bad_request
|
|
||||||
|
|
||||||
# test a working call
|
|
||||||
get :index, :params => { :ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id}" }
|
|
||||||
assert_response :success
|
|
||||||
assert_select "osm" do
|
|
||||||
assert_select "way", :count => 4
|
|
||||||
assert_select "way[id='#{way1.id}'][visible='true']", :count => 1
|
|
||||||
assert_select "way[id='#{way2.id}'][visible='false']", :count => 1
|
|
||||||
assert_select "way[id='#{way3.id}'][visible='true']", :count => 1
|
|
||||||
assert_select "way[id='#{way4.id}'][visible='true']", :count => 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# check error when a non-existent way is included
|
|
||||||
get :index, :params => { :ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id},0" }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
# -------------------------------------
|
|
||||||
# Test simple way creation.
|
|
||||||
# -------------------------------------
|
|
||||||
|
|
||||||
def test_create
|
|
||||||
node1 = create(:node)
|
|
||||||
node2 = create(:node)
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_changeset = create(:changeset, :user => private_user)
|
|
||||||
user = create(:user)
|
|
||||||
changeset = create(:changeset, :user => user)
|
|
||||||
|
|
||||||
## First check that it fails when creating a way using a non-public user
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
# use the first user's open changeset
|
|
||||||
changeset_id = private_changeset.id
|
|
||||||
|
|
||||||
# create a way with pre-existing nodes
|
|
||||||
xml = "<osm><way changeset='#{changeset_id}'>" \
|
|
||||||
"<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
|
|
||||||
"<tag k='test' v='yes' /></way></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# hope for failure
|
|
||||||
assert_response :forbidden,
|
|
||||||
"way upload did not return forbidden status"
|
|
||||||
|
|
||||||
## Now use a public user
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# use the first user's open changeset
|
|
||||||
changeset_id = changeset.id
|
|
||||||
|
|
||||||
# create a way with pre-existing nodes
|
|
||||||
xml = "<osm><way changeset='#{changeset_id}'>" \
|
|
||||||
"<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
|
|
||||||
"<tag k='test' v='yes' /></way></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# hope for success
|
|
||||||
assert_response :success,
|
|
||||||
"way upload did not return success status"
|
|
||||||
# read id of created way and search for it
|
|
||||||
wayid = @response.body
|
|
||||||
checkway = Way.find(wayid)
|
|
||||||
assert_not_nil checkway,
|
|
||||||
"uploaded way not found in data base after upload"
|
|
||||||
# compare values
|
|
||||||
assert_equal checkway.nds.length, 2,
|
|
||||||
"saved way does not contain exactly one node"
|
|
||||||
assert_equal checkway.nds[0], node1.id,
|
|
||||||
"saved way does not contain the right node on pos 0"
|
|
||||||
assert_equal checkway.nds[1], node2.id,
|
|
||||||
"saved way does not contain the right node on pos 1"
|
|
||||||
assert_equal checkway.changeset_id, changeset_id,
|
|
||||||
"saved way does not belong to the correct changeset"
|
|
||||||
assert_equal user.id, checkway.changeset.user_id,
|
|
||||||
"saved way does not belong to user that created it"
|
|
||||||
assert_equal true, checkway.visible,
|
|
||||||
"saved way is not visible"
|
|
||||||
end
|
|
||||||
|
|
||||||
# -------------------------------------
|
|
||||||
# Test creating some invalid ways.
|
|
||||||
# -------------------------------------
|
|
||||||
|
|
||||||
def test_create_invalid
|
|
||||||
node = create(:node)
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_open_changeset = create(:changeset, :user => private_user)
|
|
||||||
private_closed_changeset = create(:changeset, :closed, :user => private_user)
|
|
||||||
user = create(:user)
|
|
||||||
open_changeset = create(:changeset, :user => user)
|
|
||||||
closed_changeset = create(:changeset, :closed, :user => user)
|
|
||||||
|
|
||||||
## First test with a private user to make sure that they are not authorized
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
# use the first user's open changeset
|
|
||||||
# create a way with non-existing node
|
|
||||||
xml = "<osm><way changeset='#{private_open_changeset.id}'>" \
|
|
||||||
"<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# expect failure
|
|
||||||
assert_response :forbidden,
|
|
||||||
"way upload with invalid node using a private user did not return 'forbidden'"
|
|
||||||
|
|
||||||
# create a way with no nodes
|
|
||||||
xml = "<osm><way changeset='#{private_open_changeset.id}'>" \
|
|
||||||
"<tag k='test' v='yes' /></way></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# expect failure
|
|
||||||
assert_response :forbidden,
|
|
||||||
"way upload with no node using a private userdid not return 'forbidden'"
|
|
||||||
|
|
||||||
# create a way inside a closed changeset
|
|
||||||
xml = "<osm><way changeset='#{private_closed_changeset.id}'>" \
|
|
||||||
"<nd ref='#{node.id}'/></way></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# expect failure
|
|
||||||
assert_response :forbidden,
|
|
||||||
"way upload to closed changeset with a private user did not return 'forbidden'"
|
|
||||||
|
|
||||||
## Now test with a public user
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# use the first user's open changeset
|
|
||||||
# create a way with non-existing node
|
|
||||||
xml = "<osm><way changeset='#{open_changeset.id}'>" \
|
|
||||||
"<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# expect failure
|
|
||||||
assert_response :precondition_failed,
|
|
||||||
"way upload with invalid node did not return 'precondition failed'"
|
|
||||||
assert_equal "Precondition failed: Way requires the nodes with id in (0), which either do not exist, or are not visible.", @response.body
|
|
||||||
|
|
||||||
# create a way with no nodes
|
|
||||||
xml = "<osm><way changeset='#{open_changeset.id}'>" \
|
|
||||||
"<tag k='test' v='yes' /></way></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# expect failure
|
|
||||||
assert_response :precondition_failed,
|
|
||||||
"way upload with no node did not return 'precondition failed'"
|
|
||||||
assert_equal "Precondition failed: Cannot create way: data is invalid.", @response.body
|
|
||||||
|
|
||||||
# create a way inside a closed changeset
|
|
||||||
xml = "<osm><way changeset='#{closed_changeset.id}'>" \
|
|
||||||
"<nd ref='#{node.id}'/></way></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# expect failure
|
|
||||||
assert_response :conflict,
|
|
||||||
"way upload to closed changeset did not return 'conflict'"
|
|
||||||
|
|
||||||
# create a way with a tag which is too long
|
|
||||||
xml = "<osm><way changeset='#{open_changeset.id}'>" \
|
|
||||||
"<nd ref='#{node.id}'/>" \
|
|
||||||
"<tag k='foo' v='#{'x' * 256}'/>" \
|
|
||||||
"</way></osm>"
|
|
||||||
put :create, :body => xml
|
|
||||||
# expect failure
|
|
||||||
assert_response :bad_request,
|
|
||||||
"way upload to with too long tag did not return 'bad_request'"
|
|
||||||
end
|
|
||||||
|
|
||||||
# -------------------------------------
|
|
||||||
# Test deleting ways.
|
|
||||||
# -------------------------------------
|
|
||||||
|
|
||||||
def test_delete
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_open_changeset = create(:changeset, :user => private_user)
|
|
||||||
private_closed_changeset = create(:changeset, :closed, :user => private_user)
|
|
||||||
private_way = create(:way, :changeset => private_open_changeset)
|
|
||||||
private_deleted_way = create(:way, :deleted, :changeset => private_open_changeset)
|
|
||||||
private_used_way = create(:way, :changeset => private_open_changeset)
|
|
||||||
create(:relation_member, :member => private_used_way)
|
|
||||||
user = create(:user)
|
|
||||||
open_changeset = create(:changeset, :user => user)
|
|
||||||
closed_changeset = create(:changeset, :closed, :user => user)
|
|
||||||
way = create(:way, :changeset => open_changeset)
|
|
||||||
deleted_way = create(:way, :deleted, :changeset => open_changeset)
|
|
||||||
used_way = create(:way, :changeset => open_changeset)
|
|
||||||
relation_member = create(:relation_member, :member => used_way)
|
|
||||||
relation = relation_member.relation
|
|
||||||
|
|
||||||
# first try to delete way without auth
|
|
||||||
delete :delete, :params => { :id => way.id }
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
# now set auth using the private user
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
# this shouldn't work as with the 0.6 api we need pay load to delete
|
|
||||||
delete :delete, :params => { :id => private_way.id }
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# Now try without having a changeset
|
|
||||||
xml = "<osm><way id='#{private_way.id}'/></osm>"
|
|
||||||
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# try to delete with an invalid (closed) changeset
|
|
||||||
xml = update_changeset(private_way.to_xml, private_closed_changeset.id)
|
|
||||||
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# try to delete with an invalid (non-existent) changeset
|
|
||||||
xml = update_changeset(private_way.to_xml, 0)
|
|
||||||
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# Now try with a valid changeset
|
|
||||||
xml = private_way.to_xml
|
|
||||||
delete :delete, :params => { :id => private_way.id }, :body => xml.to_s
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# check the returned value - should be the new version number
|
|
||||||
# valid delete should return the new version number, which should
|
|
||||||
# be greater than the old version number
|
|
||||||
# assert @response.body.to_i > current_ways(:visible_way).version,
|
|
||||||
# "delete request should return a new version number for way"
|
|
||||||
|
|
||||||
# this won't work since the way is already deleted
|
|
||||||
xml = private_deleted_way.to_xml
|
|
||||||
delete :delete, :params => { :id => private_deleted_way.id }, :body => xml.to_s
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
# this shouldn't work as the way is used in a relation
|
|
||||||
xml = private_used_way.to_xml
|
|
||||||
delete :delete, :params => { :id => private_used_way.id }, :body => xml.to_s
|
|
||||||
assert_response :forbidden,
|
|
||||||
"shouldn't be able to delete a way used in a relation (#{@response.body}), when done by a private user"
|
|
||||||
|
|
||||||
# this won't work since the way never existed
|
|
||||||
delete :delete, :params => { :id => 0 }
|
|
||||||
assert_response :forbidden
|
|
||||||
|
|
||||||
### Now check with a public user
|
|
||||||
# now set auth
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# this shouldn't work as with the 0.6 api we need pay load to delete
|
|
||||||
delete :delete, :params => { :id => way.id }
|
|
||||||
assert_response :bad_request
|
|
||||||
|
|
||||||
# Now try without having a changeset
|
|
||||||
xml = "<osm><way id='#{way.id}'/></osm>"
|
|
||||||
delete :delete, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :bad_request
|
|
||||||
|
|
||||||
# try to delete with an invalid (closed) changeset
|
|
||||||
xml = update_changeset(way.to_xml, closed_changeset.id)
|
|
||||||
delete :delete, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict
|
|
||||||
|
|
||||||
# try to delete with an invalid (non-existent) changeset
|
|
||||||
xml = update_changeset(way.to_xml, 0)
|
|
||||||
delete :delete, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict
|
|
||||||
|
|
||||||
# Now try with a valid changeset
|
|
||||||
xml = way.to_xml
|
|
||||||
delete :delete, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# check the returned value - should be the new version number
|
|
||||||
# valid delete should return the new version number, which should
|
|
||||||
# be greater than the old version number
|
|
||||||
assert @response.body.to_i > way.version,
|
|
||||||
"delete request should return a new version number for way"
|
|
||||||
|
|
||||||
# this won't work since the way is already deleted
|
|
||||||
xml = deleted_way.to_xml
|
|
||||||
delete :delete, :params => { :id => deleted_way.id }, :body => xml.to_s
|
|
||||||
assert_response :gone
|
|
||||||
|
|
||||||
# this shouldn't work as the way is used in a relation
|
|
||||||
xml = used_way.to_xml
|
|
||||||
delete :delete, :params => { :id => used_way.id }, :body => xml.to_s
|
|
||||||
assert_response :precondition_failed,
|
|
||||||
"shouldn't be able to delete a way used in a relation (#{@response.body})"
|
|
||||||
assert_equal "Precondition failed: Way #{used_way.id} is still used by relations #{relation.id}.", @response.body
|
|
||||||
|
|
||||||
# this won't work since the way never existed
|
|
||||||
delete :delete, :params => { :id => 0 }
|
|
||||||
assert_response :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# tests whether the API works and prevents incorrect use while trying
|
|
||||||
# to update ways.
|
|
||||||
def test_update
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_way = create(:way, :changeset => create(:changeset, :user => private_user))
|
|
||||||
user = create(:user)
|
|
||||||
way = create(:way, :changeset => create(:changeset, :user => user))
|
|
||||||
node = create(:node)
|
|
||||||
create(:way_node, :way => private_way, :node => node)
|
|
||||||
create(:way_node, :way => way, :node => node)
|
|
||||||
|
|
||||||
## First test with no user credentials
|
|
||||||
# try and update a way without authorisation
|
|
||||||
xml = way.to_xml
|
|
||||||
put :update, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :unauthorized
|
|
||||||
|
|
||||||
## Second test with the private user
|
|
||||||
|
|
||||||
# setup auth
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
## trying to break changesets
|
|
||||||
|
|
||||||
# try and update in someone else's changeset
|
|
||||||
xml = update_changeset(private_way.to_xml,
|
|
||||||
create(:changeset).id)
|
|
||||||
put :update, :params => { :id => private_way.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "update with other user's changeset should be forbidden when date isn't public"
|
|
||||||
|
|
||||||
# try and update in a closed changeset
|
|
||||||
xml = update_changeset(private_way.to_xml,
|
|
||||||
create(:changeset, :closed, :user => private_user).id)
|
|
||||||
put :update, :params => { :id => private_way.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "update with closed changeset should be forbidden, when data isn't public"
|
|
||||||
|
|
||||||
# try and update in a non-existant changeset
|
|
||||||
xml = update_changeset(private_way.to_xml, 0)
|
|
||||||
put :update, :params => { :id => private_way.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data("update with changeset=0 should be forbidden, when data isn't public")
|
|
||||||
|
|
||||||
## try and submit invalid updates
|
|
||||||
xml = xml_replace_node(private_way.to_xml, node.id, 9999)
|
|
||||||
put :update, :params => { :id => private_way.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "way with non-existent node should be forbidden, when data isn't public"
|
|
||||||
|
|
||||||
xml = xml_replace_node(private_way.to_xml, node.id, create(:node, :deleted).id)
|
|
||||||
put :update, :params => { :id => private_way.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "way with deleted node should be forbidden, when data isn't public"
|
|
||||||
|
|
||||||
## finally, produce a good request which will still not work
|
|
||||||
xml = private_way.to_xml
|
|
||||||
put :update, :params => { :id => private_way.id }, :body => xml.to_s
|
|
||||||
assert_require_public_data "should have failed with a forbidden when data isn't public"
|
|
||||||
|
|
||||||
## Finally test with the public user
|
|
||||||
|
|
||||||
# setup auth
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
## trying to break changesets
|
|
||||||
|
|
||||||
# try and update in someone else's changeset
|
|
||||||
xml = update_changeset(way.to_xml,
|
|
||||||
create(:changeset).id)
|
|
||||||
put :update, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict, "update with other user's changeset should be rejected"
|
|
||||||
|
|
||||||
# try and update in a closed changeset
|
|
||||||
xml = update_changeset(way.to_xml,
|
|
||||||
create(:changeset, :closed, :user => user).id)
|
|
||||||
put :update, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict, "update with closed changeset should be rejected"
|
|
||||||
|
|
||||||
# try and update in a non-existant changeset
|
|
||||||
xml = update_changeset(way.to_xml, 0)
|
|
||||||
put :update, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict, "update with changeset=0 should be rejected"
|
|
||||||
|
|
||||||
## try and submit invalid updates
|
|
||||||
xml = xml_replace_node(way.to_xml, node.id, 9999)
|
|
||||||
put :update, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :precondition_failed, "way with non-existent node should be rejected"
|
|
||||||
|
|
||||||
xml = xml_replace_node(way.to_xml, node.id, create(:node, :deleted).id)
|
|
||||||
put :update, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :precondition_failed, "way with deleted node should be rejected"
|
|
||||||
|
|
||||||
## next, attack the versioning
|
|
||||||
current_way_version = way.version
|
|
||||||
|
|
||||||
# try and submit a version behind
|
|
||||||
xml = xml_attr_rewrite(way.to_xml,
|
|
||||||
"version", current_way_version - 1)
|
|
||||||
put :update, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict, "should have failed on old version number"
|
|
||||||
|
|
||||||
# try and submit a version ahead
|
|
||||||
xml = xml_attr_rewrite(way.to_xml,
|
|
||||||
"version", current_way_version + 1)
|
|
||||||
put :update, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict, "should have failed on skipped version number"
|
|
||||||
|
|
||||||
# try and submit total crap in the version field
|
|
||||||
xml = xml_attr_rewrite(way.to_xml,
|
|
||||||
"version", "p1r4t3s!")
|
|
||||||
put :update, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :conflict,
|
|
||||||
"should not be able to put 'p1r4at3s!' in the version field"
|
|
||||||
|
|
||||||
## try an update with the wrong ID
|
|
||||||
xml = create(:way).to_xml
|
|
||||||
put :update, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :bad_request,
|
|
||||||
"should not be able to update a way with a different ID from the XML"
|
|
||||||
|
|
||||||
## try an update with a minimal valid XML doc which isn't a well-formed OSM doc.
|
|
||||||
xml = "<update/>"
|
|
||||||
put :update, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :bad_request,
|
|
||||||
"should not be able to update a way with non-OSM XML doc."
|
|
||||||
|
|
||||||
## finally, produce a good request which should work
|
|
||||||
xml = way.to_xml
|
|
||||||
put :update, :params => { :id => way.id }, :body => xml.to_s
|
|
||||||
assert_response :success, "a valid update request failed"
|
|
||||||
end
|
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# test tags handling
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
|
|
||||||
##
|
|
||||||
# Try adding a new tag to a way
|
|
||||||
def test_add_tags
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => private_user))
|
|
||||||
user = create(:user)
|
|
||||||
way = create(:way_with_nodes, :nodes_count => 2, :changeset => create(:changeset, :user => user))
|
|
||||||
|
|
||||||
## Try with the non-public user
|
|
||||||
# setup auth
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
# add an identical tag to the way
|
|
||||||
tag_xml = XML::Node.new("tag")
|
|
||||||
tag_xml["k"] = "new"
|
|
||||||
tag_xml["v"] = "yes"
|
|
||||||
|
|
||||||
# add the tag into the existing xml
|
|
||||||
way_xml = private_way.to_xml
|
|
||||||
way_xml.find("//osm/way").first << tag_xml
|
|
||||||
|
|
||||||
# try and upload it
|
|
||||||
put :update, :params => { :id => private_way.id }, :body => way_xml.to_s
|
|
||||||
assert_response :forbidden,
|
|
||||||
"adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
|
|
||||||
|
|
||||||
## Now try with the public user
|
|
||||||
# setup auth
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# add an identical tag to the way
|
|
||||||
tag_xml = XML::Node.new("tag")
|
|
||||||
tag_xml["k"] = "new"
|
|
||||||
tag_xml["v"] = "yes"
|
|
||||||
|
|
||||||
# add the tag into the existing xml
|
|
||||||
way_xml = way.to_xml
|
|
||||||
way_xml.find("//osm/way").first << tag_xml
|
|
||||||
|
|
||||||
# try and upload it
|
|
||||||
put :update, :params => { :id => way.id }, :body => way_xml.to_s
|
|
||||||
assert_response :success,
|
|
||||||
"adding a new tag to a way should succeed"
|
|
||||||
assert_equal way.version + 1, @response.body.to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Try adding a duplicate of an existing tag to a way
|
|
||||||
def test_add_duplicate_tags
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_way = create(:way, :changeset => create(:changeset, :user => private_user))
|
|
||||||
private_existing_tag = create(:way_tag, :way => private_way)
|
|
||||||
user = create(:user)
|
|
||||||
way = create(:way, :changeset => create(:changeset, :user => user))
|
|
||||||
existing_tag = create(:way_tag, :way => way)
|
|
||||||
|
|
||||||
## Try with the non-public user
|
|
||||||
# setup auth
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
# add an identical tag to the way
|
|
||||||
tag_xml = XML::Node.new("tag")
|
|
||||||
tag_xml["k"] = private_existing_tag.k
|
|
||||||
tag_xml["v"] = private_existing_tag.v
|
|
||||||
|
|
||||||
# add the tag into the existing xml
|
|
||||||
way_xml = private_way.to_xml
|
|
||||||
way_xml.find("//osm/way").first << tag_xml
|
|
||||||
|
|
||||||
# try and upload it
|
|
||||||
put :update, :params => { :id => private_way.id }, :body => way_xml.to_s
|
|
||||||
assert_response :forbidden,
|
|
||||||
"adding a duplicate tag to a way for a non-public should fail with 'forbidden'"
|
|
||||||
|
|
||||||
## Now try with the public user
|
|
||||||
# setup auth
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# add an identical tag to the way
|
|
||||||
tag_xml = XML::Node.new("tag")
|
|
||||||
tag_xml["k"] = existing_tag.k
|
|
||||||
tag_xml["v"] = existing_tag.v
|
|
||||||
|
|
||||||
# add the tag into the existing xml
|
|
||||||
way_xml = way.to_xml
|
|
||||||
way_xml.find("//osm/way").first << tag_xml
|
|
||||||
|
|
||||||
# try and upload it
|
|
||||||
put :update, :params => { :id => way.id }, :body => way_xml.to_s
|
|
||||||
assert_response :bad_request,
|
|
||||||
"adding a duplicate tag to a way should fail with 'bad request'"
|
|
||||||
assert_equal "Element way/#{way.id} has duplicate tags with key #{existing_tag.k}", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Try adding a new duplicate tags to a way
|
|
||||||
def test_new_duplicate_tags
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_way = create(:way, :changeset => create(:changeset, :user => private_user))
|
|
||||||
user = create(:user)
|
|
||||||
way = create(:way, :changeset => create(:changeset, :user => user))
|
|
||||||
|
|
||||||
## First test with the non-public user so should be rejected
|
|
||||||
# setup auth
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
# create duplicate tag
|
|
||||||
tag_xml = XML::Node.new("tag")
|
|
||||||
tag_xml["k"] = "i_am_a_duplicate"
|
|
||||||
tag_xml["v"] = "foobar"
|
|
||||||
|
|
||||||
# add the tag into the existing xml
|
|
||||||
way_xml = private_way.to_xml
|
|
||||||
|
|
||||||
# add two copies of the tag
|
|
||||||
way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
|
|
||||||
|
|
||||||
# try and upload it
|
|
||||||
put :update, :params => { :id => private_way.id }, :body => way_xml.to_s
|
|
||||||
assert_response :forbidden,
|
|
||||||
"adding new duplicate tags to a way using a non-public user should fail with 'forbidden'"
|
|
||||||
|
|
||||||
## Now test with the public user
|
|
||||||
# setup auth
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# create duplicate tag
|
|
||||||
tag_xml = XML::Node.new("tag")
|
|
||||||
tag_xml["k"] = "i_am_a_duplicate"
|
|
||||||
tag_xml["v"] = "foobar"
|
|
||||||
|
|
||||||
# add the tag into the existing xml
|
|
||||||
way_xml = way.to_xml
|
|
||||||
|
|
||||||
# add two copies of the tag
|
|
||||||
way_xml.find("//osm/way").first << tag_xml.copy(true) << tag_xml
|
|
||||||
|
|
||||||
# try and upload it
|
|
||||||
put :update, :params => { :id => way.id }, :body => way_xml.to_s
|
|
||||||
assert_response :bad_request,
|
|
||||||
"adding new duplicate tags to a way should fail with 'bad request'"
|
|
||||||
assert_equal "Element way/#{way.id} has duplicate tags with key i_am_a_duplicate", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Try adding a new duplicate tags to a way.
|
|
||||||
# But be a bit subtle - use unicode decoding ambiguities to use different
|
|
||||||
# binary strings which have the same decoding.
|
|
||||||
def test_invalid_duplicate_tags
|
|
||||||
private_user = create(:user, :data_public => false)
|
|
||||||
private_changeset = create(:changeset, :user => private_user)
|
|
||||||
user = create(:user)
|
|
||||||
changeset = create(:changeset, :user => user)
|
|
||||||
|
|
||||||
## First make sure that you can't with a non-public user
|
|
||||||
# setup auth
|
|
||||||
basic_authorization private_user.email, "test"
|
|
||||||
|
|
||||||
# add the tag into the existing xml
|
|
||||||
way_str = "<osm><way changeset='#{private_changeset.id}'>"
|
|
||||||
way_str << "<tag k='addr:housenumber' v='1'/>"
|
|
||||||
way_str << "<tag k='addr:housenumber' v='2'/>"
|
|
||||||
way_str << "</way></osm>"
|
|
||||||
|
|
||||||
# try and upload it
|
|
||||||
put :create, :body => way_str
|
|
||||||
assert_response :forbidden,
|
|
||||||
"adding new duplicate tags to a way with a non-public user should fail with 'forbidden'"
|
|
||||||
|
|
||||||
## Now do it with a public user
|
|
||||||
# setup auth
|
|
||||||
basic_authorization user.email, "test"
|
|
||||||
|
|
||||||
# add the tag into the existing xml
|
|
||||||
way_str = "<osm><way changeset='#{changeset.id}'>"
|
|
||||||
way_str << "<tag k='addr:housenumber' v='1'/>"
|
|
||||||
way_str << "<tag k='addr:housenumber' v='2'/>"
|
|
||||||
way_str << "</way></osm>"
|
|
||||||
|
|
||||||
# try and upload it
|
|
||||||
put :create, :body => way_str
|
|
||||||
assert_response :bad_request,
|
|
||||||
"adding new duplicate tags to a way should fail with 'bad request'"
|
|
||||||
assert_equal "Element way/ has duplicate tags with key addr:housenumber", @response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# test that a call to ways_for_node returns all ways that contain the node
|
|
||||||
# and none that don't.
|
|
||||||
def test_ways_for_node
|
|
||||||
node = create(:node)
|
|
||||||
way1 = create(:way)
|
|
||||||
way2 = create(:way)
|
|
||||||
create(:way_node, :way => way1, :node => node)
|
|
||||||
create(:way_node, :way => way2, :node => node)
|
|
||||||
# create an unrelated way
|
|
||||||
create(:way_with_nodes, :nodes_count => 2)
|
|
||||||
# create a way which used to use the node
|
|
||||||
way3_v1 = create(:old_way, :version => 1)
|
|
||||||
_way3_v2 = create(:old_way, :current_way => way3_v1.current_way, :version => 2)
|
|
||||||
create(:old_way_node, :old_way => way3_v1, :node => node)
|
|
||||||
|
|
||||||
get :ways_for_node, :params => { :id => node.id }
|
|
||||||
assert_response :success
|
|
||||||
ways_xml = XML::Parser.string(@response.body).parse
|
|
||||||
assert_not_nil ways_xml, "failed to parse ways_for_node response"
|
|
||||||
|
|
||||||
# check that the set of IDs match expectations
|
|
||||||
expected_way_ids = [way1.id,
|
|
||||||
way2.id]
|
|
||||||
found_way_ids = ways_xml.find("//osm/way").collect { |w| w["id"].to_i }
|
|
||||||
assert_equal expected_way_ids.sort, found_way_ids.sort,
|
|
||||||
"expected ways for node #{node.id} did not match found"
|
|
||||||
|
|
||||||
# check the full ways to ensure we're not missing anything
|
|
||||||
expected_way_ids.each do |id|
|
|
||||||
way_xml = ways_xml.find("//osm/way[@id='#{id}']").first
|
|
||||||
assert_ways_are_equal(Way.find(id),
|
|
||||||
Way.from_xml_node(way_xml))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# update the changeset_id of a way element
|
|
||||||
def update_changeset(xml, changeset_id)
|
|
||||||
xml_attr_rewrite(xml, "changeset", changeset_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# update an attribute in the way element
|
|
||||||
def xml_attr_rewrite(xml, name, value)
|
|
||||||
xml.find("//osm/way").first[name] = value.to_s
|
|
||||||
xml
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# replace a node in a way element
|
|
||||||
def xml_replace_node(xml, old_node, new_node)
|
|
||||||
xml.find("//osm/way/nd[@ref='#{old_node}']").first["ref"] = new_node.to_s
|
|
||||||
xml
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Add table
Add a link
Reference in a new issue