]> git.openstreetmap.org Git - rails.git/commitdiff
Merge remote-tracking branch 'upstream/pull/5632'
authorTom Hughes <tom@compton.nu>
Wed, 12 Feb 2025 18:21:02 +0000 (18:21 +0000)
committerTom Hughes <tom@compton.nu>
Wed, 12 Feb 2025 18:21:02 +0000 (18:21 +0000)
71 files changed:
CONTRIBUTING.md
DOCKER.md
Gemfile.lock
app/abilities/api_ability.rb
app/assets/javascripts/application.js
app/assets/javascripts/edit/id.js.erb
app/assets/javascripts/index/contextmenu.js
app/assets/javascripts/index/directions.js
app/assets/javascripts/index/directions/fossgis_osrm.js
app/assets/javascripts/index/directions/fossgis_valhalla.js
app/assets/javascripts/index/directions/graphhopper.js
app/assets/javascripts/index/layers/data.js
app/assets/javascripts/index/query.js
app/assets/javascripts/leaflet.locate.js
app/assets/javascripts/osm.js.erb
app/controllers/accounts/terms_controller.rb
app/controllers/api/nodes/relations_controller.rb
app/controllers/api/nodes/ways_controller.rb
app/controllers/api/notes_controller.rb
app/controllers/api/old_elements_controller.rb
app/controllers/api/old_nodes_controller.rb
app/controllers/api/old_relations_controller.rb
app/controllers/api/old_ways_controller.rb
app/controllers/api/relations/relations_controller.rb
app/controllers/api/users_controller.rb
app/controllers/api/ways/relations_controller.rb
app/controllers/api_controller.rb
app/controllers/application_controller.rb
app/controllers/concerns/session_methods.rb
app/controllers/sessions_controller.rb
app/controllers/site_controller.rb
app/models/note.rb
app/views/api/old_nodes/index.json.jbuilder [moved from app/views/api/old_nodes/history.json.jbuilder with 100% similarity]
app/views/api/old_nodes/index.xml.builder [moved from app/views/api/old_nodes/history.xml.builder with 100% similarity]
app/views/api/old_relations/index.json.jbuilder [moved from app/views/api/old_relations/history.json.jbuilder with 100% similarity]
app/views/api/old_relations/index.xml.builder [moved from app/views/api/old_relations/history.xml.builder with 100% similarity]
app/views/api/old_ways/index.json.jbuilder [moved from app/views/api/old_ways/history.json.jbuilder with 100% similarity]
app/views/api/old_ways/index.xml.builder [moved from app/views/api/old_ways/history.xml.builder with 100% similarity]
app/views/layouts/_offline_flash.erb [new file with mode: 0644]
app/views/old_elements/_actions.html.erb
app/views/old_elements/index.html.erb
app/views/site/_not_public_flash.erb [new file with mode: 0644]
app/views/site/edit.html.erb
config/eslint.js
config/locales/de.yml
config/locales/el.yml
config/locales/en.yml
config/locales/gd.yml
config/locales/gl.yml
config/locales/it.yml
config/locales/lb.yml
config/locales/ru.yml
config/locales/skr-arab.yml
config/locales/sr.yml
config/routes.rb
config/settings.yml
db/migrate/20250206202905_add_text_index_to_notes.rb [new file with mode: 0644]
db/structure.sql
lib/tasks/eslint.rake
test/controllers/api/old_nodes_controller_test.rb
test/controllers/api/old_relations_controller_test.rb
test/controllers/api/old_ways_controller_test.rb
test/controllers/api/relations_controller_test.rb
test/controllers/old_nodes_controller_test.rb
test/controllers/old_relations_controller_test.rb
test/controllers/old_ways_controller_test.rb
test/integration/node_versions_test.rb [new file with mode: 0644]
test/integration/user_blocks_test.rb
test/integration/way_versions_test.rb [new file with mode: 0644]
test/system/directions_test.rb
yarn.lock

index aea8b30b908eb569223ea2d9b82207ee19577ba0..46fe63bb2b101595fe02b0ea2fb40e43f0553a40 100644 (file)
@@ -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.
index b93bf6d508346fff56c5c572b40f5ae241932b40..4cfc6d7361a269bcb565296c6573b0b992ecf4a6 100644 (file)
--- 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
 
index 0fd4dc5ea1662aa9055b3f31df547b9020149eb4..9475cb663e83f8b3cab7894c249e2fafdf9a78cc 100644 (file)
@@ -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)
index 55476ab53f72440f5871b8df3a6419c3fd83c82b..e774f6820df3f364c3d766f1c2bd25129f12a0dc 100644 (file)
@@ -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?
index 86a0a0789962abcc08c9de91aaf853d3991b8cbf..2e13f69671bec7253adbe5c98887a33c105d7725 100644 (file)
@@ -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");
index cb4ff18651331ecadf8cf799165e43a2b9cacfe6..7c907ed3a3496d2419179de705791c5f2182f740 100644 (file)
@@ -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"));
 });
