From: Tom Hughes Date: Wed, 12 Feb 2025 18:21:02 +0000 (+0000) Subject: Merge remote-tracking branch 'upstream/pull/5632' X-Git-Tag: live~162 X-Git-Url: https://git.openstreetmap.org./rails.git/commitdiff_plain/46f642d5cd88d51bd6e80e48cc24f7a6da68d4c7?hp=5310c5b9dd67f7d416ee778a36f43c21126a1575 Merge remote-tracking branch 'upstream/pull/5632' --- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aea8b30b9..46fe63bb2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -87,7 +87,7 @@ and why it should be the way it is. ## i18n -If you make a change that involve the locale files (in `config/locales`) then please +If you make a change that involves the locale files (in `config/locales`) then please only submit changes to the `en.yml` file. The other files are updated via [Translatewiki](https://translatewiki.net/wiki/Translating:OpenStreetMap) and should not be included in your pull request. @@ -143,7 +143,7 @@ with making the reviews as straightforward as possible: large to review in one sitting, or if changes are requested, then the maintainer needs to repeatedly re-read code that has already been considered. * The commit history is important. This is a large codebase, developed over many - years by many developers. We frequently need to read the commit history (e.g. + years by many developers. We frequently need to read the commit history (for example using `git blame`) to figure out what is going on. So small, understandable, and relevant commits are important for other developers looking back at your work in future. diff --git a/DOCKER.md b/DOCKER.md index b93bf6d50..4cfc6d736 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -56,7 +56,7 @@ This is a workaround. [See issues/2185 for details](https://github.com/openstree touch config/settings.local.yml ``` -**Windows users:** `touch` is not an availible command in Windows so just create a `settings.local.yml` file in the `config` directory, or if you have WSL you can run `wsl touch config/settings.local.yml`. +**Windows users:** `touch` is not an available command in Windows so just create a `settings.local.yml` file in the `config` directory, or if you have WSL you can run `wsl touch config/settings.local.yml`. ## Installation diff --git a/Gemfile.lock b/Gemfile.lock index 0fd4dc5ea..9475cb663 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -85,23 +85,24 @@ GEM annotate (3.2.0) activerecord (>= 3.2, < 8.0) rake (>= 10.4, < 14.0) - argon2 (2.3.0) + argon2 (2.3.2) ffi (~> 1.15) ffi-compiler (~> 1.0) ast (2.4.2) autoprefixer-rails (10.4.19.0) execjs (~> 2) aws-eventstream (1.3.0) - aws-partitions (1.1044.0) - aws-sdk-core (3.217.1) + aws-partitions (1.1049.0) + aws-sdk-core (3.218.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) + base64 jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.97.0) + aws-sdk-kms (1.98.0) aws-sdk-core (~> 3, >= 3.216.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.179.0) + aws-sdk-s3 (1.180.0) aws-sdk-core (~> 3, >= 3.216.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) @@ -211,9 +212,10 @@ GEM railties (>= 5) doorkeeper-i18n (5.2.7) doorkeeper (>= 5.2) - doorkeeper-openid_connect (1.8.10) + doorkeeper-openid_connect (1.8.11) doorkeeper (>= 5.5, < 5.9) jwt (>= 2.5) + ostruct (>= 0.5) drb (2.2.1) dry-configurable (1.3.0) dry-core (~> 1.1) @@ -321,8 +323,8 @@ GEM image_optim (~> 0.24) railties sprockets - image_processing (1.13.0) - mini_magick (>= 4.9.5, < 5) + image_processing (1.14.0) + mini_magick (>= 4.9.5, < 6) ruby-vips (>= 2.0.17, < 3) image_size (3.4.0) in_threads (1.6.0) @@ -340,7 +342,7 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.9.1) + json (2.10.1) jwt (2.10.1) base64 kgio (2.11.4) @@ -369,7 +371,9 @@ GEM marcel (1.0.4) matrix (0.4.2) maxminddb (0.1.22) - mini_magick (4.13.2) + mini_magick (5.1.2) + benchmark + logger mini_mime (1.1.5) mini_portile2 (2.8.8) mini_racer (0.9.0) @@ -377,21 +381,21 @@ GEM minitest (5.25.4) minitest-focus (1.4.0) minitest (>= 4, < 6) - msgpack (1.7.5) + msgpack (1.8.0) multi_json (1.15.0) multi_xml (0.7.1) bigdecimal (~> 3.1) nap (1.1.0) net-http (0.6.0) uri - net-imap (0.5.5) + net-imap (0.5.6) date net-protocol net-pop (0.1.2) net-protocol net-protocol (0.2.2) timeout - net-smtp (0.5.0) + net-smtp (0.5.1) net-protocol nio4r (2.7.4) nokogiri (1.18.2) @@ -456,7 +460,7 @@ GEM iniparse (~> 1.4) rexml (>= 3.3.9) parallel (1.26.3) - parser (3.3.7.0) + parser (3.3.7.1) ast (~> 2.4.1) racc pg (1.5.9) @@ -536,7 +540,7 @@ GEM rb-inotify (0.11.1) ffi (~> 1.0) rchardet (1.9.0) - rdoc (6.11.0) + rdoc (6.12.0) psych (>= 4.0.0) regexp_parser (2.10.0) reline (0.6.0) @@ -549,7 +553,7 @@ GEM rouge (4.5.1) rtlcss (0.2.1) mini_racer (>= 0.6.3) - rubocop (1.71.1) + rubocop (1.71.2) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -580,7 +584,7 @@ GEM rubocop (~> 1.0) ruby-openid (2.9.2) ruby-progressbar (1.13.0) - ruby-vips (2.2.2) + ruby-vips (2.2.3) ffi (~> 1.12) logger rubyzip (2.4.1) @@ -649,7 +653,7 @@ GEM simpleidn vendorer (0.2.0) version_gem (1.1.4) - webmock (3.24.0) + webmock (3.25.0) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) diff --git a/app/abilities/api_ability.rb b/app/abilities/api_ability.rb index 55476ab53..e774f6820 100644 --- a/app/abilities/api_ability.rb +++ b/app/abilities/api_ability.rb @@ -15,8 +15,7 @@ class ApiAbility can [:read, :download], Changeset can :read, Tracepoint can :read, User - can :read, [Node, Way, Relation] - can [:history, :read], [OldNode, OldWay, OldRelation] + can :read, [Node, Way, Relation, OldNode, OldWay, OldRelation] can :read, UserBlock if user&.active? diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 86a0a0789..2e13f6967 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -113,13 +113,13 @@ $(document).ready(function () { */ setTimeout(function () { $("header").children(":visible").each(function (i, e) { - headerWidth = headerWidth + $(e).outerWidth(); + headerWidth += $(e).outerWidth(); }); $("body").addClass("compact-nav"); $("header").children(":visible").each(function (i, e) { - compactWidth = compactWidth + $(e).outerWidth(); + compactWidth += $(e).outerWidth(); }); $("body").removeClass("compact-nav"); diff --git a/app/assets/javascripts/edit/id.js.erb b/app/assets/javascripts/edit/id.js.erb index cb4ff1865..7c907ed3a 100644 --- a/app/assets/javascripts/edit/id.js.erb +++ b/app/assets/javascripts/edit/id.js.erb @@ -32,5 +32,5 @@ $(document).ready(function () { if (idData.gpx) params.set("gpx", idData.gpx); - id.attr("src", idData.url + "#" + params); + id.attr("src", idData.url + "#" + params.toString().replace(/\+/g, "%20")); }); diff --git a/app/assets/javascripts/index/contextmenu.js b/app/assets/javascripts/index/contextmenu.js index fea7e7314..d7e6b427d 100644 --- a/app/assets/javascripts/index/contextmenu.js +++ b/app/assets/javascripts/index/contextmenu.js @@ -65,9 +65,8 @@ OSM.initializeContextMenu = function (map) { function getDirectionsEndpointCoordinatesFromInput(input) { if (input.attr("data-lat") && input.attr("data-lon")) { return input.attr("data-lat") + "," + input.attr("data-lon"); - } else { - return $(input).val(); } + return $(input).val(); } var updateMenu = function updateMenu() { diff --git a/app/assets/javascripts/index/directions.js b/app/assets/javascripts/index/directions.js index 356dc6271..0ac66d9d6 100644 --- a/app/assets/javascripts/index/directions.js +++ b/app/assets/javascripts/index/directions.js @@ -3,7 +3,7 @@ //= require_tree ./directions OSM.Directions = function (map) { - var routeRequest = null; // jqXHR object of an ongoing route request or null + let controller = null; // the AbortController for the current route request if a route request is in progress var chosenEngine; var popup = L.popup({ autoPanPadding: [100, 100] }); @@ -23,7 +23,7 @@ OSM.Directions = function (map) { var endpointDragCallback = function (dragging) { if (!map.hasLayer(polyline)) return; if (dragging && !chosenEngine.draggable) return; - if (dragging && routeRequest) return; + if (dragging && controller) return; getRoute(false, !dragging); }; @@ -109,7 +109,7 @@ OSM.Directions = function (map) { function getRoute(fitRoute, reportErrors) { // Cancel any route that is already in progress - if (routeRequest) routeRequest.abort(); + if (controller) controller.abort(); const points = endpoints.map(p => p.latlng); @@ -126,20 +126,8 @@ OSM.Directions = function (map) { // again. $("#sidebar_content").html($(".directions_form .loader_copy").html()); map.setSidebarOverlaid(false); - - routeRequest = chosenEngine.getRoute(points, function (err, route) { - routeRequest = null; - - if (err) { - map.removeLayer(polyline); - - if (reportErrors) { - $("#sidebar_content").html("
" + I18n.t("javascripts.directions.errors.no_route") + "
"); - } - - return; - } - + controller = new AbortController(); + chosenEngine.getRoute(points, controller.signal).then(function (route) { polyline .setLatLngs(route.line) .addTo(map); @@ -212,6 +200,13 @@ OSM.Directions = function (map) { map.setSidebarOverlaid(true); // TODO: collapse width of sidebar back to previous }); + }).catch(function () { + map.removeLayer(polyline); + if (reportErrors) { + $("#sidebar_content").html("
" + I18n.t("javascripts.directions.errors.no_route") + "
"); + } + }).finally(function () { + controller = null; }); function getDistText(dist) { diff --git a/app/assets/javascripts/index/directions/fossgis_osrm.js b/app/assets/javascripts/index/directions/fossgis_osrm.js index bb968f2da..cd1731247 100644 --- a/app/assets/javascripts/index/directions/fossgis_osrm.js +++ b/app/assets/javascripts/index/directions/fossgis_osrm.js @@ -154,15 +154,15 @@ creditline: "OSRM (FOSSGIS)", draggable: true, - getRoute: function (points, callback) { - const data = [ - { name: "overview", value: "false" }, - { name: "geometries", value: "polyline" }, - { name: "steps", value: true } - ]; + getRoute: function (points, signal) { + const query = new URLSearchParams({ + overview: "false", + geometries: "polyline", + steps: true + }); if (cachedHints.length === points.length) { - data.push({ name: "hints", value: cachedHints.join(";") }); + query.set("hints", cachedHints.join(";")); } else { // invalidate cache cachedHints = []; @@ -170,22 +170,13 @@ const req_path = "routed-" + vehicleType + "/route/v1/driving/" + points.map(p => p.lng + "," + p.lat).join(";"); - return $.ajax({ - url: OSM.FOSSGIS_OSRM_URL + req_path, - data, - dataType: "json", - success: function (response) { - if (response.code !== "Ok") { - return callback(true); - } - + return fetch(OSM.FOSSGIS_OSRM_URL + req_path + "?" + query, { signal }) + .then(response => response.json()) + .then(response => { + if (response.code !== "Ok") throw new Error(); cachedHints = response.waypoints.map(wp => wp.hint); - callback(false, _processDirections(response.routes[0])); - }, - error: function () { - callback(true); - } - }); + return _processDirections(response.routes[0]); + }); } }; } diff --git a/app/assets/javascripts/index/directions/fossgis_valhalla.js b/app/assets/javascripts/index/directions/fossgis_valhalla.js index bbccccb13..41ad6a972 100644 --- a/app/assets/javascripts/index/directions/fossgis_valhalla.js +++ b/app/assets/javascripts/index/directions/fossgis_valhalla.js @@ -87,8 +87,8 @@ "Valhalla (FOSSGIS)", draggable: false, - getRoute: function (points, callback) { - const data = { + getRoute: function (points, signal) { + const query = new URLSearchParams({ json: JSON.stringify({ locations: points.map(function (p) { return { lat: p.lat, lon: p.lng, radius: 5 }; @@ -99,22 +99,13 @@ language: I18n.currentLocale() } }) - }; - return $.ajax({ - url: OSM.FOSSGIS_VALHALLA_URL, - data, - dataType: "json", - success: function ({ trip }) { - if (trip.status === 0) { - callback(false, _processDirections(trip.legs)); - } else { - callback(true); - } - }, - error: function () { - callback(true); - } }); + return fetch(OSM.FOSSGIS_VALHALLA_URL + "?" + query, { signal }) + .then(response => response.json()) + .then(({ trip }) => { + if (trip.status !== 0) throw new Error(); + return _processDirections(trip.legs); + }); } }; } diff --git a/app/assets/javascripts/index/directions/graphhopper.js b/app/assets/javascripts/index/directions/graphhopper.js index 191475873..729618f2d 100644 --- a/app/assets/javascripts/index/directions/graphhopper.js +++ b/app/assets/javascripts/index/directions/graphhopper.js @@ -51,33 +51,24 @@ creditline: "GraphHopper", draggable: false, - getRoute: function (points, callback) { + getRoute: function (points, signal) { // GraphHopper Directions API documentation // https://graphhopper.com/api/1/docs/routing/ - const data = { + const query = new URLSearchParams({ vehicle: vehicleType, locale: I18n.currentLocale(), key: "LijBPDQGfu7Iiq80w3HzwB4RUDJbMbhs6BU0dEnn", elevation: false, instructions: true, - turn_costs: vehicleType === "car", - point: points.map(p => p.lat + "," + p.lng) - }; - return $.ajax({ - url: OSM.GRAPHHOPPER_URL, - data, - traditional: true, - dataType: "json", - success: function ({ paths }) { - if (!paths || paths.length === 0) { - return callback(true); - } - callback(false, _processDirections(paths[0])); - }, - error: function () { - callback(true); - } + turn_costs: vehicleType === "car" }); + points.forEach(p => query.append("point", p.lat + "," + p.lng)); + return fetch(OSM.GRAPHHOPPER_URL + "?" + query, { signal }) + .then(response => response.json()) + .then(({ paths }) => { + if (!paths || paths.length === 0) throw new Error(); + return _processDirections(paths[0]); + }); } }; } diff --git a/app/assets/javascripts/index/layers/data.js b/app/assets/javascripts/index/layers/data.js index dc522af41..e6a7cc666 100644 --- a/app/assets/javascripts/index/layers/data.js +++ b/app/assets/javascripts/index/layers/data.js @@ -121,7 +121,7 @@ OSM.initializeDataLayer = function (map) { }, error: function (XMLHttpRequest, textStatus) { dataLoader = null; - if (textStatus === "abort") { return; } + if (textStatus === "abort") return; function closeError() { $("#browse_status").empty(); diff --git a/app/assets/javascripts/index/query.js b/app/assets/javascripts/index/query.js index 5c8ee8876..54d5e080f 100644 --- a/app/assets/javascripts/index/query.js +++ b/app/assets/javascripts/index/query.js @@ -115,17 +115,16 @@ OSM.Query = function (map) { } } - if (tags.name) { - return tags.name; - } else if (tags.ref) { - return tags.ref; - } else if (tags["addr:housename"]) { - return tags["addr:housename"]; - } else if (tags["addr:housenumber"] && tags["addr:street"]) { + for (const key of ["name", "ref", "addr:housename"]) { + if (tags[key]) { + return tags[key]; + } + } + + if (tags["addr:housenumber"] && tags["addr:street"]) { return tags["addr:housenumber"] + " " + tags["addr:street"]; - } else { - return "#" + feature.id; } + return "#" + feature.id; } function featureGeometry(feature) { @@ -285,10 +284,11 @@ OSM.Query = function (map) { .hide(); if (marker) map.removeLayer(marker); - marker = L.circle(latlng, Object.assign({ + marker = L.circle(latlng, { radius: radius, - className: "query-marker" - }, featureStyle)).addTo(map); + className: "query-marker", + ...featureStyle + }).addTo(map); runQuery(latlng, radius, nearby, $("#query-nearby"), false); runQuery(latlng, radius, isin, $("#query-isin"), true, compareSize); diff --git a/app/assets/javascripts/leaflet.locate.js b/app/assets/javascripts/leaflet.locate.js index d43a5e458..d19929208 100644 --- a/app/assets/javascripts/leaflet.locate.js +++ b/app/assets/javascripts/leaflet.locate.js @@ -1,5 +1,5 @@ L.OSM.locate = function (options) { - var control = L.control.locate(Object.assign({ + var control = L.control.locate({ icon: "icon geolocate", iconLoading: "icon geolocate", strings: { @@ -7,8 +7,9 @@ L.OSM.locate = function (options) { popup: function (options) { return I18n.t("javascripts.map.locate." + options.unit + "Popup", { count: options.distance }); } - } - }, options)); + }, + ...options + }); control.onAdd = function (map) { var container = Object.getPrototypeOf(this).onAdd.apply(this, [map]); diff --git a/app/assets/javascripts/osm.js.erb b/app/assets/javascripts/osm.js.erb index 030c51a08..062d537ff 100644 --- a/app/assets/javascripts/osm.js.erb +++ b/app/assets/javascripts/osm.js.erb @@ -62,7 +62,8 @@ OSM = { }, mapParams: function (search) { - var params = OSM.params(search), mapParams = {}; + var params = OSM.params(search), + mapParams = {}; if (params.mlon && params.mlat) { mapParams.marker = true; diff --git a/app/controllers/accounts/terms_controller.rb b/app/controllers/accounts/terms_controller.rb index 45e0cc239..13e9de890 100644 --- a/app/controllers/accounts/terms_controller.rb +++ b/app/controllers/accounts/terms_controller.rb @@ -4,8 +4,7 @@ module Accounts layout "site" - before_action :disable_terms_redirect - before_action :authorize_web + before_action -> { authorize_web(:skip_terms => true) } before_action :set_locale before_action :check_database_readable diff --git a/app/controllers/api/nodes/relations_controller.rb b/app/controllers/api/nodes/relations_controller.rb index 0f0409e19..c6b33eab8 100644 --- a/app/controllers/api/nodes/relations_controller.rb +++ b/app/controllers/api/nodes/relations_controller.rb @@ -6,13 +6,12 @@ module Api before_action :set_request_formats def index - relation_ids = RelationMember.where(:member_type => "Node", :member_id => params[:node_id]).collect(&:relation_id).uniq - - @relations = [] - - Relation.find(relation_ids).each do |relation| - @relations << relation if relation.visible - end + @relations = Relation + .visible + .where(:id => RelationMember.where( + :member_type => "Node", + :member_id => params[:node_id] + ).select(:relation_id)) # Render the result respond_to do |format| diff --git a/app/controllers/api/nodes/ways_controller.rb b/app/controllers/api/nodes/ways_controller.rb index 2a71f1903..cdbdc07d0 100644 --- a/app/controllers/api/nodes/ways_controller.rb +++ b/app/controllers/api/nodes/ways_controller.rb @@ -10,9 +10,11 @@ module Api # :node_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 index - way_ids = WayNode.where(:node_id => params[:node_id]).collect { |ws| ws.id[0] }.uniq - - @ways = Way.where(:id => way_ids, :visible => true) + @ways = Way + .visible + .where(:id => WayNode.where( + :node_id => params[:node_id] + ).select(:way_id)) # Render the result respond_to do |format| diff --git a/app/controllers/api/notes_controller.rb b/app/controllers/api/notes_controller.rb index eca0728b6..bc4d2eaf2 100644 --- a/app/controllers/api/notes_controller.rb +++ b/app/controllers/api/notes_controller.rb @@ -81,19 +81,22 @@ module Api # 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] + description = params[:text] + + # Get note's author info (for logged in users - user_id, for logged out users - IP address) + note_author_info = author_info # 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) + @note = Note.create(:lat => lat, :lon => lon, :description => description, :user_id => note_author_info[:user_id], :user_ip => note_author_info[:user_ip]) 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") + # Add opening comment (description) to the note + add_comment(@note, description, "opened") end # Return a copy of the new note @@ -263,7 +266,9 @@ module Api end # Add any text filter - @notes = @notes.joins(:comments).where("to_tsvector('english', note_comments.body) @@ plainto_tsquery('english', ?)", params[:q]) if params[:q] + if params[:q] + @notes = @notes.joins(:comments).where("to_tsvector('english', note_comments.body) @@ plainto_tsquery('english', ?) OR to_tsvector('english', notes.description) @@ plainto_tsquery('english', ?)", params[:q], params[:q]) + end # Add any date filter if params[:from] @@ -379,17 +384,28 @@ module Api end end + ## + # Get author's information (for logged in users - user_id, for logged out users - IP address) + def author_info + if scope_enabled?(:write_notes) + { :user_id => current_user.id } + else + { :user_ip => request.remote_ip } + end + end + ## # Add a comment to a note def add_comment(note, text, event, notify: true) attributes = { :visible => true, :event => event, :body => text } - author = current_user if scope_enabled?(:write_notes) + # Get note comment's author info (for logged in users - user_id, for logged out users - IP address) + note_comment_author_info = author_info - if author - attributes[:author_id] = author.id + if note_comment_author_info[:user_ip].nil? + attributes[:author_id] = note_comment_author_info[:user_id] else - attributes[:author_ip] = request.remote_ip + attributes[:author_ip] = note_comment_author_info[:user_ip] end comment = note.comments.create!(attributes) diff --git a/app/controllers/api/old_elements_controller.rb b/app/controllers/api/old_elements_controller.rb index 73e57c1f8..bdbc5e392 100644 --- a/app/controllers/api/old_elements_controller.rb +++ b/app/controllers/api/old_elements_controller.rb @@ -4,17 +4,17 @@ module Api class OldElementsController < ApiController before_action :check_api_writable, :only => [:redact] - before_action :setup_user_auth, :only => [:history, :show] + before_action :setup_user_auth, :only => [:index, :show] before_action :authorize, :only => [:redact] authorize_resource - before_action :lookup_old_element, :except => [:history] - before_action :lookup_old_element_versions, :only => [:history] + before_action :lookup_old_element, :except => [:index] + before_action :lookup_old_element_versions, :only => [:index] before_action :set_request_formats, :except => [:redact] - def history + def index # 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. diff --git a/app/controllers/api/old_nodes_controller.rb b/app/controllers/api/old_nodes_controller.rb index c2831c48b..b63761af9 100644 --- a/app/controllers/api/old_nodes_controller.rb +++ b/app/controllers/api/old_nodes_controller.rb @@ -3,11 +3,11 @@ module Api private def lookup_old_element - @old_element = OldNode.find([params[:id], params[:version]]) + @old_element = OldNode.find([params[:node_id], params[:version]]) end def lookup_old_element_versions - @elements = OldNode.where(:node_id => params[:id]).order(:version) + @elements = OldNode.where(:node_id => params[:node_id]).order(:version) end end end diff --git a/app/controllers/api/old_relations_controller.rb b/app/controllers/api/old_relations_controller.rb index 4679384dd..c065c657d 100644 --- a/app/controllers/api/old_relations_controller.rb +++ b/app/controllers/api/old_relations_controller.rb @@ -3,11 +3,11 @@ module Api private def lookup_old_element - @old_element = OldRelation.find([params[:id], params[:version]]) + @old_element = OldRelation.find([params[:relation_id], params[:version]]) end def lookup_old_element_versions - @elements = OldRelation.where(:relation_id => params[:id]).order(:version) + @elements = OldRelation.where(:relation_id => params[:relation_id]).order(:version) end end end diff --git a/app/controllers/api/old_ways_controller.rb b/app/controllers/api/old_ways_controller.rb index 0afa3059c..654100767 100644 --- a/app/controllers/api/old_ways_controller.rb +++ b/app/controllers/api/old_ways_controller.rb @@ -3,11 +3,11 @@ module Api private def lookup_old_element - @old_element = OldWay.find([params[:id], params[:version]]) + @old_element = OldWay.find([params[:way_id], params[:version]]) end def lookup_old_element_versions - @elements = OldWay.where(:way_id => params[:id]).order(:version) + @elements = OldWay.where(:way_id => params[:way_id]).order(:version) end end end diff --git a/app/controllers/api/relations/relations_controller.rb b/app/controllers/api/relations/relations_controller.rb index 1769e1396..9b8292526 100644 --- a/app/controllers/api/relations/relations_controller.rb +++ b/app/controllers/api/relations/relations_controller.rb @@ -6,13 +6,12 @@ module Api before_action :set_request_formats def index - relation_ids = RelationMember.where(:member_type => "Relation", :member_id => params[:relation_id]).collect(&:relation_id).uniq - - @relations = [] - - Relation.find(relation_ids).each do |relation| - @relations << relation if relation.visible - end + @relations = Relation + .visible + .where(:id => RelationMember.where( + :member_type => "Relation", + :member_id => params[:relation_id] + ).select(:relation_id)) # Render the result respond_to do |format| diff --git a/app/controllers/api/users_controller.rb b/app/controllers/api/users_controller.rb index 462b4ba3c..58b02a489 100644 --- a/app/controllers/api/users_controller.rb +++ b/app/controllers/api/users_controller.rb @@ -1,8 +1,7 @@ module Api class UsersController < ApiController - before_action :disable_terms_redirect, :only => [:details] before_action :setup_user_auth, :only => [:show, :index] - before_action :authorize, :only => [:details] + before_action -> { authorize(:skip_terms => true) }, :only => [:details] authorize_resource @@ -46,14 +45,5 @@ module Api format.json { render :show } end end - - private - - 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 diff --git a/app/controllers/api/ways/relations_controller.rb b/app/controllers/api/ways/relations_controller.rb index fcd8b40dd..4188dfe4e 100644 --- a/app/controllers/api/ways/relations_controller.rb +++ b/app/controllers/api/ways/relations_controller.rb @@ -6,13 +6,12 @@ module Api before_action :set_request_formats def index - relation_ids = RelationMember.where(:member_type => "Way", :member_id => params[:way_id]).collect(&:relation_id).uniq - - @relations = [] - - Relation.find(relation_ids).each do |relation| - @relations << relation if relation.visible - end + @relations = Relation + .visible + .where(:id => RelationMember.where( + :member_type => "Way", + :member_id => params[:way_id] + ).select(:relation_id)) # Render the result respond_to do |format| diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index 23f35a40e..5faa39165 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -49,9 +49,9 @@ class ApiController < ApplicationController end end - def authorize(errormessage = "Couldn't authenticate you") + def authorize(errormessage: "Couldn't authenticate you", skip_terms: false) # make the current_user object from any auth sources we have - setup_user_auth + setup_user_auth(:skip_terms => skip_terms) # handle authenticate pass/fail unless current_user @@ -92,7 +92,7 @@ class ApiController < ApplicationController # sets up the current_user for use by other methods. this is mostly called # from the authorize method, but can be called elsewhere if authorisation # is optional. - def setup_user_auth + def setup_user_auth(skip_terms: false) logger.info " setup_user_auth" # try and setup using OAuth self.current_user = User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token&.accessible? @@ -113,7 +113,7 @@ class ApiController < ApplicationController # if the user hasn't seen the contributor terms then don't # allow editing - they have to go to the web site and see # (but can decline) the CTs to continue. - if !current_user.terms_seen && flash[:skip_terms].nil? + if !current_user.terms_seen && !skip_terms set_locale report_error t("application.setup_user_auth.need_to_see_terms"), :forbidden end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 176fd8c2e..25de71f20 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -39,7 +39,7 @@ class ApplicationController < ActionController::Base private - def authorize_web + def authorize_web(skip_terms: false) if session[:user] self.current_user = User.find_by(:id => session[:user], :status => %w[active confirmed suspended]) @@ -55,7 +55,7 @@ class ApplicationController < ActionController::Base # don't allow access to any auth-requiring part of the site unless # the new CTs have been seen (and accept/decline chosen). - elsif !current_user.terms_seen && flash[:skip_terms].nil? + elsif !current_user.terms_seen && !skip_terms flash[:notice] = t "accounts.terms.show.you need to accept or decline" if params[:referer] redirect_to account_terms_path(:referer => params[:referer]) @@ -259,12 +259,7 @@ class ApplicationController < ActionController::Base request.content_security_policy = policy - case Settings.status - when "database_offline", "api_offline" - flash.now[:warning] = t("layouts.osm_offline") - when "database_readonly", "api_readonly" - flash.now[:warning] = t("layouts.osm_read_only") - end + flash.now[:warning] = { :partial => "layouts/offline_flash" } unless api_status == "online" request.xhr? ? "xhr" : "map" end diff --git a/app/controllers/concerns/session_methods.rb b/app/controllers/concerns/session_methods.rb index 4bbfac34f..d0fb0c419 100644 --- a/app/controllers/concerns/session_methods.rb +++ b/app/controllers/concerns/session_methods.rb @@ -81,13 +81,4 @@ module SessionMethods session.delete(:remember_me) 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 diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index abbaf5e92..19fe05f30 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -3,8 +3,8 @@ class SessionsController < ApplicationController layout "site" - before_action :disable_terms_redirect, :only => [:destroy] - before_action :authorize_web + before_action :authorize_web, :except => [:destroy] + before_action -> { authorize_web(:skip_terms => true) }, :only => [:destroy] before_action :set_locale before_action :check_database_readable before_action :require_cookies, :only => [:new] diff --git a/app/controllers/site_controller.rb b/app/controllers/site_controller.rb index 9adbaa195..5110be019 100644 --- a/app/controllers/site_controller.rb +++ b/app/controllers/site_controller.rb @@ -107,6 +107,14 @@ class SiteController < ApplicationController rescue ActiveRecord::RecordNotFound # don't try and derive a location from a missing/deleted object end + + if api_status != "online" + flash.now[:warning] = { :partial => "layouts/offline_flash" } + elsif current_user && !current_user.data_public? + flash.now[:warning] = { :partial => "not_public_flash" } + else + @enable_editor = true + end end def copyright @@ -129,11 +137,7 @@ class SiteController < ApplicationController def export; end def offline - flash.now[:warning] = if Settings.status == "database_offline" - t("layouts.osm_offline") - else - t("layouts.osm_read_only") - end + flash.now[:warning] = { :partial => "layouts/offline_flash" } render :html => nil, :layout => true end diff --git a/app/models/note.rb b/app/models/note.rb index 807ee9ec8..b7215d6f7 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -16,9 +16,10 @@ # # Indexes # -# notes_created_at_idx (created_at) -# notes_tile_status_idx (tile,status) -# notes_updated_at_idx (updated_at) +# index_notes_on_description (to_tsvector('english'::regconfig, description)) USING gin +# notes_created_at_idx (created_at) +# notes_tile_status_idx (tile,status) +# notes_updated_at_idx (updated_at) # # Foreign Keys # diff --git a/app/views/api/old_nodes/history.json.jbuilder b/app/views/api/old_nodes/index.json.jbuilder similarity index 100% rename from app/views/api/old_nodes/history.json.jbuilder rename to app/views/api/old_nodes/index.json.jbuilder diff --git a/app/views/api/old_nodes/history.xml.builder b/app/views/api/old_nodes/index.xml.builder similarity index 100% rename from app/views/api/old_nodes/history.xml.builder rename to app/views/api/old_nodes/index.xml.builder diff --git a/app/views/api/old_relations/history.json.jbuilder b/app/views/api/old_relations/index.json.jbuilder similarity index 100% rename from app/views/api/old_relations/history.json.jbuilder rename to app/views/api/old_relations/index.json.jbuilder diff --git a/app/views/api/old_relations/history.xml.builder b/app/views/api/old_relations/index.xml.builder similarity index 100% rename from app/views/api/old_relations/history.xml.builder rename to app/views/api/old_relations/index.xml.builder diff --git a/app/views/api/old_ways/history.json.jbuilder b/app/views/api/old_ways/index.json.jbuilder similarity index 100% rename from app/views/api/old_ways/history.json.jbuilder rename to app/views/api/old_ways/index.json.jbuilder diff --git a/app/views/api/old_ways/history.xml.builder b/app/views/api/old_ways/index.xml.builder similarity index 100% rename from app/views/api/old_ways/history.xml.builder rename to app/views/api/old_ways/index.xml.builder diff --git a/app/views/layouts/_offline_flash.erb b/app/views/layouts/_offline_flash.erb new file mode 100644 index 000000000..364788dcf --- /dev/null +++ b/app/views/layouts/_offline_flash.erb @@ -0,0 +1,26 @@ +
+ <% if %w[database_offline api_offline].include? Settings.status %> +