index fea7e73145415ecf36bca270b53f19feab5683f3..d7e6b427da6ce585a03b8e0581ddfd793e9d9a24 100644 (file)
@@ -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() {
index 356dc6271d041878883417ded8025a607fad36c3..0ac66d9d640f3d970f2a867757e0d6ae59ffb3a6 100644 (file)
@@ -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("<div class=\"alert alert-danger\">" + I18n.t("javascripts.directions.errors.no_route") + "</div>");
-        }
-
-        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("<div class=\"alert alert-danger\">" + I18n.t("javascripts.directions.errors.no_route") + "</div>");
+      }
+    }).finally(function () {
+      controller = null;
     });
 
     function getDistText(dist) {
index bb968f2da3c5594175d9fb72a2dc5299d4f2d87b..cd1731247f70c650e3bd49c5341f5f589fd92f4a 100644 (file)
       creditline: "<a href=\"https://routing.openstreetmap.de/about.html\" target=\"_blank\">OSRM (FOSSGIS)</a>",
       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 = [];
 
         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]);
+          });
       }
     };
   }
index bbccccb13d1d35ac514c534b7d4600e84908fdf8..41ad6a9725fcb332b3ec63030f36fad0f3c60343 100644 (file)
@@ -87,8 +87,8 @@
       "<a href='https://gis-ops.com/global-open-valhalla-server-online/' target='_blank'>Valhalla (FOSSGIS)</a>",
       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 };
               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);
+          });
       }
     };
   }
index 19147587303b69b079fbc3792c526a64b6f0193a..729618f2d596441597482dea5f159f99f09357b4 100644 (file)
       creditline: "<a href=\"https://www.graphhopper.com/\" target=\"_blank\">GraphHopper</a>",
       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]);
+          });
       }
     };
   }
index dc522af418d809f6e16c90ccf5b91a67bf211494..e6a7cc66684be0323361d21758156a9ee816ffb8 100644 (file)
@@ -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();
index 5c8ee88760ceb549adb01ed81efdfb2797bae47d..54d5e080fb0f5675a968bcd720f9a0373ae5e23c 100644 (file)
@@ -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);
index d43a5e458fe4e1615f445509bb32bf9c230d8839..d199292083d93bbbded0586326f6f5e09a0056dc 100644 (file)
@@ -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]);
index 030c51a08e6901b0e1ce524f851e2c6430ce6466..062d537ffe8347195e8a70d588400ae725674a5a 100644 (file)
@@ -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;
index 45e0cc239f4dcb465e3c50755a1fc111996c70d6..13e9de890925c4704d93b71b4701fcf52f144c72 100644 (file)
@@ -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
 
index 0f0409e1927a735213acac2dd1f1841365d9b0c4..c6b33eab81242767bfd6eb6a0f9df849a4bfd623 100644 (file)
@@ -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|
index 2a71f1903956e001d893487c8d635c7c47152e66..cdbdc07d0becd36fdd0311b00d6fdb832efa8ccc 100644 (file)
@@ -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|
index eca0728b65700d90d7ea2f38126c9e06fe961d7c..bc4d2eaf2ceaa47076e57e4182fdcca583d65809 100644 (file)
@@ -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)
index 73e57c1f82355415baeef6a4ee044e3a741b942c..bdbc5e392e21dd00409b5f93dc3494e00f7de334 100644 (file)
@@ -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.
index c2831c48ba558e34b0b937f0a423d1818d571591..b63761af9cfb96353cbb4dfd56d39735ab46052e 100644 (file)
@@ -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
index 4679384dd3877084ad9378a8e17b6aa75e9c606d..c065c657d1a787a4eb8b14a075b226164dcfbc74 100644 (file)
@@ -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
index 0afa3059ccfce9a9ff1b350e7fbe1f5c3f41b82d..654100767f06e833c46f4ce3c400caa9f8cd3487 100644 (file)
@@ -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
index 1769e1396ace5bda28da782864c0bf0acec6f261..9b829252645aa26a9528270e1726dcd3f31f14da 100644 (file)
@@ -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|
index 462b4ba3c48f8ae0ea5f37af172e28c79d68fd3d..58b02a48941feab848f7466558d2f9d4d31a38e3 100644 (file)
@@ -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
index fcd8b40dd03ab714257c4bc93b22762069026cd2..4188dfe4eceb637d9107cd7a73ee12372424e098 100644 (file)
@@ -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|
index 23f35a40eeaf73662765d1d8cf137f2605f40f9c..5faa39165d089a249179b506ddaf9ee5dea6dedb 100644 (file)
@@ -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
index 176fd8c2e060213d8264c1ccd1dd8cddca281df6..25de71f20a1cdba2cc8b211ce316ad321f1604d7 100644 (file)
@@ -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
index 4bbfac34f6dff3305b576e343a6e119e95784249..d0fb0c419e726cc85e0443376ccd925bb2bb9863 100644 (file)
@@ -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
index abbaf5e921e45aedf89e60d7057023b9a0374143..19fe05f3082574961423f1f3965abcd53e386a43 100644 (file)
@@ -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]
index 9adbaa1953fd9fe774bf9bb90eb130da53aed90e..5110be0197ab21face71e30427ac5b29c04b7092 100644 (file)
@@ -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
 