+ <%= t(".osm_offline") %> +

+ <% elsif %w[database_readonly api_readonly].include? Settings.status %> +

+ <%= t(".osm_read_only") %> +

+ <% end %> + + <% if Settings.status_expected_restore_date %> + <% expected_restore_time = Time.parse(Settings.status_expected_restore_date).utc %> + <% if expected_restore_time > Time.now.utc %> +

+ <%= t ".expected_restore_html", :time => friendly_date(expected_restore_time) %> +

+ <% end %> + <% end %> + + <% if Settings.status_announcement_url %> +

+ <%= link_to t(".announcement"), Settings.status_announcement_url %> +

+ <% end %> +
diff --git a/app/views/old_elements/_actions.html.erb b/app/views/old_elements/_actions.html.erb index 36a16abb4..b4d28123c 100644 --- a/app/views/old_elements/_actions.html.erb +++ b/app/views/old_elements/_actions.html.erb @@ -2,7 +2,7 @@ <%= link_to t("browse.view_details"), :controller => @type.pluralize, :action => :show %> <% if !@feature.redacted? %> · - <%= link_to t("browse.download_xml"), :controller => "api/old_#{@type.pluralize}", :action => :show %> + <%= link_to t("browse.download_xml"), send(:"api_#{@type}_version_path", *@feature.id) %> <% elsif current_user&.moderator? %> · <% if !params[:show_redactions] %> diff --git a/app/views/old_elements/index.html.erb b/app/views/old_elements/index.html.erb index d4ecbfa60..eaf7787fa 100644 --- a/app/views/old_elements/index.html.erb +++ b/app/views/old_elements/index.html.erb @@ -5,7 +5,7 @@ <%= render :partial => "browse/#{@type}", :collection => @feature.send(:"old_#{@type}s").reverse %>
- <%= link_to t("browse.download_xml"), :controller => "api/old_#{@type.pluralize}", :action => "history" %> + <%= link_to t("browse.download_xml"), send(:"api_#{@type}_versions_path", @feature.id) %> · <%= link_to t("browse.view_details"), :controller => @type.pluralize, :action => :show %> <% if params[:show_redactions] %> diff --git a/app/views/site/_not_public_flash.erb b/app/views/site/_not_public_flash.erb new file mode 100644 index 000000000..bcd010f35 --- /dev/null +++ b/app/views/site/_not_public_flash.erb @@ -0,0 +1,3 @@ +

<%= t ".not_public" %>

+

<%= t ".not_public_description_html", :user_page => (link_to t(".user_page_link"), edit_account_path(:anchor => "public")) %>

+

<%= t ".anon_edits_html", :link => link_to(t(".anon_edits_link_text"), t(".anon_edits_link")) %>

diff --git a/app/views/site/edit.html.erb b/app/views/site/edit.html.erb index 1eb733822..6d463df78 100644 --- a/app/views/site/edit.html.erb +++ b/app/views/site/edit.html.erb @@ -1,17 +1,5 @@ <% content_for :content do %> - <% if Settings.status == "database_offline" or Settings.status == "api_offline" %> -
-

<%= t "layouts.osm_offline" %>

-
- <% elsif Settings.status == "database_readonly" or Settings.status == "api_readonly" %> -
-

<%= t "layouts.osm_read_only" %>

-
- <% elsif !current_user.data_public? %> -

<%= t ".not_public" %>

-

<%= t ".not_public_description_html", :user_page => (link_to t(".user_page_link"), edit_account_path(:anchor => "public")) %>

-

<%= t ".anon_edits_html", :link => link_to(t(".anon_edits_link_text"), t(".anon_edits_link")) %>

- <% else %> + <% if @enable_editor %> <%= render :partial => preferred_editor %> <% end %> <% end %> diff --git a/config/eslint.js b/config/eslint.js index 90040f7bf..895ab380c 100644 --- a/config/eslint.js +++ b/config/eslint.js @@ -6,6 +6,14 @@ const stylisticJs = require("@stylistic/eslint-plugin-js"); module.exports = [ js.configs.recommended, erb.configs.recommended, + { + ignores: [ + "app/assets/javascripts/i18n/", + "coverage/assets/", + "public/assets/", + "vendor/" + ] + }, { plugins: { "@stylistic": stylisticJs @@ -19,8 +27,8 @@ module.exports = [ Cookies: "readonly", I18n: "readonly", L: "readonly", - OSM: "writable", Matomo: "readonly", + OSM: "writable", Turbo: "readonly", updateLinks: "readonly" } @@ -47,23 +55,25 @@ module.exports = [ "@stylistic/eol-last": "error", "@stylistic/func-call-spacing": "error", "@stylistic/indent": ["error", 2, { - SwitchCase: 1, - VariableDeclarator: "first", + CallExpression: { arguments: "first" }, FunctionDeclaration: { parameters: "first" }, FunctionExpression: { parameters: "first" }, - CallExpression: { arguments: "first" } + SwitchCase: 1, + VariableDeclarator: "first" }], "@stylistic/key-spacing": "error", "@stylistic/keyword-spacing": "error", + "@stylistic/max-statements-per-line": "error", "@stylistic/no-floating-decimal": "error", "@stylistic/no-mixed-operators": "error", - "@stylistic/no-multiple-empty-lines": "error", "@stylistic/no-multi-spaces": "error", + "@stylistic/no-multiple-empty-lines": "error", "@stylistic/no-trailing-spaces": "error", "@stylistic/no-whitespace-before-property": "error", "@stylistic/object-curly-newline": ["error", { consistent: true }], "@stylistic/object-curly-spacing": ["error", "always"], "@stylistic/object-property-newline": ["error", { allowAllPropertiesOnSameLine: true }], + "@stylistic/one-var-declaration-per-line": "error", "@stylistic/operator-linebreak": ["error", "after"], "@stylistic/padded-blocks": ["error", "never"], "@stylistic/quote-props": ["error", "consistent-as-needed", { keywords: true, numbers: true }], @@ -91,6 +101,7 @@ module.exports = [ "no-caller": "error", "no-console": "warn", "no-div-regex": "error", + "no-else-return": ["error", { allowElseIf: false }], "no-eq-null": "error", "no-eval": "error", "no-extend-native": "error", @@ -101,8 +112,8 @@ module.exports = [ "no-implied-eval": "error", "no-invalid-this": "error", "no-iterator": "error", - "no-labels": "error", "no-label-var": "error", + "no-labels": "error", "no-lone-blocks": "error", "no-lonely-if": "error", "no-loop-func": "error", @@ -126,12 +137,14 @@ module.exports = [ "no-unneeded-ternary": "error", "no-unused-expressions": "off", "no-unused-vars": ["error", { caughtErrors: "none" }], + "no-use-before-define": ["error", { functions: false }], "no-useless-call": "error", "no-useless-concat": "error", "no-useless-return": "error", - "no-use-before-define": ["error", { functions: false }], "no-void": "error", "no-warning-comments": "warn", + "operator-assignment": "error", + "prefer-object-spread": "error", "radix": ["error", "always"], "yoda": "error" } @@ -156,6 +169,9 @@ module.exports = [ globals: { ...globals.commonjs } + }, + rules: { + "sort-keys": ["error", "asc", { minKeys: 5 }] } } ]; diff --git a/config/locales/de.yml b/config/locales/de.yml index 59a663548..2333bf08f 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1683,6 +1683,7 @@ de: reports: Meldungen last_updated: Zuletzt aktualisiert last_updated_time_ago_user_html: '%{time_ago} von %{user}' + reporting_users: Benutzer melden reports_count: one: Eine Meldung other: '%{count} Meldungen' @@ -1721,6 +1722,8 @@ de: reopened: Der Problemstatus wurde auf „Offen“ geändert comments: comment_from_html: Kommentar von %{user_link} erstellt am %{comment_created_at} + reassign_to_moderators: Weise das Problem den Moderatoren zu + reassign_to_administrators: Weise das Problem den Administratoren zu reports: reported_by_html: Gemeldet als %{category} von %{user} bei %{updated_at} helper: @@ -3318,6 +3321,7 @@ de: open_title: Ungelöster Hinweis Nr. %{note_name} closed_title: Erledigter Hinweis Nr. %{note_name} hidden_title: Versteckter Hinweis Nr. %{note_name} + description_when_author_is_deleted: gelöscht event_opened_by_html: Erstellt von %{user} %{time_ago} event_opened_by_anonymous_html: Erstellt von anonym %{time_ago} event_commented_by_html: Kommentar von %{user} %{time_ago} diff --git a/config/locales/el.yml b/config/locales/el.yml index 7c48cafcb..d343d9785 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -1609,6 +1609,7 @@ el: reports: Αναφορές last_updated: Τελευταία ενημέρωση last_updated_time_ago_user_html: '%{time_ago} από %{user}' + reporting_users: Αναφερόμενοι Χρήστες reports_count: one: '%{count} Αναφορά' other: '%{count} Αναφορές' @@ -1648,6 +1649,8 @@ el: reopened: Η κατάσταση του ζητήματος έχει οριστεί σε 'Ανοιχτό' comments: comment_from_html: Σχόλιο από %{user_link} γραμμένο στις %{comment_created_at} + reassign_to_moderators: Ανάθεση ζητήματος σε Συντονιστές + reassign_to_administrators: Ανάθεση ζητήματος σε Διαχειριστές reports: reported_by_html: Αναφέρθηκε ως %{category} από %{user} στις %{updated_at} helper: @@ -3233,6 +3236,7 @@ el: open_title: 'Ανοικτή σημείωση #%{note_name}' closed_title: 'Επιλυμένη σημείωση #%{note_name}' hidden_title: 'Κρυφή σημείωση #%{note_name}' + description_when_author_is_deleted: διαγράφηκε event_opened_by_html: Δημιουργήθηκε από %{user} %{time_ago} event_opened_by_anonymous_html: Δημιουργήθηκε από ανώνυμο %{time_ago} event_commented_by_html: Σχολιάστηκε από %{user} %{time_ago} diff --git a/config/locales/en.yml b/config/locales/en.yml index 7b2be6a0c..9aabfc92a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -942,6 +942,7 @@ en: bridleway: "Bridleway" bus_guideway: "Guided Bus Lane" bus_stop: "Bus Stop" + busway: "Busway" construction: "Highway under Construction" corridor: "Corridor" crossing: "Crossing" @@ -1624,8 +1625,6 @@ en: partners_corpmembers: "OSMF corporate members" partners_partners: "partners" tou: "Terms of Use" - osm_offline: "The OpenStreetMap database is currently offline while essential database maintenance work is carried out." - osm_read_only: "The OpenStreetMap database is currently in read-only mode while essential database maintenance work is carried out." nothing_to_preview: "Nothing to preview." help: Help about: About @@ -1633,6 +1632,11 @@ en: communities: Communities learn_more: "Learn More" more: More + offline_flash: + osm_offline: "The OpenStreetMap database is currently offline while essential maintenance work is carried out." + osm_read_only: "The OpenStreetMap database is currently in read-only mode while essential maintenance work is carried out." + expected_restore_html: "Services are expected to be restored in %{time}." + announcement: "You can read the announcement here." user_mailer: diary_comment_notification: description: "OpenStreetMap Diary Entry #%{id}" @@ -2249,13 +2253,14 @@ en: license_url: "https://openstreetmap.org/copyright" project_url: "https://openstreetmap.org" remote_failed: "Editing failed - make sure JOSM or Merkaartor is loaded and the remote control option is enabled" - edit: + not_public_flash: not_public: "You have not set your edits to be public." not_public_description_html: "You can no longer edit the map unless you do so. You can set your edits as public from your %{user_page}." user_page_link: user page anon_edits_html: "(%{link})" anon_edits_link: "https://wiki.openstreetmap.org/wiki/Disabling_anonymous_edits" anon_edits_link_text: "Find out why this is the case." + edit: id_not_configured: "iD has not been configured" export: title: "Export" diff --git a/config/locales/gd.yml b/config/locales/gd.yml index 083612acc..02d9047e6 100644 --- a/config/locales/gd.yml +++ b/config/locales/gd.yml @@ -49,6 +49,7 @@ gd: message: Teachdaireachd node: Nòd node_tag: Taga nòid + note: Nòta old_node: Seann nòd old_node_tag: Taga seann nòid old_relation: Seann dàimh @@ -76,6 +77,8 @@ gd: name: Ainm (Riatanach) callback_url: URL ais-ghairm support_url: URL taice + allow_read_prefs: na roghainnean a' chleachdaiche aca a leughadh + allow_write_prefs: na roghainnean a' chleachdaiche aca atharrachadh allow_write_api: am mapa atharrachadh allow_read_gpx: na lorgaidhean GPS prìobhaideach aca a leughadh allow_write_gpx: lorgaidhean GPS a luchdadh suas @@ -91,6 +94,7 @@ gd: language_code: Cànan doorkeeper/application: name: Ainm + scopes: Ceadan friend: user: Cleachdaiche friend: Caraid @@ -103,7 +107,7 @@ gd: longitude: Domhan-fhad public: Poblach description: Tuairisgeul - gpx_file: Luchdaich suas faidhle GPX + gpx_file: Tagh faidhle lorgadh GPS visibility: Faicsinneachd tagstring: Tagaichean message: @@ -114,12 +118,14 @@ gd: redaction: title: Tiotal description: Tuairisgeul + report: + category: Tagh adhbhar dhan aithris agad user: email: Post-d new_email: An seòladh puist-d ùr active: Gnìomhach display_name: Ainm seallaidh - description: Tuairisgeul + description: Tuairisgeul Pròifile home_lat: Domhan-leud home_lon: Domhan-fhad languages: Cànain @@ -206,6 +212,9 @@ gd: auth: providers: google: Google + facebook: Facebook + github: GitHub + wikipedia: Uicipeid api: notes: comment: @@ -238,7 +247,7 @@ gd: openid: link text: dè th`ann? contributor terms: - heading: 'Teirmichean a'' chom-pàirtiche:' + heading: Teirmichean a' chom-pàirtiche agreed: Dh'aontaich thu ri teirmichean ùra a' chom-pàirtiche. not yet agreed: Cha do dh'aontaich thu ri teirmichean ùra a' chom-pàirtiche fhathast. @@ -261,11 +270,13 @@ gd: cancel: Sguir dheth terms: show: - title: Teirmichean a' chom-pàirtiche - heading: Teirmichean a' chom-pàirtiche + title: Teirmichean + heading: Teirmichean + heading_ct: Teirmichean a' chom-pàirtiche consider_pd: A bharrachd air an aonta gu h-àrd, aontaichidh mi gun dèid na bheir mi dhuibh 'nam cho-thabhartaiche a chur sa Public Domain consider_pd_why: Dè th`ann? + cancel: Sguir dheth you need to accept or decline: Feuch an leugh thu teirmichean ùra a' chom-pàirtiche agus an uairsin aontaich no nach gabh riutha mus lean thu air adhart. legale_select: 'Dùthaich còmhnaidh:' @@ -404,8 +415,11 @@ gd: show: title: 'Seata atharraichean: %{id}' created: 'Air a chruthachadh: %{when}' + closed: 'Air a dhùnadh: %{when}' created_ago_html: Air a chruthachadh %{time_ago} + closed_ago_html: Air a dhùnadh %{time_ago} created_ago_by_html: Air a chruthachadh %{time_ago} le %{user} + closed_ago_by_html: Air a dhùnadh %{time_ago} le %{user} discussion: Deasbaireachd join_discussion: Clàraich a-steach gus pàirt a ghabhail san deasbaireachd subscribe: Fo-sgrìobh @@ -440,6 +454,7 @@ gd: nearby users: Cleachdaichean am fagas eile no nearby users: Chan eil cleachdaiche sam bith eile ann a dh'innis gu bheil iad ris an obair-mhapa am fagas. + followed_changesets: seataichean atharraichean nearby_changesets: seata atharraichean nan cleachdaichean am fagas nearby_diaries: clàran leabhair-latha am fagas diary_entries: @@ -517,12 +532,13 @@ gd: station: Stèisean trama-adhair aeroway: aerodrome: Raon-adhair - apron: Aparan - gate: Geata + apron: Aparan puirt-adhair + gate: Geata puirt-adhair + hangar: Hangar helipad: Port-heileacoptair runway: Raon-laighe taxiway: Raon-cuairteachaidh - terminal: Tèirmineal + terminal: Tèirmineal puirt-adhair amenity: animal_shelter: Fasgadh bheathaichean arts_centre: Ionad ealain @@ -562,13 +578,15 @@ gd: fire_station: Stèisean-smàlaidh food_court: Talla bìdh fountain: Fuaran - fuel: Connadh + fuel: Stèisean-peatrail gambling: Cearrachas grave_yard: Cladh hospital: Ospadal hunting_stand: Stannd seilge ice_cream: Reòiteagan + internet_cafe: Cafaidh eadar-lìn kindergarten: Sgoil-àraich + language_school: Sgoil cànain library: Leabhar-lann marketplace: Ionad-margaidh monastery: Manachainn @@ -607,6 +625,7 @@ gd: waste_basket: Bogsa-sgudail waste_disposal: Ionad-sgudail boundary: + aboriginal_lands: Tìrean Tùsanaich administrative: Crìoch rianachd census: Crìoch cunntas-sluaigh national_park: Pàirc nàiseanta @@ -618,9 +637,12 @@ gd: viaduct: Drochaid-rathaid "yes": Drochaid building: + chapel: Seipeal garage: Garaids garages: Garaidsean + hangar: Hangar house: Taigh + roof: Mullach shed: Seada static_caravan: Carabhan "yes": Togalach @@ -633,6 +655,7 @@ gd: photographer: Neach-dhealbh plumber: Plumair shoemaker: Greusaiche + stonemason: Clachair tailor: Tàillear "yes": Bùth cheàrd emergency: @@ -646,6 +669,7 @@ gd: bus_guideway: Lonaig bus-stiùirichte bus_stop: Stad-bus construction: Mòr-rathad 'ga thogail + corridor: Trannsa cycleway: Slighe baidhseagail elevator: Àrdaichear emergency_access_point: Puing-inntrigidh èiginn @@ -706,6 +730,8 @@ gd: wayside_cross: Cros ri taobh an rathaid wayside_shrine: Naomh-chiste ri taobh an rathaid wreck: Long bhriste + information: + map: Mapa junction: "yes": Gobhal landuse: @@ -766,7 +792,10 @@ gd: track: Cuairt-ruith water_park: Pàirc-uisge "yes": Cur-seachad + lock: + "yes": Tuil-dhoras man_made: + bridge: Drochaid cairn: Càrn lighthouse: Taigh-solais pier: Cidhe @@ -814,6 +843,7 @@ gd: sand: Gainmheach scree: Sgàirneach scrub: Fiodhach + shingle: Mol spring: Fuaran stone: Clach strait: Caolas @@ -902,7 +932,9 @@ gd: carpet: Bùth bhratan charity: Bùth carthannais chemist: Bùth-chungaidhean + chocolate: Seòclaid clothes: Bùth aodach + coffee: Bùth cofaidh computer: Bùth choimpiutairean confectionery: Bùth mìlseanachd convenience: Bùth goireasach @@ -928,9 +960,10 @@ gd: grocery: Gròsair hairdresser: Gruagaire hardware: Bùth leasachadh dachaigh - hifi: HiFi + hifi: Bùth HiFi jewelry: Bùth usgaran kiosk: Cìtheasg + kitchen: Bùth cidsin laundry: Taigh-nigheachain mall: Ionad-seopadaireachd mobile_phone: Bùth fhònaichean-làmhe @@ -942,6 +975,7 @@ gd: outdoor: Bùth acainnean blàir pet: Bùth pheatachan photo: Bùth dhealbhan + seafood: Biadh na mara second_hand: Bùth rudan ath-làimhe shoes: Bùth bhrògan sports: Bùth spòrs @@ -977,6 +1011,11 @@ gd: tunnel: culvert: Cùlbhart "yes": Tunail + water: + lake: Loch + pond: Lod + reservoir: Stòr-amar + lock: Tuil-dhoras waterway: artificial: Slighe-uisge fhuadain boatyard: Bàta-lann @@ -1012,6 +1051,9 @@ gd: search: Lorg states: open: Fosgailte + page: + states: + open: Fosgailte show: reports: one: '%{count} aithris' @@ -1023,6 +1065,15 @@ gd: helper: reportable_title: note: 'Nòta #%{note_id}' + reports: + new: + categories: + diary_comment: + other_label: Eile + user: + other_label: Eile + note: + other_label: Eile layouts: logo: alt_text: Suaicheantas OpenStreetMap @@ -1040,6 +1091,7 @@ gd: intro_header: Fàilte gu OpenStreetMap! intro_text: '''S e mapa an t-saoghail a tha san OpenStreetMap a chaidh a chruthachadh le daoine mar thu fhèin ''s a tha saor fo cheadachas fhosgailte.' + partners_fastly: Fastly partners_partners: com-pàirtichean eile osm_offline: Tha an stòr-dàta aig OpenStreetMap far loidhńe an-dràsta on a tha sinn a' dèanamh obair-charaidh riatanach air. @@ -1069,6 +1121,9 @@ gd: hi: Shin thu, %{to_user}, see_their_profile: '''S urrainn dhut a'' phròifil aig an neach ud a shealltainn air %{userurl}.' + gpx_details: + description: Tuairisgeul + tags: Tagaichean gpx_failure: hi: Shin thu, %{to_user}, failed_to_import: 'ion-phortachadh. Seo a'' mhearachd:' @@ -1250,8 +1305,17 @@ gd: an URL? update: flash changed: Chaidh am facal-faire agad atharrachadh. + preferences: + show: + site_color_schemes: + light: Soilleir + dark: Dorcha + map_color_schemes: + light: Soilleir + dark: Dorcha profiles: edit: + cancel: Sguir dheth image: Dealbh gravatar: gravatar: Cleachd Gravatar @@ -1286,6 +1350,9 @@ gd: richtext_field: edit: Deasaich pagination: + changeset_comments: + older: Beachdan nas sine + newer: Beachdan nas ùire diary_comments: older: Beachdan nas sine newer: Beachdan nas ùire @@ -1823,6 +1890,7 @@ gd: description: Tuairisgeul created_at: Air a chruthachadh last_changed: An t-atharrachadh mu dheireadh + closed: Dùinte show: title: 'Nòta: %{id}' description: Tuairisgeul diff --git a/config/locales/gl.yml b/config/locales/gl.yml index aa761d9f6..ad5d747b6 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -1580,6 +1580,7 @@ gl: reports: Denuncias last_updated: Última actualización last_updated_time_ago_user_html: '%{time_ago} por %{user}' + reporting_users: Usuarios denunciantes reports_count: one: '%{count} denuncia' other: '%{count} denuncias' @@ -1618,6 +1619,8 @@ gl: reopened: O estado da incidencia mudou a "Aberta" comments: comment_from_html: Comentario de %{user_link} no %{comment_created_at} + reassign_to_moderators: Reasignar a incidencia aos moderadores + reassign_to_administrators: Reasignar a incidencia aos administradores reports: reported_by_html: Denunciado coma %{category} por %{user} o %{updated_at} helper: @@ -3183,6 +3186,7 @@ gl: open_title: Nota sen resolver n.º %{note_name} closed_title: Nota resolta n.º %{note_name} hidden_title: Nota agochada n.º %{note_name} + description_when_author_is_deleted: eliminado event_opened_by_html: Creado por %{user} %{time_ago} event_opened_by_anonymous_html: Creado por un usuario anónimo %{time_ago} event_commented_by_html: Comentario de %{user} %{time_ago} diff --git a/config/locales/it.yml b/config/locales/it.yml index c147303be..271d8272a 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -3216,6 +3216,7 @@ it: open_title: 'Nota irrisolta #%{note_name}' closed_title: 'Nota risolta #%{note_name}' hidden_title: 'Nota nascosta #%{note_name}' + description_when_author_is_deleted: cancellato event_opened_by_html: Creata da %{user} %{time_ago} event_opened_by_anonymous_html: Creata da anonimo %{time_ago} event_commented_by_html: Commento da %{user} %{time_ago} diff --git a/config/locales/lb.yml b/config/locales/lb.yml index ead60fb80..b950cc390 100644 --- a/config/locales/lb.yml +++ b/config/locales/lb.yml @@ -1453,6 +1453,7 @@ lb: reports: Rapporten last_updated: Lescht Aktualiséierung last_updated_time_ago_user_html: '%{time_ago} vum %{user}' + reporting_users: Benotzer mellen reports_count: one: '%{count} Bericht' other: '%{count} Berichter' @@ -2492,6 +2493,7 @@ lb: open_title: 'Ongeléisten Hiweis #%{note_name}' closed_title: 'Geléisten Hiweis #%{note_name}' hidden_title: Verstoppt Notiz N° %{note_name} + description_when_author_is_deleted: geläscht report: Dësen Hiweis mellen discussion: Diskussioun subscribe: Abonéieren diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 04f1d259f..d7fd37996 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -18,6 +18,7 @@ # Author: BushmanK # Author: Butko # Author: CM3X +# Author: Cabadeck # Author: Calibrator # Author: Chilin # Author: Cjaushe4ka @@ -1712,6 +1713,7 @@ ru: reopened: Статус проблемы был установлен в "Открыто" comments: comment_from_html: Комментарий участника %{user_link}, созданный %{comment_created_at} + reassign_to_administrators: Перенаправить проблему Администраторам reports: reported_by_html: Указано как %{category} пользователем %{user} %{updated_at} helper: diff --git a/config/locales/skr-arab.yml b/config/locales/skr-arab.yml index 08fc951a5..3f1d4dad3 100644 --- a/config/locales/skr-arab.yml +++ b/config/locales/skr-arab.yml @@ -1367,6 +1367,7 @@ skr-arab: show: title: نوٹ:%{id} description: تفصیل + description_when_author_is_deleted: مٹا ݙتے discussion: بحث مباحثہ subscribe: سبسکرائب کرو unsubscribe: اݨ سبسکرائب کرو diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 82e9c870d..af42dc29c 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -2334,6 +2334,7 @@ sr: open_title: Нерешена белешка бр. %{note_name} closed_title: Решена белешка бр. %{note_name} hidden_title: Скривена белешка бр. %{note_name} + description_when_author_is_deleted: обрисано event_opened_by_html: Направио %{user} %{time_ago} report: пријави ову белешку anonymous_warning: Ова белешка садржи коментаре анонимних корисника које би diff --git a/config/routes.rb b/config/routes.rb index 2b8632698..54bf037cf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -30,17 +30,11 @@ OpenStreetMap::Application.routes.draw do post "changeset/comment/:id/hide" => "changeset_comments#destroy", :as => :changeset_comment_hide, :id => /\d+/ post "changeset/comment/:id/unhide" => "changeset_comments#restore", :as => :changeset_comment_unhide, :id => /\d+/ - get "node/:id/history" => "old_nodes#history", :as => :api_node_history, :id => /\d+/ - post "node/:id/:version/redact" => "old_nodes#redact", :as => :node_version_redact, :version => /\d+/, :id => /\d+/ - get "node/:id/:version" => "old_nodes#show", :as => :api_old_node, :id => /\d+/, :version => /\d+/ + post "node/:node_id/:version/redact" => "old_nodes#redact", :as => :node_version_redact, :version => /\d+/, :node_id => /\d+/ - get "way/:id/history" => "old_ways#history", :as => :api_way_history, :id => /\d+/ - post "way/:id/:version/redact" => "old_ways#redact", :as => :way_version_redact, :version => /\d+/, :id => /\d+/ - get "way/:id/:version" => "old_ways#show", :as => :api_old_way, :id => /\d+/, :version => /\d+/ + post "way/:way_id/:version/redact" => "old_ways#redact", :as => :way_version_redact, :version => /\d+/, :id => /\d+/ - get "relation/:id/history" => "old_relations#history", :as => :api_relation_history, :id => /\d+/ - post "relation/:id/:version/redact" => "old_relations#redact", :as => :relation_version_redact, :version => /\d+/, :id => /\d+/ - get "relation/:id/:version" => "old_relations#show", :as => :api_old_relation, :id => /\d+/, :version => /\d+/ + post "relation/:relation_id/:version/redact" => "old_relations#redact", :as => :relation_version_redact, :version => /\d+/, :id => /\d+/ end namespace :api, :path => "api/0.6" do @@ -50,6 +44,8 @@ OpenStreetMap::Application.routes.draw do resources :ways, :only => :index resources :relations, :only => :index end + resources :versions, :path => "history", :controller => :old_nodes, :only => :index + resources :versions, :path => "", :version => /\d+/, :param => :version, :controller => :old_nodes, :only => :show end put "node/create" => "nodes#create", :as => nil @@ -61,6 +57,8 @@ OpenStreetMap::Application.routes.draw do scope :module => :ways do resources :relations, :only => :index end + resources :versions, :path => "history", :controller => :old_ways, :only => :index + resources :versions, :path => "", :version => /\d+/, :param => :version, :controller => :old_ways, :only => :show end put "way/create" => "ways#create", :as => nil @@ -72,6 +70,8 @@ OpenStreetMap::Application.routes.draw do scope :module => :relations do resources :relations, :only => :index end + resources :versions, :path => "history", :controller => :old_relations, :only => :index + resources :versions, :path => "", :version => /\d+/, :param => :version, :controller => :old_relations, :only => :show end put "relation/create" => "relations#create", :as => nil diff --git a/config/settings.yml b/config/settings.yml index c79199145..2e0346f00 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -23,6 +23,10 @@ api_version: "0.6" # database_offline - database offline with site in emergency mode # gpx_offline - gpx storage offline status: "online" +# Expected services restoration date added to offline flash messages +#status_expected_restore_date: "2024-12-18 12:00:00Z" +# Application status announcement url added to offline flash messages +#status_announcement_url: "https://en.osm.town/@osm_tech" # The maximum area you're allowed to request, in square degrees max_request_area: 0.25 # Number of GPS trace/trackpoints returned per-page diff --git a/db/migrate/20250206202905_add_text_index_to_notes.rb b/db/migrate/20250206202905_add_text_index_to_notes.rb new file mode 100644 index 000000000..7798f2287 --- /dev/null +++ b/db/migrate/20250206202905_add_text_index_to_notes.rb @@ -0,0 +1,7 @@ +class AddTextIndexToNotes < ActiveRecord::Migration[7.2] + disable_ddl_transaction! + + def change + add_index :notes, "to_tsvector('english', description)", :using => "GIN", :name => "index_notes_on_description", :algorithm => :concurrently + end +end diff --git a/db/structure.sql b/db/structure.sql index 9093c47fe..d23c2d748 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -2540,6 +2540,13 @@ CREATE INDEX index_note_comments_on_created_at ON public.note_comments USING btr CREATE INDEX index_note_subscriptions_on_note_id ON public.note_subscriptions USING btree (note_id); +-- +-- Name: index_notes_on_description; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_notes_on_description ON public.notes USING gin (to_tsvector('english'::regconfig, description)); + + -- -- Name: index_oauth_access_grants_on_application_id; Type: INDEX; Schema: public; Owner: - -- @@ -3422,6 +3429,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('23'), ('22'), ('21'), +('20250206202905'), ('20250121191749'), ('20250105154621'), ('20250104140952'), diff --git a/lib/tasks/eslint.rake b/lib/tasks/eslint.rake index 27b550e40..17f28a4e9 100644 --- a/lib/tasks/eslint.rake +++ b/lib/tasks/eslint.rake @@ -8,18 +8,12 @@ def config_file Rails.root.join("config/eslint.js").to_s end -def js_files - Rails.application.assets.each_file.select do |file| - (file.ends_with?(".js") || file.ends_with?(".js.erb")) && !file.match?(%r{/(gems|vendor|i18n|node_modules)/}) - end -end - namespace "eslint" do task :check => :environment do - system(yarn_path, "run", "eslint", "-c", config_file, *js_files) || abort + system(yarn_path, "run", "eslint", "-c", config_file, "--no-warn-ignored", Rails.root.to_s) || abort end task :fix => :environment do - system(yarn_path, "run", "eslint", "-c", config_file, "--fix", *js_files) || abort + system(yarn_path, "run", "eslint", "-c", config_file, "--no-warn-ignored", "--fix", Rails.root.to_s) || abort end end diff --git a/test/controllers/api/old_nodes_controller_test.rb b/test/controllers/api/old_nodes_controller_test.rb index 8ce19f3ea..0c5faa9f9 100644 --- a/test/controllers/api/old_nodes_controller_test.rb +++ b/test/controllers/api/old_nodes_controller_test.rb @@ -2,159 +2,139 @@ require "test_helper" module Api class OldNodesControllerTest < ActionDispatch::IntegrationTest - # - # 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" } + { :controller => "api/old_nodes", :action => "index", :node_id => "1" } ) assert_routing( - { :path => "/api/0.6/node/1/2", :method => :get }, - { :controller => "api/old_nodes", :action => "show", :id => "1", :version => "2" } + { :path => "/api/0.6/node/1/history.json", :method => :get }, + { :controller => "api/old_nodes", :action => "index", :node_id => "1", :format => "json" } ) assert_routing( - { :path => "/api/0.6/node/1/history.json", :method => :get }, - { :controller => "api/old_nodes", :action => "history", :id => "1", :format => "json" } + { :path => "/api/0.6/node/1/2", :method => :get }, + { :controller => "api/old_nodes", :action => "show", :node_id => "1", :version => "2" } ) assert_routing( { :path => "/api/0.6/node/1/2.json", :method => :get }, - { :controller => "api/old_nodes", :action => "show", :id => "1", :version => "2", :format => "json" } + { :controller => "api/old_nodes", :action => "show", :node_id => "1", :version => "2", :format => "json" } ) assert_routing( { :path => "/api/0.6/node/1/2/redact", :method => :post }, - { :controller => "api/old_nodes", :action => "redact", :id => "1", :version => "2" } + { :controller => "api/old_nodes", :action => "redact", :node_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, :lat => 0, :lon => 0, :changeset => create(:changeset, :user => private_user)) - user = create(:user) - node = create(:node, :with_history, :version => 4, :lat => 0, :lon => 0, :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 - auth_header = bearer_authorization_header private_user - - # setup a simple XML node - xml_doc = xml_for_node(private_node) - 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 - 0.5).to_s - xml_node["lon"] = precision(rand - 0.5).to_s - with_controller(NodesController.new) do - put api_node_path(nodeid), :params => xml_doc.to_s, :headers => auth_header - assert_response :forbidden, "Should have rejected node update" - xml_node["version"] = @response.body.to_s + def test_index + node = create(:node, :version => 2) + create(:old_node, :node_id => node.id, :version => 1, :latitude => 60 * OldNode::SCALE, :longitude => 30 * OldNode::SCALE) + create(:old_node, :node_id => node.id, :version => 2, :latitude => 61 * OldNode::SCALE, :longitude => 31 * OldNode::SCALE) + + get api_node_versions_path(node) + + assert_response :success + assert_dom "osm:root", 1 do + assert_dom "> node", 2 do |dom_nodes| + assert_dom dom_nodes[0], "> @id", node.id.to_s + assert_dom dom_nodes[0], "> @version", "1" + assert_dom dom_nodes[0], "> @lat", "60.0000000" + assert_dom dom_nodes[0], "> @lon", "30.0000000" + + assert_dom dom_nodes[1], "> @id", node.id.to_s + assert_dom dom_nodes[1], "> @version", "2" + assert_dom dom_nodes[1], "> @lat", "61.0000000" + assert_dom dom_nodes[1], "> @lon", "31.0000000" end - # save a version for later checking - versions[xml_node["version"]] = xml_doc.to_s end + 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 api_node_path(nodeid), :params => xml_doc.to_s, :headers => auth_header - 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 + ## + # test that redacted nodes aren't visible in the history + def test_index_redacted_unauthorised + node = create(:node, :with_history, :version => 2) + node.old_nodes.find_by(:version => 1).redact!(create(:redaction)) - # probably should check that they didn't get written to the database + get api_node_versions_path(node) - ## Now do it with the public user - auth_header = bearer_authorization_header user + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm node[id='#{node.id}'][version='1']", 0, + "redacted node #{node.id} version 1 shouldn't be present in the history." - # setup a simple XML node + get api_node_versions_path(node, :show_redactions => "true") - xml_doc = xml_for_node(node) - xml_node = xml_doc.find("//osm/node").first - nodeid = node.id + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm node[id='#{node.id}'][version='1']", 0, + "redacted node #{node.id} version 1 shouldn't be present in the history when passing flag." + end - # keep a hash of the versions => string, as we'll need something - # to test against later - versions = {} + def test_index_redacted_normal_user + node = create(:node, :with_history, :version => 2) + node.old_nodes.find_by(:version => 1).redact!(create(:redaction)) - # save a version for later checking - versions[xml_node["version"]] = xml_doc.to_s + get api_node_versions_path(node), :headers => bearer_authorization_header - # randomly move the node about - 3.times do - # move the node somewhere else - xml_node["lat"] = precision(rand - 0.5).to_s - xml_node["lon"] = precision(rand - 0.5).to_s - with_controller(NodesController.new) do - put api_node_path(nodeid), :params => xml_doc.to_s, :headers => auth_header - 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 + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm node[id='#{node.id}'][version='1']", 0, + "redacted node #{node.id} version 1 shouldn't be present in the history, even when logged in." - # 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 api_node_path(nodeid), :params => xml_doc.to_s, :headers => auth_header - 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 + get api_node_versions_path(node, :show_redactions => "true"), :headers => bearer_authorization_header + + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm node[id='#{node.id}'][version='1']", 0, + "redacted node #{node.id} version 1 shouldn't be present in the history, even when logged in and passing flag." + end - # check all the versions - versions.each_key do |key| - get api_old_node_path(nodeid, key.to_i) + def test_index_redacted_moderator + node = create(:node, :with_history, :version => 2) + node.old_nodes.find_by(:version => 1).redact!(create(:redaction)) + auth_header = bearer_authorization_header create(:moderator_user) - assert_response :success, - "couldn't get version #{key.to_i} of node #{nodeid}" + get api_node_versions_path(node), :headers => auth_header - check_node = Node.from_xml(versions[key]) - api_node = Node.from_xml(@response.body.to_s) + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm node[id='#{node.id}'][version='1']", 0, + "node #{node.id} version 1 should not be present in the history for moderators when not passing flag." + + get api_node_versions_path(node, :show_redactions => "true"), :headers => auth_header + + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm node[id='#{node.id}'][version='1']", 1, + "node #{node.id} version 1 should still be present in the history for moderators when passing flag." + end - assert_nodes_are_equal check_node, api_node + def test_show + node = create(:node, :version => 2) + create(:old_node, :node_id => node.id, :version => 1, :latitude => 60 * OldNode::SCALE, :longitude => 30 * OldNode::SCALE) + create(:old_node, :node_id => node.id, :version => 2, :latitude => 61 * OldNode::SCALE, :longitude => 31 * OldNode::SCALE) + + get api_node_version_path(node, 1) + + assert_response :success + assert_dom "osm:root", 1 do + assert_dom "> node", 1 do + assert_dom "> @id", node.id.to_s + assert_dom "> @version", "1" + assert_dom "> @lat", "60.0000000" + assert_dom "> @lon", "30.0000000" + end + end + + get api_node_version_path(node, 2) + + assert_response :success + assert_dom "osm:root", 1 do + assert_dom "> node", 1 do + assert_dom "> @id", node.id.to_s + assert_dom "> @version", "2" + assert_dom "> @lat", "61.0000000" + assert_dom "> @lon", "31.0000000" + end end end - def test_not_found_version + def test_show_not_found check_not_found_id_version(70000, 312344) check_not_found_id_version(-1, -13) check_not_found_id_version(create(:node).id, 24354) @@ -162,194 +142,132 @@ module Api 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 that redacted nodes aren't visible, regardless of + # authorisation except as moderator... + def test_show_redacted_unauthorised + node = create(:node, :with_history, :version => 2) + node.old_nodes.find_by(:version => 1).redact!(create(:redaction)) - # Ensure the lat/lon is formatted as a decimal e.g. not 4.0e-05 - def test_lat_lon_xml_format - old_node = create(:old_node, :latitude => (0.00004 * OldNode::SCALE).to_i, :longitude => (0.00008 * OldNode::SCALE).to_i) + get api_node_version_path(node, 1) - get api_node_history_path(old_node.node_id) - assert_match(/lat="0.0000400"/, response.body) - assert_match(/lon="0.0000800"/, response.body) - end + assert_response :forbidden, "Redacted node shouldn't be visible via the version API." - ## - # 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) + get api_node_version_path(node, 1, :show_redactions => "true") - do_redact_node(node_v3, - create(:redaction)) - assert_response :unauthorized, "should need to be authenticated to redact." + assert_response :forbidden, "Redacted node shouldn't be visible via the version API when passing flag." end - ## - # test the redaction of an old version of a node, while being - # authorised as a normal user. - def test_redact_node_normal_user - auth_header = bearer_authorization_header + def test_show_redacted_normal_user + node = create(:node, :with_history, :version => 2) + node.old_nodes.find_by(:version => 1).redact!(create(:redaction)) - node = create(:node, :with_history, :version => 4) - node_v3 = node.old_nodes.find_by(:version => 3) + get api_node_version_path(node, 1), :headers => bearer_authorization_header - do_redact_node(node_v3, - create(:redaction), - auth_header) - assert_response :forbidden, "should need to be moderator to redact." + assert_response :forbidden, "Redacted node shouldn't be visible via the version API, even when logged in." + + get api_node_version_path(node, 1, :show_redactions => "true"), :headers => bearer_authorization_header + + assert_response :forbidden, "Redacted node shouldn't be visible via the version API, even when logged in and passing flag." end - ## - # test that, even as moderator, the current version of a node - # can't be redacted. - def test_redact_node_current_version + def test_show_redacted_moderator + node = create(:node, :with_history, :version => 2) + node.old_nodes.find_by(:version => 1).redact!(create(:redaction)) auth_header = bearer_authorization_header create(:moderator_user) - node = create(:node, :with_history, :version => 4) - node_v4 = node.old_nodes.find_by(:version => 4) + get api_node_version_path(node, 1), :headers => auth_header - do_redact_node(node_v4, - create(:redaction), - auth_header) - assert_response :bad_request, "shouldn't be OK to redact current version as moderator." - end + assert_response :forbidden, "Redacted node should be gone for moderator, when flag not passed." - def test_redact_node_by_regular_without_write_redactions_scope - auth_header = bearer_authorization_header(create(:user), :scopes => %w[read_prefs write_api]) - do_redact_redactable_node(auth_header) - assert_response :forbidden, "should need to be moderator to redact." - end + get api_node_version_path(node, 1, :show_redactions => "true"), :headers => auth_header - def test_redact_node_by_regular_with_write_redactions_scope - auth_header = bearer_authorization_header(create(:user), :scopes => %w[write_redactions]) - do_redact_redactable_node(auth_header) - assert_response :forbidden, "should need to be moderator to redact." + assert_response :success, "Redacted node should not be gone for moderator, when flag passed." end - def test_redact_node_by_moderator_without_write_redactions_scope - auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[read_prefs write_api]) - do_redact_redactable_node(auth_header) - assert_response :forbidden, "should need to have write_redactions scope to redact." - end + # Ensure the lat/lon is formatted as a decimal e.g. not 4.0e-05 + def test_lat_lon_xml_format + old_node = create(:old_node, :latitude => (0.00004 * OldNode::SCALE).to_i, :longitude => (0.00008 * OldNode::SCALE).to_i) - def test_redact_node_by_moderator_with_write_redactions_scope - auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[write_redactions]) - do_redact_redactable_node(auth_header) - assert_response :success, "should be OK to redact old version as moderator with write_redactions scope." + get api_node_versions_path(old_node.node_id) + assert_match(/lat="0.0000400"/, response.body) + assert_match(/lon="0.0000800"/, response.body) end ## - # test that redacted nodes aren't visible, regardless of - # authorisation except as moderator... - def test_version_redacted + # test that, even as moderator, the current version of a node + # can't be redacted. + def test_redact_node_current_version node = create(:node, :with_history, :version => 2) - node_v1 = node.old_nodes.find_by(:version => 1) - node_v1.redact!(create(:redaction)) + old_node = node.old_nodes.find_by(:version => 2) + redaction = create(:redaction) + auth_header = bearer_authorization_header create(:moderator_user) - get api_old_node_path(node_v1.node_id, node_v1.version) - assert_response :forbidden, "Redacted node shouldn't be visible via the version API." + post node_version_redact_path(*old_node.id), :params => { :redaction => redaction.id }, :headers => auth_header - # not even to a logged-in user - auth_header = bearer_authorization_header - get api_old_node_path(node_v1.node_id, node_v1.version), :headers => auth_header - assert_response :forbidden, "Redacted node shouldn't be visible via the version API, even when logged in." + assert_response :bad_request, "shouldn't be OK to redact current version as moderator." + assert_nil old_node.reload.redaction end ## - # test that redacted nodes aren't visible in the history - def test_history_redacted + # 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 => 2) - node_v1 = node.old_nodes.find_by(:version => 1) - node_v1.redact!(create(:redaction)) + old_node = node.old_nodes.find_by(:version => 1) + redaction = create(:redaction) - get api_node_history_path(node) - 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." + post node_version_redact_path(*old_node.id), :params => { :redaction => redaction.id } - # not even to a logged-in user - auth_header = bearer_authorization_header - get api_node_history_path(node), :headers => auth_header - 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." + assert_response :unauthorized, "should need to be authenticated to redact." + assert_nil old_node.reload.redaction 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) - auth_header = bearer_authorization_header create(:moderator_user) + def test_redact_node_by_regular_without_write_redactions_scope + node = create(:node, :with_history, :version => 2) + old_node = node.old_nodes.find_by(:version => 1) + redaction = create(:redaction) + auth_header = bearer_authorization_header(create(:user), :scopes => %w[read_prefs write_api]) + + post node_version_redact_path(*old_node.id), :params => { :redaction => redaction.id }, :headers => auth_header - do_redact_node(node_v3, create(:redaction), auth_header) - assert_response :success, "should be OK to redact old version as moderator." + assert_response :forbidden, "should need to be moderator to redact." + assert_nil old_node.reload.redaction + end + + def test_redact_node_by_regular_with_write_redactions_scope + node = create(:node, :with_history, :version => 2) + old_node = node.old_nodes.find_by(:version => 1) + redaction = create(:redaction) + auth_header = bearer_authorization_header(create(:user), :scopes => %w[write_redactions]) - # check moderator can still see the redacted data, when passing - # the appropriate flag - get api_old_node_path(node_v3.node_id, node_v3.version), :headers => auth_header - assert_response :forbidden, "After redaction, node should be gone for moderator, when flag not passed." - get api_old_node_path(node_v3.node_id, node_v3.version, :show_redactions => "true"), :headers => auth_header - assert_response :success, "After redaction, node should not be gone for moderator, when flag passed." + post node_version_redact_path(*old_node.id), :params => { :redaction => redaction.id }, :headers => auth_header - # and when accessed via history - get api_node_history_path(node) - 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 api_node_history_path(node, :show_redactions => "true"), :headers => auth_header - 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." + assert_response :forbidden, "should need to be moderator to redact." + assert_nil old_node.reload.redaction 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) - auth_header = bearer_authorization_header create(:moderator_user) + def test_redact_node_by_moderator_without_write_redactions_scope + node = create(:node, :with_history, :version => 2) + old_node = node.old_nodes.find_by(:version => 1) + redaction = create(:redaction) + auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[read_prefs write_api]) - do_redact_node(node_v3, create(:redaction), auth_header) - assert_response :success, "should be OK to redact old version as moderator." + post node_version_redact_path(*old_node.id), :params => { :redaction => redaction.id }, :headers => auth_header - # re-auth as non-moderator - auth_header = bearer_authorization_header + assert_response :forbidden, "should need to have write_redactions scope to redact." + assert_nil old_node.reload.redaction + end - # check can't see the redacted data - get api_old_node_path(node_v3.node_id, node_v3.version), :headers => auth_header - assert_response :forbidden, "Redacted node shouldn't be visible via the version API." + def test_redact_node_by_moderator_with_write_redactions_scope + node = create(:node, :with_history, :version => 2) + old_node = node.old_nodes.find_by(:version => 1) + redaction = create(:redaction) + auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[write_redactions]) - # and when accessed via history - get api_node_history_path(node), :headers => auth_header - 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." + post node_version_redact_path(*old_node.id), :params => { :redaction => redaction.id }, :headers => auth_header + + assert_response :success, "should be OK to redact old version as moderator with write_redactions scope." + assert_equal redaction, old_node.reload.redaction end ## @@ -357,130 +275,54 @@ module Api # 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)) + old_node = node.old_nodes.find_by(:version => 1) + redaction = create(:redaction) + old_node.redact!(redaction) + + post node_version_redact_path(*old_node.id) - post node_version_redact_path(node_v1.node_id, node_v1.version) assert_response :unauthorized, "should need to be authenticated to unredact." + assert_equal redaction, old_node.reload.redaction 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)) + old_node = node.old_nodes.find_by(:version => 1) + redaction = create(:redaction) + old_node.redact!(redaction) + auth_header = bearer_authorization_header - auth_header = bearer_authorization_header user + post node_version_redact_path(*old_node.id), :headers => auth_header - post node_version_redact_path(node_v1.node_id, node_v1.version), :headers => auth_header assert_response :forbidden, "should need to be moderator to unredact." + assert_equal redaction, old_node.reload.redaction 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)) + old_node = node.old_nodes.find_by(:version => 1) + old_node.redact!(create(:redaction)) + auth_header = bearer_authorization_header create(:moderator_user) - auth_header = bearer_authorization_header moderator_user + post node_version_redact_path(*old_node.id), :headers => auth_header - post node_version_redact_path(node_v1.node_id, node_v1.version), :headers => auth_header 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 api_old_node_path(node_v1.node_id, node_v1.version), :headers => auth_header - assert_response :success, "After unredaction, node should not be gone for moderator." - - # and when accessed via history - get api_node_history_path(node) - 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." - - auth_header = bearer_authorization_header - - # check normal user can now see the redacted data - get api_old_node_path(node_v1.node_id, node_v1.version), :headers => auth_header - assert_response :success, "After unredaction, node should be visible to normal users." - - # and when accessed via history - get api_node_history_path(node) - 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." + assert_nil old_node.reload.redaction end private - def do_redact_redactable_node(headers = {}) - node = create(:node, :with_history, :version => 4) - node_v3 = node.old_nodes.find_by(:version => 3) - do_redact_node(node_v3, create(:redaction), headers) - end - - def do_redact_node(node, redaction, headers = {}) - get api_old_node_path(node.node_id, node.version), :headers => headers - assert_response :success, "should be able to get version #{node.version} of node #{node.node_id}." - - # now redact it - post node_version_redact_path(node.node_id, node.version), :params => { :redaction => redaction.id }, :headers => headers - end - - def check_current_version(node_id) - # get the current version of the node - current_node = with_controller(NodesController.new) do - get api_node_path(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 api_old_node_path(node_id, 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 - def check_not_found_id_version(id, version) - get api_old_node_path(id, version) + get api_node_version_path(id, version) assert_response :not_found rescue ActionController::UrlGenerationError => e assert_match(/No route matches/, e.to_s) 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 { 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 diff --git a/test/controllers/api/old_relations_controller_test.rb b/test/controllers/api/old_relations_controller_test.rb index 880c34011..603499503 100644 --- a/test/controllers/api/old_relations_controller_test.rb +++ b/test/controllers/api/old_relations_controller_test.rb @@ -7,191 +7,258 @@ module Api def test_routes assert_routing( { :path => "/api/0.6/relation/1/history", :method => :get }, - { :controller => "api/old_relations", :action => "history", :id => "1" } + { :controller => "api/old_relations", :action => "index", :relation_id => "1" } ) assert_routing( - { :path => "/api/0.6/relation/1/2", :method => :get }, - { :controller => "api/old_relations", :action => "show", :id => "1", :version => "2" } + { :path => "/api/0.6/relation/1/history.json", :method => :get }, + { :controller => "api/old_relations", :action => "index", :relation_id => "1", :format => "json" } ) assert_routing( - { :path => "/api/0.6/relation/1/history.json", :method => :get }, - { :controller => "api/old_relations", :action => "history", :id => "1", :format => "json" } + { :path => "/api/0.6/relation/1/2", :method => :get }, + { :controller => "api/old_relations", :action => "show", :relation_id => "1", :version => "2" } ) assert_routing( { :path => "/api/0.6/relation/1/2.json", :method => :get }, - { :controller => "api/old_relations", :action => "show", :id => "1", :version => "2", :format => "json" } + { :controller => "api/old_relations", :action => "show", :relation_id => "1", :version => "2", :format => "json" } ) assert_routing( { :path => "/api/0.6/relation/1/2/redact", :method => :post }, - { :controller => "api/old_relations", :action => "redact", :id => "1", :version => "2" } + { :controller => "api/old_relations", :action => "redact", :relation_id => "1", :version => "2" } ) end - # ------------------------------------- - # Test reading old relations. - # ------------------------------------- - def test_history - # check that a visible relations is returned properly - get api_relation_history_path(create(:relation, :with_history)) + ## + # check that a visible relations is returned properly + def test_index + relation = create(:relation, :with_history, :version => 2) + + get api_relation_versions_path(relation) + assert_response :success + assert_dom "osm:root", 1 do + assert_dom "> relation", 2 do |dom_relations| + assert_dom dom_relations[0], "> @id", relation.id.to_s + assert_dom dom_relations[0], "> @version", "1" + + assert_dom dom_relations[1], "> @id", relation.id.to_s + assert_dom dom_relations[1], "> @version", "2" + end + end + end - # check chat a non-existent relations is not returned - get api_relation_history_path(0) + ## + # check that a non-existent relations is not returned + def test_index_invalid + get api_relation_versions_path(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) + # test that redacted relations aren't visible in the history + def test_index_redacted_unauthorised + relation = create(:relation, :with_history, :version => 2) + relation.old_relations.find_by(:version => 1).redact!(create(:redaction)) - do_redact_relation(relation_v3, create(:redaction)) - assert_response :unauthorized, "should need to be authenticated to redact." - end + get api_relation_versions_path(relation) - ## - # 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) + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm relation[id='#{relation.id}'][version='1']", 0, + "redacted relation #{relation.id} version 1 shouldn't be present in the history." - auth_header = bearer_authorization_header + get api_relation_versions_path(relation, :show_redactions => "true") - do_redact_relation(relation_v3, create(:redaction), auth_header) - assert_response :forbidden, "should need to be moderator to redact." + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm relation[id='#{relation.id}'][version='1']", 0, + "redacted relation #{relation.id} version 1 shouldn't be present in the history when passing flag." 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 + def test_index_redacted_normal_user + relation = create(:relation, :with_history, :version => 2) + relation.old_relations.find_by(:version => 1).redact!(create(:redaction)) - auth_header = bearer_authorization_header create(:moderator_user) + get api_relation_versions_path(relation), :headers => bearer_authorization_header - do_redact_relation(relation_latest, create(:redaction), auth_header) - assert_response :bad_request, "shouldn't be OK to redact current version as moderator." - end + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm relation[id='#{relation.id}'][version='1']", 0, + "redacted relation #{relation.id} version 1 shouldn't be present in the history, even when logged in." - def test_redact_relation_by_regular_without_write_redactions_scope - auth_header = bearer_authorization_header(create(:user), :scopes => %w[read_prefs write_api]) - do_redact_redactable_relation(auth_header) - assert_response :forbidden, "should need to be moderator to redact." - end + get api_relation_versions_path(relation, :show_redactions => "true"), :headers => bearer_authorization_header - def test_redact_relation_by_regular_with_write_redactions_scope - auth_header = bearer_authorization_header(create(:user), :scopes => %w[write_redactions]) - do_redact_redactable_relation(auth_header) - assert_response :forbidden, "should need to be moderator to redact." + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm relation[id='#{relation.id}'][version='1']", 0, + "redacted relation #{relation.id} version 1 shouldn't be present in the history, even when logged in and passing flag." end - def test_redact_relation_by_moderator_without_write_redactions_scope - auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[read_prefs write_api]) - do_redact_redactable_relation(auth_header) - assert_response :forbidden, "should need to have write_redactions scope to redact." + def test_index_redacted_moderator + relation = create(:relation, :with_history, :version => 2) + relation.old_relations.find_by(:version => 1).redact!(create(:redaction)) + auth_header = bearer_authorization_header create(:moderator_user) + + get api_relation_versions_path(relation), :headers => auth_header + + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm relation[id='#{relation.id}'][version='1']", 0, + "relation #{relation.id} version 1 should not be present in the history for moderators when not passing flag." + + get api_relation_versions_path(relation, :show_redactions => "true"), :headers => auth_header + + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm relation[id='#{relation.id}'][version='1']", 1, + "relation #{relation.id} version 1 should still be present in the history for moderators when passing flag." end - def test_redact_relation_by_moderator_with_write_redactions_scope - auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[write_redactions]) - do_redact_redactable_relation(auth_header) - assert_response :success, "should be OK to redact old version as moderator with write_redactions scope." + def test_show + relation = create(:relation, :with_history, :version => 2) + create(:old_relation_tag, :old_relation => relation.old_relations[0], :k => "k1", :v => "v1") + create(:old_relation_tag, :old_relation => relation.old_relations[1], :k => "k2", :v => "v2") + + get api_relation_version_path(relation, 1) + + assert_response :success + assert_dom "osm:root", 1 do + assert_dom "> relation", 1 do + assert_dom "> @id", relation.id.to_s + assert_dom "> @version", "1" + assert_dom "> tag", 1 do + assert_dom "> @k", "k1" + assert_dom "> @v", "v1" + end + end + end + + get api_relation_version_path(relation, 2) + + assert_response :success + assert_dom "osm:root", 1 do + assert_dom "> relation", 1 do + assert_dom "> @id", relation.id.to_s + assert_dom "> @version", "2" + assert_dom "> tag", 1 do + assert_dom "> @k", "k2" + assert_dom "> @v", "v2" + end + end + end end ## # test that redacted relations aren't visible, regardless of # authorisation except as moderator... - def test_version_redacted + def test_show_redacted_unauthorised relation = create(:relation, :with_history, :version => 2) - relation_v1 = relation.old_relations.find_by(:version => 1) - relation_v1.redact!(create(:redaction)) + relation.old_relations.find_by(:version => 1).redact!(create(:redaction)) + + get api_relation_version_path(relation, 1) - get api_old_relation_path(relation_v1.relation_id, relation_v1.version) assert_response :forbidden, "Redacted relation shouldn't be visible via the version API." - # not even to a logged-in user - auth_header = bearer_authorization_header - get api_old_relation_path(relation_v1.relation_id, relation_v1.version), :headers => auth_header + get api_relation_version_path(relation, 1, :show_redactions => "true") + + assert_response :forbidden, "Redacted relation shouldn't be visible via the version API when passing flag." + end + + def test_show_redacted_normal_user + relation = create(:relation, :with_history, :version => 2) + relation.old_relations.find_by(:version => 1).redact!(create(:redaction)) + + get api_relation_version_path(relation, 1), :headers => bearer_authorization_header + assert_response :forbidden, "Redacted relation shouldn't be visible via the version API, even when logged in." + + get api_relation_version_path(relation, 1, :show_redactions => "true"), :headers => bearer_authorization_header + + assert_response :forbidden, "Redacted relation shouldn't be visible via the version API, even when logged in and passing flag." + end + + def test_show_redacted_moderator + relation = create(:relation, :with_history, :version => 2) + relation.old_relations.find_by(:version => 1).redact!(create(:redaction)) + auth_header = bearer_authorization_header create(:moderator_user) + + get api_relation_version_path(relation, 1), :headers => auth_header + + assert_response :forbidden, "Redacted relation should be gone for moderator, when flag not passed." + + get api_relation_version_path(relation, 1, :show_redactions => "true"), :headers => auth_header + + assert_response :success, "Redacted relation should not be gone for moderator, when flag passed." end ## - # test that redacted relations aren't visible in the history - def test_history_redacted + # 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 => 2) - relation_v1 = relation.old_relations.find_by(:version => 1) - relation_v1.redact!(create(:redaction)) + old_relation = relation.old_relations.find_by(:version => 2) + redaction = create(:redaction) + auth_header = bearer_authorization_header create(:moderator_user) - get api_relation_history_path(relation) - 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." + post relation_version_redact_path(*old_relation.id), :params => { :redaction => redaction.id }, :headers => auth_header - # not even to a logged-in user - auth_header = bearer_authorization_header - get api_old_relation_path(relation_v1.relation_id, relation_v1.version), :headers => auth_header - get api_relation_history_path(relation), :headers => auth_header - 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." + assert_response :bad_request, "shouldn't be OK to redact current version as moderator." + assert_nil old_relation.reload.redaction 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) + # 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 => 2) + old_relation = relation.old_relations.find_by(:version => 1) + redaction = create(:redaction) - auth_header = bearer_authorization_header create(:moderator_user) + post relation_version_redact_path(*old_relation.id), :params => { :redaction => redaction.id } - do_redact_relation(relation_v3, create(:redaction), auth_header) - assert_response :success, "should be OK to redact old version as moderator." + assert_response :unauthorized, "should need to be authenticated to redact." + assert_nil old_relation.reload.redaction + end - # check moderator can still see the redacted data, when passing - # the appropriate flag - get api_old_relation_path(relation_v3.relation_id, relation_v3.version), :headers => auth_header - assert_response :forbidden, "After redaction, relation should be gone for moderator, when flag not passed." - get api_old_relation_path(relation_v3.relation_id, relation_v3.version, :show_redactions => "true"), :headers => auth_header - assert_response :success, "After redaction, relation should not be gone for moderator, when flag passed." + def test_redact_relation_by_regular_without_write_redactions_scope + relation = create(:relation, :with_history, :version => 2) + old_relation = relation.old_relations.find_by(:version => 1) + redaction = create(:redaction) + auth_header = bearer_authorization_header(create(:user), :scopes => %w[read_prefs write_api]) - # and when accessed via history - get api_relation_history_path(relation), :headers => auth_header - 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 api_relation_history_path(relation, :show_redactions => "true"), :headers => auth_header - 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." + post relation_version_redact_path(*old_relation.id), :params => { :redaction => redaction.id }, :headers => auth_header + + assert_response :forbidden, "should need to be moderator to redact." + assert_nil old_relation.reload.redaction 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) + def test_redact_relation_by_regular_with_write_redactions_scope + relation = create(:relation, :with_history, :version => 2) + old_relation = relation.old_relations.find_by(:version => 1) + redaction = create(:redaction) + auth_header = bearer_authorization_header(create(:user), :scopes => %w[write_redactions]) - auth_header = bearer_authorization_header create(:moderator_user) + post relation_version_redact_path(*old_relation.id), :params => { :redaction => redaction.id }, :headers => auth_header - do_redact_relation(relation_v3, create(:redaction), auth_header) - assert_response :success, "should be OK to redact old version as moderator." + assert_response :forbidden, "should need to be moderator to redact." + assert_nil old_relation.reload.redaction + end - # re-auth as non-moderator - auth_header = bearer_authorization_header + def test_redact_relation_by_moderator_without_write_redactions_scope + relation = create(:relation, :with_history, :version => 2) + old_relation = relation.old_relations.find_by(:version => 1) + redaction = create(:redaction) + auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[read_prefs write_api]) - # check can't see the redacted data - get api_old_relation_path(relation_v3.relation_id, relation_v3.version), :headers => auth_header - assert_response :forbidden, "Redacted relation shouldn't be visible via the version API." + post relation_version_redact_path(*old_relation.id), :params => { :redaction => redaction.id }, :headers => auth_header - # and when accessed via history - get api_relation_history_path(relation), :headers => auth_header - 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." + assert_response :forbidden, "should need to have write_redactions scope to redact." + assert_nil old_relation.reload.redaction + end + + def test_redact_relation_by_moderator_with_write_redactions_scope + relation = create(:relation, :with_history, :version => 2) + old_relation = relation.old_relations.find_by(:version => 1) + redaction = create(:redaction) + auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[write_redactions]) + + post relation_version_redact_path(*old_relation.id), :params => { :redaction => redaction.id }, :headers => auth_header + + assert_response :success, "should be OK to redact old version as moderator with write_redactions scope." + assert_equal redaction, old_relation.reload.redaction end ## @@ -199,11 +266,14 @@ module Api # 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)) + old_relation = relation.old_relations.find_by(:version => 1) + redaction = create(:redaction) + old_relation.redact!(redaction) + + post relation_version_redact_path(*old_relation.id) - post relation_version_redact_path(relation_v1.relation_id, relation_v1.version) assert_response :unauthorized, "should need to be authenticated to unredact." + assert_equal redaction, old_relation.reload.redaction end ## @@ -211,13 +281,15 @@ module Api # 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)) - + old_relation = relation.old_relations.find_by(:version => 1) + redaction = create(:redaction) + old_relation.redact!(redaction) auth_header = bearer_authorization_header - post relation_version_redact_path(relation_v1.relation_id, relation_v1.version), :headers => auth_header + post relation_version_redact_path(*old_relation.id), :headers => auth_header + assert_response :forbidden, "should need to be moderator to unredact." + assert_equal redaction, old_relation.reload.redaction end ## @@ -225,95 +297,14 @@ module Api # 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)) - + old_relation = relation.old_relations.find_by(:version => 1) + old_relation.redact!(create(:redaction)) auth_header = bearer_authorization_header create(:moderator_user) - post relation_version_redact_path(relation_v1.relation_id, relation_v1.version), :headers => auth_header - 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 api_old_relation_path(relation_v1.relation_id, relation_v1.version), :headers => auth_header - assert_response :success, "After unredaction, relation should not be gone for moderator." - - # and when accessed via history - get api_relation_history_path(relation), :headers => auth_header - 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." - - auth_header = bearer_authorization_header + post relation_version_redact_path(*old_relation.id), :headers => auth_header - # check normal user can now see the redacted data - get api_old_relation_path(relation_v1.relation_id, relation_v1.version), :headers => auth_header - assert_response :success, "After redaction, node should not be gone for normal user." - - # and when accessed via history - get api_relation_history_path(relation), :headers => auth_header - 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_redactable_relation(headers = {}) - relation = create(:relation, :with_history, :version => 4) - relation_v3 = relation.old_relations.find_by(:version => 3) - do_redact_relation(relation_v3, create(:redaction), headers) - end - - def do_redact_relation(relation, redaction, headers = {}) - get api_old_relation_path(relation.relation_id, relation.version) - assert_response :success, "should be able to get version #{relation.version} of relation #{relation.relation_id}." - - # now redact it - post relation_version_redact_path(relation.relation_id, relation.version), :params => { :redaction => redaction.id }, :headers => headers + assert_response :success, "should be OK to unredact old version as moderator." + assert_nil old_relation.reload.redaction end end end diff --git a/test/controllers/api/old_ways_controller_test.rb b/test/controllers/api/old_ways_controller_test.rb index cc9eaf312..6946d2ab1 100644 --- a/test/controllers/api/old_ways_controller_test.rb +++ b/test/controllers/api/old_ways_controller_test.rb @@ -7,66 +7,178 @@ module Api def test_routes assert_routing( { :path => "/api/0.6/way/1/history", :method => :get }, - { :controller => "api/old_ways", :action => "history", :id => "1" } + { :controller => "api/old_ways", :action => "index", :way_id => "1" } ) assert_routing( - { :path => "/api/0.6/way/1/2", :method => :get }, - { :controller => "api/old_ways", :action => "show", :id => "1", :version => "2" } + { :path => "/api/0.6/way/1/history.json", :method => :get }, + { :controller => "api/old_ways", :action => "index", :way_id => "1", :format => "json" } ) assert_routing( - { :path => "/api/0.6/way/1/history.json", :method => :get }, - { :controller => "api/old_ways", :action => "history", :id => "1", :format => "json" } + { :path => "/api/0.6/way/1/2", :method => :get }, + { :controller => "api/old_ways", :action => "show", :way_id => "1", :version => "2" } ) assert_routing( { :path => "/api/0.6/way/1/2.json", :method => :get }, - { :controller => "api/old_ways", :action => "show", :id => "1", :version => "2", :format => "json" } + { :controller => "api/old_ways", :action => "show", :way_id => "1", :version => "2", :format => "json" } ) assert_routing( { :path => "/api/0.6/way/1/2/redact", :method => :post }, - { :controller => "api/old_ways", :action => "redact", :id => "1", :version => "2" } + { :controller => "api/old_ways", :action => "redact", :way_id => "1", :version => "2" } ) end - # ------------------------------------- - # Test reading old ways. - # ------------------------------------- + ## + # check that a visible way is returned properly + def test_index + way = create(:way, :with_history, :version => 2) + + get api_way_versions_path(way) - def test_history_visible - # check that a visible way is returned properly - get api_way_history_path(create(:way, :with_history)) assert_response :success + assert_dom "osm:root", 1 do + assert_dom "> way", 2 do |dom_ways| + assert_dom dom_ways[0], "> @id", way.id.to_s + assert_dom dom_ways[0], "> @version", "1" + + assert_dom dom_ways[1], "> @id", way.id.to_s + assert_dom dom_ways[1], "> @version", "2" + end + end end - def test_history_invisible - # check that an invisible way's history is returned properly - get api_way_history_path(create(:way, :with_history, :deleted)) + ## + # check that an invisible way's history is returned properly + def test_index_invisible + get api_way_versions_path(create(:way, :with_history, :deleted)) assert_response :success end - def test_history_invalid - # check chat a non-existent way is not returned - get api_way_history_path(0) + ## + # check chat a non-existent way is not returned + def test_index_invalid + get api_way_versions_path(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) + # test that redacted ways aren't visible in the history + def test_index_redacted_unauthorised + way = create(:way, :with_history, :version => 2) + way.old_ways.find_by(:version => 1).redact!(create(:redaction)) + + get api_way_versions_path(way) - 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) + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm way[id='#{way.id}'][version='1']", 0, + "redacted way #{way.id} version 1 shouldn't be present in the history." - check_current_version(way.id) - check_current_version(used_way.id) - check_current_version(way_with_versions.id) + get api_way_versions_path(way, :show_redactions => "true") + + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm way[id='#{way.id}'][version='1']", 0, + "redacted way #{way.id} version 1 shouldn't be present in the history when passing flag." + end + + def test_index_redacted_normal_user + way = create(:way, :with_history, :version => 2) + way.old_ways.find_by(:version => 1).redact!(create(:redaction)) + + get api_way_versions_path(way), :headers => bearer_authorization_header + + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm way[id='#{way.id}'][version='1']", 0, + "redacted node #{way.id} version 1 shouldn't be present in the history, even when logged in." + + get api_way_versions_path(way, :show_redactions => "true"), :headers => bearer_authorization_header + + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm way[id='#{way.id}'][version='1']", 0, + "redacted node #{way.id} version 1 shouldn't be present in the history, even when logged in and passing flag." + end + + def test_index_redacted_moderator + way = create(:way, :with_history, :version => 2) + way.old_ways.find_by(:version => 1).redact!(create(:redaction)) + auth_header = bearer_authorization_header create(:moderator_user) + + get api_way_versions_path(way), :headers => auth_header + + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm way[id='#{way.id}'][version='1']", 0, + "way #{way.id} version 1 should not be present in the history for moderators when not passing flag." + + get api_way_versions_path(way, :show_redactions => "true"), :headers => auth_header + + assert_response :success, "Redaction shouldn't have stopped history working." + assert_dom "osm way[id='#{way.id}'][version='1']", 1, + "way #{way.id} version 1 should still be present in the history for moderators when passing flag." + end + + def test_show + way = create(:way, :with_history, :version => 2) + + get api_way_version_path(way, 1) + + assert_response :success + assert_dom "osm:root", 1 do + assert_dom "> way", 1 do + assert_dom "> @id", way.id.to_s + assert_dom "> @version", "1" + end + end + + get api_way_version_path(way, 2) + + assert_response :success + assert_dom "osm:root", 1 do + assert_dom "> way", 1 do + assert_dom "> @id", way.id.to_s + assert_dom "> @version", "2" + end + end + end + + ## + # test that redacted ways aren't visible, regardless of + # authorisation except as moderator... + def test_show_redacted_unauthorised + way = create(:way, :with_history, :version => 2) + way.old_ways.find_by(:version => 1).redact!(create(:redaction)) + + get api_way_version_path(way, 1) + + assert_response :forbidden, "Redacted way shouldn't be visible via the version API." + + get api_way_version_path(way, 1, :show_redactions => "true") + + assert_response :forbidden, "Redacted way shouldn't be visible via the version API when passing flag." + end + + def test_show_redacted_normal_user + way = create(:way, :with_history, :version => 2) + way.old_ways.find_by(:version => 1).redact!(create(:redaction)) + + get api_way_version_path(way, 1), :headers => bearer_authorization_header + + assert_response :forbidden, "Redacted way shouldn't be visible via the version API, even when logged in." + + get api_way_version_path(way, 1, :show_redactions => "true"), :headers => bearer_authorization_header + + assert_response :forbidden, "Redacted way shouldn't be visible via the version API, even when logged in and passing flag." + end + + def test_show_redacted_moderator + way = create(:way, :with_history, :version => 2) + way.old_ways.find_by(:version => 1).redact!(create(:redaction)) + auth_header = bearer_authorization_header create(:moderator_user) + + get api_way_version_path(way, 1), :headers => auth_header + + assert_response :forbidden, "Redacted node should be gone for moderator, when flag not passed." + + get api_way_version_path(way, 1, :show_redactions => "true"), :headers => auth_header + + assert_response :success, "Redacted node should not be gone for moderator, when flag passed." end ## @@ -83,153 +195,81 @@ module Api 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 - auth_header = bearer_authorization_header - way = create(:way, :with_history, :version => 4) - way_v3 = way.old_ways.find_by(:version => 3) - - do_redact_way(way_v3, create(:redaction), auth_header) - 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 + way = create(:way, :with_history, :version => 2) + old_way = way.old_ways.find_by(:version => 2) + redaction = create(:redaction) auth_header = bearer_authorization_header create(:moderator_user) - way = create(:way, :with_history, :version => 4) - way_latest = way.old_ways.last - do_redact_way(way_latest, create(:redaction), auth_header) - assert_response :bad_request, "shouldn't be OK to redact current version as moderator." - end + post way_version_redact_path(*old_way.id), :params => { :redaction => redaction.id }, :headers => auth_header - def test_redact_way_by_regular_without_write_redactions_scope - auth_header = bearer_authorization_header(create(:user), :scopes => %w[read_prefs write_api]) - do_redact_redactable_way(auth_header) - assert_response :forbidden, "should need to be moderator to redact." + assert_response :bad_request, "shouldn't be OK to redact current version as moderator." + assert_nil old_way.reload.redaction end - def test_redact_way_by_regular_with_write_redactions_scope - auth_header = bearer_authorization_header(create(:user), :scopes => %w[write_redactions]) - do_redact_redactable_way(auth_header) - assert_response :forbidden, "should need to be moderator to redact." - 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 => 2) + old_way = way.old_ways.find_by(:version => 1) + redaction = create(:redaction) - def test_redact_way_by_moderator_without_write_redactions_scope - auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[read_prefs write_api]) - do_redact_redactable_way(auth_header) - assert_response :forbidden, "should need to have write_redactions scope to redact." - end + post way_version_redact_path(*old_way.id), :params => { :redaction => redaction.id } - def test_redact_way_by_moderator_with_write_redactions_scope - auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[write_redactions]) - do_redact_redactable_way(auth_header) - assert_response :success, "should be OK to redact old version as moderator with write_redactions scope." + assert_response :unauthorized, "should need to be authenticated to redact." + assert_nil old_way.reload.redaction end - ## - # test that redacted ways aren't visible, regardless of - # authorisation except as moderator... - def test_version_redacted + def test_redact_way_by_regular_without_write_redactions_scope way = create(:way, :with_history, :version => 2) - way_v1 = way.old_ways.find_by(:version => 1) - way_v1.redact!(create(:redaction)) + old_way = way.old_ways.find_by(:version => 1) + redaction = create(:redaction) + auth_header = bearer_authorization_header(create(:user), :scopes => %w[read_prefs write_api]) - get api_old_way_path(way_v1.way_id, way_v1.version) - assert_response :forbidden, "Redacted way shouldn't be visible via the version API." + post way_version_redact_path(*old_way.id), :params => { :redaction => redaction.id }, :headers => auth_header - # not even to a logged-in user - auth_header = bearer_authorization_header - get api_old_way_path(way_v1.way_id, way_v1.version), :headers => auth_header - assert_response :forbidden, "Redacted way shouldn't be visible via the version API, even when logged in." + assert_response :forbidden, "should need to be moderator to redact." + assert_nil old_way.reload.redaction end - ## - # test that redacted ways aren't visible in the history - def test_history_redacted + def test_redact_way_by_regular_with_write_redactions_scope way = create(:way, :with_history, :version => 2) - way_v1 = way.old_ways.find_by(:version => 1) - way_v1.redact!(create(:redaction)) + old_way = way.old_ways.find_by(:version => 1) + redaction = create(:redaction) + auth_header = bearer_authorization_header(create(:user), :scopes => %w[write_redactions]) - get api_way_history_path(way) - 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." + post way_version_redact_path(*old_way.id), :params => { :redaction => redaction.id }, :headers => auth_header - # not even to a logged-in user - auth_header = bearer_authorization_header - get api_way_history_path(way), :headers => auth_header - 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." + assert_response :forbidden, "should need to be moderator to redact." + assert_nil old_way.reload.redaction 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) - auth_header = bearer_authorization_header create(:moderator_user) - - do_redact_way(way_v3, create(:redaction), auth_header) - assert_response :success, "should be OK to redact old version as moderator." + def test_redact_way_by_moderator_without_write_redactions_scope + way = create(:way, :with_history, :version => 2) + old_way = way.old_ways.find_by(:version => 1) + redaction = create(:redaction) + auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[read_prefs write_api]) - # check moderator can still see the redacted data, when passing - # the appropriate flag - get api_old_way_path(way_v3.way_id, way_v3.version), :headers => auth_header - assert_response :forbidden, "After redaction, node should be gone for moderator, when flag not passed." - get api_old_way_path(way_v3.way_id, way_v3.version, :show_redactions => "true"), :headers => auth_header - assert_response :success, "After redaction, node should not be gone for moderator, when flag passed." + post way_version_redact_path(*old_way.id), :params => { :redaction => redaction.id }, :headers => auth_header - # and when accessed via history - get api_way_history_path(way), :headers => auth_header - 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 api_way_history_path(way, :show_redactions => "true"), :headers => auth_header - 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." + assert_response :forbidden, "should need to have write_redactions scope to redact." + assert_nil old_way.reload.redaction 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) - auth_header = bearer_authorization_header create(:moderator_user) - - do_redact_way(way_v3, create(:redaction), auth_header) - assert_response :success, "should be OK to redact old version as moderator." - - # re-auth as non-moderator - auth_header = bearer_authorization_header + def test_redact_way_by_moderator_with_write_redactions_scope + way = create(:way, :with_history, :version => 2) + old_way = way.old_ways.find_by(:version => 1) + redaction = create(:redaction) + auth_header = bearer_authorization_header(create(:moderator_user), :scopes => %w[write_redactions]) - # check can't see the redacted data - get api_old_way_path(way_v3.way_id, way_v3.version), :headers => auth_header - assert_response :forbidden, "Redacted node shouldn't be visible via the version API." + post way_version_redact_path(*old_way.id), :params => { :redaction => redaction.id }, :headers => auth_header - # and when accessed via history - get api_way_history_path(way), :headers => auth_header - 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." + assert_response :success, "should be OK to redact old version as moderator with write_redactions scope." + assert_equal redaction, old_way.reload.redaction end ## @@ -237,11 +277,14 @@ module Api # 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)) + old_way = way.old_ways.find_by(:version => 1) + redaction = create(:redaction) + old_way.redact!(redaction) + + post way_version_redact_path(*old_way.id) - post way_version_redact_path(way_v1.way_id, way_v1.version) assert_response :unauthorized, "should need to be authenticated to unredact." + assert_equal redaction, old_way.reload.redaction end ## @@ -249,81 +292,39 @@ module Api # 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)) - + old_way = way.old_ways.find_by(:version => 1) + redaction = create(:redaction) + old_way.redact!(redaction) auth_header = bearer_authorization_header - post way_version_redact_path(way_v1.way_id, way_v1.version), :headers => auth_header + post way_version_redact_path(*old_way.id), :headers => auth_header + assert_response :forbidden, "should need to be moderator to unredact." + assert_equal redaction, old_way.reload.redaction 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)) + old_way = way.old_ways.find_by(:version => 1) + old_way.redact!(create(:redaction)) + auth_header = bearer_authorization_header create(:moderator_user) - auth_header = bearer_authorization_header moderator_user + post way_version_redact_path(*old_way.id), :headers => auth_header - post way_version_redact_path(way_v1.way_id, way_v1.version), :headers => auth_header 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 api_old_way_path(way_v1.way_id, way_v1.version), :headers => auth_header - assert_response :success, "After unredaction, node should not be gone for moderator." - - # and when accessed via history - get api_way_history_path(way), :headers => auth_header - 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." - - auth_header = bearer_authorization_header - - # check normal user can now see the unredacted data - get api_old_way_path(way_v1.way_id, way_v1.version), :headers => auth_header - assert_response :success, "After redaction, node should not be gone for moderator, when flag passed." - - # and when accessed via history - get api_way_history_path(way), :headers => auth_header - 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." + assert_nil old_way.reload.redaction 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 api_way_path(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 api_old_way_path(way_id, 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 api_way_history_path(way_id) + get api_way_versions_path(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" @@ -332,7 +333,7 @@ module Api history_way = Way.from_xml_node(way_doc) assert_not_nil history_way, "parsing way #{way_id} version failed" - get api_old_way_path(way_id, history_way.version) + get api_way_version_path(way_id, 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}" @@ -340,25 +341,5 @@ module Api assert_ways_are_equal history_way, version_way end end - - def do_redact_redactable_way(headers = {}) - way = create(:way, :with_history, :version => 4) - way_v3 = way.old_ways.find_by(:version => 3) - do_redact_way(way_v3, create(:redaction), headers) - end - - def do_redact_way(way, redaction, headers = {}) - get api_old_way_path(way.way_id, way.version) - assert_response :success, "should be able to get version #{way.version} of way #{way.way_id}." - - # now redact it - post way_version_redact_path(way.way_id, way.version), :params => { :redaction => redaction.id }, :headers => headers - 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 diff --git a/test/controllers/api/relations_controller_test.rb b/test/controllers/api/relations_controller_test.rb index 5e480b69c..9e2972495 100644 --- a/test/controllers/api/relations_controller_test.rb +++ b/test/controllers/api/relations_controller_test.rb @@ -782,7 +782,7 @@ module Api # check the ordering in the history tables: with_controller(OldRelationsController.new) do - get api_old_relation_path(relation_id, 2) + get api_relation_version_path(relation_id, 2) assert_response :success, "can't read back version 2 of the relation #{relation_id}" check_ordering(doc, @response.body) end @@ -862,7 +862,7 @@ module Api # check the ordering in the history tables: with_controller(OldRelationsController.new) do - get api_old_relation_path(relation_id, 1) + get api_relation_version_path(relation_id, 1) assert_response :success, "can't read back version 1 of the relation: #{@response.body}" check_ordering(doc, @response.body) end @@ -1084,7 +1084,7 @@ module Api get api_relation_path(id) else with_controller(OldRelationsController.new) do - get api_old_relation_path(id, ver) + get api_relation_version_path(id, ver) end end assert_response :success diff --git a/test/controllers/old_nodes_controller_test.rb b/test/controllers/old_nodes_controller_test.rb index 8e3a14ab6..aba69b03a 100644 --- a/test/controllers/old_nodes_controller_test.rb +++ b/test/controllers/old_nodes_controller_test.rb @@ -114,7 +114,7 @@ class OldNodesControllerTest < ActionDispatch::IntegrationTest assert_select "h4", /^Version/ do assert_select "a[href='#{old_node_path node, 1}']", :count => 0 end - assert_select ".secondary-actions a[href='#{api_old_node_path node, 1}']", :count => 1 + assert_select ".secondary-actions a[href='#{api_node_version_path node, 1}']", :count => 1 assert_select ".secondary-actions a[href='#{node_path node}']", :count => 1 assert_select ".secondary-actions a[href='#{old_node_path node, 1, :params => { :show_redactions => true }}']", :count => 0 assert_select ".secondary-actions a[href='#{node_history_path node}']", :count => 1 @@ -129,7 +129,7 @@ class OldNodesControllerTest < ActionDispatch::IntegrationTest assert_select "h4", /^Version/ do assert_select "a[href='#{old_node_path node, 1}']", :count => 0 end - assert_select ".secondary-actions a[href='#{api_old_node_path node, 1}']", :count => 1 + assert_select ".secondary-actions a[href='#{api_node_version_path node, 1}']", :count => 1 assert_select ".secondary-actions a[href='#{node_path node}']", :count => 1 assert_select ".secondary-actions a[href='#{node_history_path node}']", :count => 1 assert_select ".secondary-actions a[href='#{old_node_path node, 2}']", :count => 1 @@ -141,7 +141,7 @@ class OldNodesControllerTest < ActionDispatch::IntegrationTest assert_select "h4", /^Version/ do assert_select "a[href='#{old_node_path node, 2}']", :count => 0 end - assert_select ".secondary-actions a[href='#{api_old_node_path node, 2}']", :count => 1 + assert_select ".secondary-actions a[href='#{api_node_version_path node, 2}']", :count => 1 assert_select ".secondary-actions a[href='#{node_path node}']", :count => 1 assert_select ".secondary-actions a[href='#{node_history_path node}']", :count => 1 assert_select ".secondary-actions a[href='#{old_node_path node, 1}']", :count => 1 @@ -157,7 +157,7 @@ class OldNodesControllerTest < ActionDispatch::IntegrationTest assert_select ".secondary-actions a[href='#{node_path node}']", :count => 1 assert_select ".secondary-actions a[href='#{old_node_path node, 1, :params => { :show_redactions => true }}']", :count => 0 assert_select ".secondary-actions a[href='#{old_node_path node, 1}']", :count => 0 - assert_select ".secondary-actions a[href='#{api_old_node_path node, 1}']", :count => 0 + assert_select ".secondary-actions a[href='#{api_node_version_path node, 1}']", :count => 0 end test "show unrevealed redacted versions to regular users" do @@ -171,7 +171,7 @@ class OldNodesControllerTest < ActionDispatch::IntegrationTest assert_select ".secondary-actions a[href='#{node_path node}']", :count => 1 assert_select ".secondary-actions a[href='#{old_node_path node, 1, :params => { :show_redactions => true }}']", :count => 0 assert_select ".secondary-actions a[href='#{old_node_path node, 1}']", :count => 0 - assert_select ".secondary-actions a[href='#{api_old_node_path node, 1}']", :count => 0 + assert_select ".secondary-actions a[href='#{api_node_version_path node, 1}']", :count => 0 end test "show unrevealed redacted versions to moderators" do @@ -185,7 +185,7 @@ class OldNodesControllerTest < ActionDispatch::IntegrationTest assert_select ".secondary-actions a[href='#{node_path node}']", :count => 1 assert_select ".secondary-actions a[href='#{old_node_path node, 1, :params => { :show_redactions => true }}']", :count => 1 assert_select ".secondary-actions a[href='#{old_node_path node, 1}']", :count => 0 - assert_select ".secondary-actions a[href='#{api_old_node_path node, 1}']", :count => 0 + assert_select ".secondary-actions a[href='#{api_node_version_path node, 1}']", :count => 0 end test "don't reveal redacted versions to anonymous users" do diff --git a/test/controllers/old_relations_controller_test.rb b/test/controllers/old_relations_controller_test.rb index 3ede1842b..cd6737dca 100644 --- a/test/controllers/old_relations_controller_test.rb +++ b/test/controllers/old_relations_controller_test.rb @@ -64,7 +64,7 @@ class OldRelationsControllerTest < ActionDispatch::IntegrationTest assert_select "h4", /^Version/ do assert_select "a[href='#{old_relation_path relation, 1}']", :count => 0 end - assert_select ".secondary-actions a[href='#{api_old_relation_path relation, 1}']", :count => 1 + assert_select ".secondary-actions a[href='#{api_relation_version_path relation, 1}']", :count => 1 assert_select ".secondary-actions a[href='#{relation_path relation}']", :count => 1 assert_select ".secondary-actions a[href='#{old_relation_path relation, 1, :params => { :show_redactions => true }}']", :count => 0 assert_select ".secondary-actions a[href='#{relation_history_path relation}']", :count => 1 @@ -79,7 +79,7 @@ class OldRelationsControllerTest < ActionDispatch::IntegrationTest assert_select "h4", /^Version/ do assert_select "a[href='#{old_relation_path relation, 1}']", :count => 0 end - assert_select ".secondary-actions a[href='#{api_old_relation_path relation, 1}']", :count => 1 + assert_select ".secondary-actions a[href='#{api_relation_version_path relation, 1}']", :count => 1 assert_select ".secondary-actions a[href='#{relation_path relation}']", :count => 1 assert_select ".secondary-actions a[href='#{relation_history_path relation}']", :count => 1 assert_select ".secondary-actions a[href='#{old_relation_path relation, 2}']", :count => 1 @@ -91,7 +91,7 @@ class OldRelationsControllerTest < ActionDispatch::IntegrationTest assert_select "h4", /^Version/ do assert_select "a[href='#{old_relation_path relation, 2}']", :count => 0 end - assert_select ".secondary-actions a[href='#{api_old_relation_path relation, 2}']", :count => 1 + assert_select ".secondary-actions a[href='#{api_relation_version_path relation, 2}']", :count => 1 assert_select ".secondary-actions a[href='#{relation_path relation}']", :count => 1 assert_select ".secondary-actions a[href='#{relation_history_path relation}']", :count => 1 assert_select ".secondary-actions a[href='#{old_relation_path relation, 1}']", :count => 1 @@ -116,7 +116,7 @@ class OldRelationsControllerTest < ActionDispatch::IntegrationTest assert_select ".secondary-actions a[href='#{relation_path relation}']", :count => 1 assert_select ".secondary-actions a[href='#{old_relation_path relation, 1, :params => { :show_redactions => true }}']", :count => 0 assert_select ".secondary-actions a[href='#{old_relation_path relation, 1}']", :count => 0 - assert_select ".secondary-actions a[href='#{api_old_relation_path relation, 1}']", :count => 0 + assert_select ".secondary-actions a[href='#{api_relation_version_path relation, 1}']", :count => 0 end test "show unrevealed redacted versions to regular users" do @@ -130,7 +130,7 @@ class OldRelationsControllerTest < ActionDispatch::IntegrationTest assert_select ".secondary-actions a[href='#{relation_path relation}']", :count => 1 assert_select ".secondary-actions a[href='#{old_relation_path relation, 1, :params => { :show_redactions => true }}']", :count => 0 assert_select ".secondary-actions a[href='#{old_relation_path relation, 1}']", :count => 0 - assert_select ".secondary-actions a[href='#{api_old_relation_path relation, 1}']", :count => 0 + assert_select ".secondary-actions a[href='#{api_relation_version_path relation, 1}']", :count => 0 end test "show unrevealed redacted versions to moderators" do @@ -144,7 +144,7 @@ class OldRelationsControllerTest < ActionDispatch::IntegrationTest assert_select ".secondary-actions a[href='#{relation_path relation}']", :count => 1 assert_select ".secondary-actions a[href='#{old_relation_path relation, 1, :params => { :show_redactions => true }}']", :count => 1 assert_select ".secondary-actions a[href='#{old_relation_path relation, 1}']", :count => 0 - assert_select ".secondary-actions a[href='#{api_old_relation_path relation, 1}']", :count => 0 + assert_select ".secondary-actions a[href='#{api_relation_version_path relation, 1}']", :count => 0 end test "don't reveal redacted versions to anonymous users" do diff --git a/test/controllers/old_ways_controller_test.rb b/test/controllers/old_ways_controller_test.rb index c7b383202..3ac6eaf2f 100644 --- a/test/controllers/old_ways_controller_test.rb +++ b/test/controllers/old_ways_controller_test.rb @@ -64,7 +64,7 @@ class OldWaysControllerTest < ActionDispatch::IntegrationTest assert_select "h4", /^Version/ do assert_select "a[href='#{old_way_path way, 1}']", :count => 0 end - assert_select ".secondary-actions a[href='#{api_old_way_path way, 1}']", :count => 1 + assert_select ".secondary-actions a[href='#{api_way_version_path way, 1}']", :count => 1 assert_select ".secondary-actions a[href='#{way_path way}']", :count => 1 assert_select ".secondary-actions a[href='#{old_way_path way, 1, :params => { :show_redactions => true }}']", :count => 0 assert_select ".secondary-actions a[href='#{way_history_path way}']", :count => 1 @@ -79,7 +79,7 @@ class OldWaysControllerTest < ActionDispatch::IntegrationTest assert_select "h4", /^Version/ do assert_select "a[href='#{old_way_path way, 1}']", :count => 0 end - assert_select ".secondary-actions a[href='#{api_old_way_path way, 1}']", :count => 1 + assert_select ".secondary-actions a[href='#{api_way_version_path way, 1}']", :count => 1 assert_select ".secondary-actions a[href='#{way_path way}']", :count => 1 assert_select ".secondary-actions a[href='#{way_history_path way}']", :count => 1 assert_select ".secondary-actions a[href='#{old_way_path way, 2}']", :count => 1 @@ -91,7 +91,7 @@ class OldWaysControllerTest < ActionDispatch::IntegrationTest assert_select "h4", /^Version/ do assert_select "a[href='#{old_way_path way, 2}']", :count => 0 end - assert_select ".secondary-actions a[href='#{api_old_way_path way, 2}']", :count => 1 + assert_select ".secondary-actions a[href='#{api_way_version_path way, 2}']", :count => 1 assert_select ".secondary-actions a[href='#{way_path way}']", :count => 1 assert_select ".secondary-actions a[href='#{way_history_path way}']", :count => 1 assert_select ".secondary-actions a[href='#{old_way_path way, 1}']", :count => 1 @@ -121,7 +121,7 @@ class OldWaysControllerTest < ActionDispatch::IntegrationTest assert_select ".secondary-actions a[href='#{way_path way}']", :count => 1 assert_select ".secondary-actions a[href='#{old_way_path way, 1, :params => { :show_redactions => true }}']", :count => 0 assert_select ".secondary-actions a[href='#{old_way_path way, 1}']", :count => 0 - assert_select ".secondary-actions a[href='#{api_old_way_path way, 1}']", :count => 0 + assert_select ".secondary-actions a[href='#{api_way_version_path way, 1}']", :count => 0 end test "show unrevealed redacted versions to regular users" do @@ -135,7 +135,7 @@ class OldWaysControllerTest < ActionDispatch::IntegrationTest assert_select ".secondary-actions a[href='#{way_path way}']", :count => 1 assert_select ".secondary-actions a[href='#{old_way_path way, 1, :params => { :show_redactions => true }}']", :count => 0 assert_select ".secondary-actions a[href='#{old_way_path way, 1}']", :count => 0 - assert_select ".secondary-actions a[href='#{api_old_way_path way, 1}']", :count => 0 + assert_select ".secondary-actions a[href='#{api_way_version_path way, 1}']", :count => 0 end test "show unrevealed redacted versions to moderators" do @@ -149,7 +149,7 @@ class OldWaysControllerTest < ActionDispatch::IntegrationTest assert_select ".secondary-actions a[href='#{way_path way}']", :count => 1 assert_select ".secondary-actions a[href='#{old_way_path way, 1, :params => { :show_redactions => true }}']", :count => 1 assert_select ".secondary-actions a[href='#{old_way_path way, 1}']", :count => 0 - assert_select ".secondary-actions a[href='#{api_old_way_path way, 1}']", :count => 0 + assert_select ".secondary-actions a[href='#{api_way_version_path way, 1}']", :count => 0 end test "don't reveal redacted versions to anonymous users" do diff --git a/test/integration/node_versions_test.rb b/test/integration/node_versions_test.rb new file mode 100644 index 000000000..71251aae9 --- /dev/null +++ b/test/integration/node_versions_test.rb @@ -0,0 +1,194 @@ +require "test_helper" + +class NodeVersionsTest < ActionDispatch::IntegrationTest + ## + # 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. + def test_version + private_user = create(:user, :data_public => false) + private_node = create(:node, :with_history, :version => 4, :lat => 0, :lon => 0, :changeset => create(:changeset, :user => private_user)) + user = create(:user) + node = create(:node, :with_history, :version => 4, :lat => 0, :lon => 0, :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 + auth_header = bearer_authorization_header private_user + + # setup a simple XML node + xml_doc = xml_for_node(private_node) + xml_node = xml_doc.find("//osm/node").first + node_id = 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 - 0.5).to_s + xml_node["lon"] = precision(rand - 0.5).to_s + with_controller(NodesController.new) do + put api_node_path(node_id), :params => xml_doc.to_s, :headers => auth_header + 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 api_node_path(node_id), :params => xml_doc.to_s, :headers => auth_header + assert_response :forbidden, + "should have rejected node #{node_id} (#{@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 + auth_header = bearer_authorization_header user + + # setup a simple XML node + + xml_doc = xml_for_node(node) + xml_node = xml_doc.find("//osm/node").first + node_id = 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 - 0.5).to_s + xml_node["lon"] = precision(rand - 0.5).to_s + with_controller(NodesController.new) do + put api_node_path(node_id), :params => xml_doc.to_s, :headers => auth_header + 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 api_node_path(node_id), :params => xml_doc.to_s, :headers => auth_header + assert_response :success, + "couldn't update node #{node_id} (#{@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 api_node_version_path(node_id, key.to_i) + + assert_response :success, + "couldn't get version #{key.to_i} of node #{node_id}" + + 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 + + ## + # 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 + + private + + def check_current_version(node_id) + # get the current version of the node + current_node = with_controller(NodesController.new) do + get api_node_path(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 api_node_version_path(node_id, 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 { 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 diff --git a/test/integration/user_blocks_test.rb b/test/integration/user_blocks_test.rb index 5147733e3..4e7315ede 100644 --- a/test/integration/user_blocks_test.rb +++ b/test/integration/user_blocks_test.rb @@ -38,6 +38,8 @@ class UserBlocksTest < ActionDispatch::IntegrationTest # revoke the ban get "/login" + assert_response :redirect + follow_redirect! assert_response :success post "/login", :params => { "username" => moderator.email, "password" => "test", :referer => "/user_blocks/#{block.id}/edit" } assert_response :redirect diff --git a/test/integration/way_versions_test.rb b/test/integration/way_versions_test.rb new file mode 100644 index 000000000..866e48573 --- /dev/null +++ b/test/integration/way_versions_test.rb @@ -0,0 +1,52 @@ +require "test_helper" + +class WayVersionsTest < ActionDispatch::IntegrationTest + ## + # 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 + + 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 api_way_path(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 api_way_version_path(way_id, 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 + + 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 diff --git a/test/system/directions_test.rb b/test/system/directions_test.rb index 08f5cb8e3..91a033c4d 100644 --- a/test/system/directions_test.rb +++ b/test/system/directions_test.rb @@ -37,7 +37,7 @@ class DirectionsSystemTest < ApplicationSystemTestCase stub_routing <<~CALLBACK const distance = points[0].distanceTo(points[1]); const time = distance * 30; - callback(false, { + return Promise.resolve({ line: points, steps: [ [points[0], 8, "1. #{start_instruction}", distance, points], @@ -53,10 +53,8 @@ class DirectionsSystemTest < ApplicationSystemTestCase execute_script <<~SCRIPT $(() => { for (const engine of OSM.Directions.engines) { - engine.getRoute = (points, callback) => { - setTimeout(() => { + engine.getRoute = (points, signal) => { #{callback_code} - }); }; } }); diff --git a/yarn.lock b/yarn.lock index 902fdb906..9e73a5eb4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -104,9 +104,9 @@ integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== "@stylistic/eslint-plugin-js@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin-js/-/eslint-plugin-js-3.0.1.tgz#15638c55a9adab2c110243a9f0d812264b067aab" - integrity sha512-hjp6BKXSUdlY4l20pDb0EjIB5PtQDGihk2EUKCjJ5gaRVfcmMMkaIyVd/yK3oH7OLxWWBxJ8qSSo+zEdkmpnYA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin-js/-/eslint-plugin-js-3.1.0.tgz#b36292b09bd810ea1b34e0720512f137335ef745" + integrity sha512-lQktsOiCr8S6StG29C5fzXYxLOD6ID1rp4j6TRS+E/qY1xd59Fm7dy5qm9UauJIEoSTlYx6yGsCHYh5UkgXPyg== dependencies: eslint-visitor-keys "^4.2.0" espree "^10.3.0" @@ -256,9 +256,9 @@ eslint-visitor-keys@^4.2.0: integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== eslint@^9.0.0: - version "9.20.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.20.0.tgz#6244c46c1640cd5e577a31ebc460fca87838c0b7" - integrity sha512-aL4F8167Hg4IvsW89ejnpTwx+B/UQRzJPGgbIOl+4XqffWsahVVsLEWoZvnrVuwpWmnRd7XeXmQI1zlKcFDteA== + version "9.20.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.20.1.tgz#923924c078f5226832449bac86662dd7e53c91d6" + integrity sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.12.1"