index 807ee9ec8b99a7e021ac9ba8b46963c2f74cd2a8..b7215d6f7706084318ecefcd845d303771f14fc9 100644 (file)
 #
 # 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/layouts/_offline_flash.erb b/app/views/layouts/_offline_flash.erb
new file mode 100644 (file)
index 0000000..364788d
--- /dev/null
@@ -0,0 +1,26 @@
+<div class="d-flex flex-column gap-2">
+  <% if %w[database_offline api_offline].include? Settings.status %>
+    <p class="mb-0">
+      <%= t(".osm_offline") %>
+    </p>
+  <% elsif %w[database_readonly api_readonly].include? Settings.status %>
+    <p class="mb-0">
+      <%= t(".osm_read_only") %>
+    </p>
+  <% 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 %>
+      <p class="mb-0">
+        <%= t ".expected_restore_html", :time => friendly_date(expected_restore_time) %>
+      </p>
+    <% end %>
+  <% end %>
+
+  <% if Settings.status_announcement_url %>
+    <p class="mb-0">
+      <%= link_to t(".announcement"), Settings.status_announcement_url %>
+    </p>
+  <% end %>
+</div>
index 36a16abb4543ffb74c3a00276c5e34e9d877b932..b4d28123ce4f38be33d3e263d8a40efbce9fbd71 100644 (file)
@@ -2,7 +2,7 @@
   <%= link_to t("browse.view_details"), :controller => @type.pluralize, :action => :show %>
   <% if !@feature.redacted? %>
     &middot;
-    <%= 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? %>
     &middot;
     <% if !params[:show_redactions] %>
index d4ecbfa60480c618041bb7902bd601abd91ec644..eaf7787fa8d2c6f92e7399ee70e9ecb2d25d0a63 100644 (file)
@@ -5,7 +5,7 @@
 <%= render :partial => "browse/#{@type}", :collection => @feature.send(:"old_#{@type}s").reverse %>
 
 <div class='secondary-actions'>
-  <%= 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) %>
   &middot;
   <%= 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 (file)
index 0000000..bcd010f
--- /dev/null
@@ -0,0 +1,3 @@
+<p><%= t ".not_public" %></p>
+<p><%= t ".not_public_description_html", :user_page => (link_to t(".user_page_link"), edit_account_path(:anchor => "public")) %></p>
+<p><%= t ".anon_edits_html", :link => link_to(t(".anon_edits_link_text"), t(".anon_edits_link")) %></p>
index 1eb73382279b638866d08af61e302681e5cff858..6d463df7848dbd52138ebad1c12640d2671d61af 100644 (file)
@@ -1,17 +1,5 @@
 <% content_for :content do %>
-  <% if Settings.status == "database_offline" or Settings.status == "api_offline" %>
-    <div class="alert alert-warning text-center">
-        <p class="my-2"><%= t "layouts.osm_offline" %></p>
-    </div>
-  <% elsif Settings.status == "database_readonly" or Settings.status == "api_readonly" %>
-    <div class="alert alert-warning text-center">
-        <p class="my-2"><%= t "layouts.osm_read_only" %></p>
-    </div>
-  <% elsif !current_user.data_public? %>
-    <p><%= t ".not_public" %></p>
-    <p><%= t ".not_public_description_html", :user_page => (link_to t(".user_page_link"), edit_account_path(:anchor => "public")) %></p>
-    <p><%= t ".anon_edits_html", :link => link_to(t(".anon_edits_link_text"), t(".anon_edits_link")) %></p>
-  <% else %>
+  <% if @enable_editor %>
     <%= render :partial => preferred_editor %>
   <% end %>
 <% end %>
index 90040f7bfae41cdaf6e7deb55f79d4bc29d86e4f..895ab380c16c275d0ff3c984fc212a728078a9c4 100644 (file)
@@ -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 }]
     }
   }
 ];
index 59a66354839eb0fb7d941cafdc421db3f5a30022..2333bf08f69d3ebf34629a1a184c3d90304967a1 100644 (file)
@@ -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}
index 7c48cafcb753606687f253f53fa88a0bb835ce18..d343d978539e7c6c3292b461e93d0a8ecc25a1c0 100644 (file)
@@ -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}
index 7b2be6a0cb63180d6a2a1376550d2688d2d7fc40..9aabfc92aa071d49a5c7bdceab40a027b5b353b2 100644 (file)
@@ -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"
index 083612acc7d2a358666a679e9bdffef541c5951c..02d9047e6e9ac79aa95c81d502a8d7ebf3830333 100644 (file)
@@ -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
index aa761d9f66f9db8369dbb51909284add968a3590..ad5d747b6d31b61a4416cdd9446de382a5bd9109 100644 (file)
@@ -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}
index c147303be73b814caad24f8f3bfd9b11c3e2649f..271d8272a14cce039a08a90cdf9ce23956a096c9 100644 (file)
@@ -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}
index ead60fb80b99d1bb5cf4afad6b36eaaa476540f5..b950cc39076a1ff3d157cfc12dfdaf1eaed8b9e8 100644 (file)
@@ -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
index 04f1d259fe42cc4ca24f71b6d230ce46cdbbad74..d7fd37996c4b69145d832a2d0ae2cea494bfdd7a 100644 (file)
@@ -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:
index 08fc951a5c27dc1ed37a0795f35867ef9f5d8199..3f1d4dad3451789f4f0a138e1573db8d5e2e94b7 100644 (file)
@@ -1367,6 +1367,7 @@ skr-arab:
     show:
       title: نوٹ:%{id}
       description: تفصیل
+      description_when_author_is_deleted: مٹا ݙتے
       discussion: بحث مباحثہ
       subscribe: سبسکرائب کرو
       unsubscribe: اݨ سبسکرائب کرو
index 82e9c870d83fcdaa2ac080755ab9c431d6dcc89a..af42dc29cbc6bb9b7be5a13b75f7acaad221cd33 100644 (file)
@@ -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: Ова белешка садржи коментаре анонимних корисника које би
index 2b86326986dae9e1c07c2df8ac060aa82769e24d..54bf037cf293984488572a901710283a02d9222b 100644 (file)
@@ -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
 
index c79199145163359dd3a7efbc6dcb6e539c4037e1..2e0346f0031c891d226deeddaa284adb2801cc31 100644 (file)
@@ -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 (file)
index 0000000..7798f22
--- /dev/null
@@ -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
index 9093c47fe72c5c075e58bb5ff6659f54ec8e974b..d23c2d7487802486884652bc258e904134844831 100644 (file)
@@ -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'),
index 27b550e40bd1f00ff0be62f75c43d5c67f4d08a2..17f28a4e9cb4ef134a4ac205690159499ff3b9ad 100644 (file)
@@ -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
index 8ce19f3ea2f5f80669801e34fc3d9115d5f0181c..0c5faa9f9c0be4f834fc269830631e731d0b67ba 100644 (file)
@@ -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
index 880c340115d13e98c04e2c57aca1ebb919757b1b..603499503b20eafef0411adbde301ccd8543f6ea 100644 (file)
@@ -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
index cc9eaf312375b322ba94f27ff38099125450fddf..6946d2ab1cda8b8e24b52b70394b9875604b9ad8 100644 (file)
@@ -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
index 5e480b69c0b1d02584554a2ba43b9578f444f8c2..9e2972495756de8b987d642b5f90ab4d4ee7667a 100644 (file)
@@ -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
index 8e3a14ab6c8717a5860dbb7a1d8ed2284ce9bad2..aba69b03aa4135985daa3683f1bdb3ea6ecc9e8f 100644 (file)
@@ -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
index 3ede1842b5d35137602c92dde30a3ddf1e83f9b2..cd6737dca32070b651f30dba7a2f9242dabe8d27 100644 (file)
@@ -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
index c7b383202bb45081f2396b4a916e93108f9ab606..3ac6eaf2f0fd87c5466578627f8487a979ae6386 100644 (file)
@@ -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 (file)
index 0000000..71251aa
--- /dev/null
@@ -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
index 5147733e3edc4c50d46b9ac49992f050f976d100..4e7315edef3d89287cb0982853ff3f746d32749f 100644 (file)
@@ -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 (file)
index 0000000..866e485
--- /dev/null
@@ -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
index 08f5cb8e37d1fac49517424c56ed9a82e2626b58..91a033c4d37f8e780d3f3dbfd59367a73b40e0af 100644 (file)
@@ -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, "<b>1.</b> #{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}
-            });
           };
         }
       });
index 902fdb90605625ba1d8243d016410cbef207b0e7..9e73a5eb4602b2e38b5ce82b52a08244648823d6 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
   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"