]> git.openstreetmap.org Git - rails.git/commitdiff
Merge remote-tracking branch 'upstream/pull/5610'
authorTom Hughes <tom@compton.nu>
Wed, 5 Feb 2025 18:35:10 +0000 (18:35 +0000)
committerTom Hughes <tom@compton.nu>
Wed, 5 Feb 2025 18:35:10 +0000 (18:35 +0000)
20 files changed:
app/abilities/api_ability.rb
app/assets/javascripts/osm.js.erb
app/assets/javascripts/user.js
app/controllers/api/nodes_controller.rb
app/controllers/api/relations_controller.rb
app/controllers/api/ways_controller.rb
app/views/issues/_comments.html.erb
app/views/notes/show.html.erb
config/eslint.js
config/initializers/sanitize.rb
config/locales/en.yml
config/routes.rb
lib/tasks/eslint.rake
package.json
test/controllers/api/changesets_controller_test.rb
test/controllers/api/nodes_controller_test.rb
test/controllers/api/relations_controller_test.rb
test/controllers/api/ways_controller_test.rb
test/system/issues_test.rb
yarn.lock

index 13a453eb59305e2a8b485994abd19ea66f0bbf8c..c74a4d0996adedcebd66ec59d47d696b01ddd28c 100644 (file)
@@ -38,7 +38,7 @@ class ApiAbility
         if user.terms_agreed?
           can [:create, :update, :upload, :close, :subscribe, :unsubscribe], Changeset if scope?(token, :write_api)
           can :create, ChangesetComment if scope?(token, :write_api)
-          can [:create, :update, :delete], [Node, Way, Relation] if scope?(token, :write_api)
+          can [:create, :update, :destroy], [Node, Way, Relation] if scope?(token, :write_api)
         end
 
         if user.moderator?
index 254e00810cd6201e459388dd5754b9df9c0f57f6..749cf5b7736767b6e52a59e70c02babac07d9022 100644 (file)
@@ -4,45 +4,44 @@
 //= depend_on key.yml
 
 OSM = {
-<% if defined?(Settings.matomo) %>
-  MATOMO:                  <%= Settings.matomo.to_json %>,
-<% end %>
-
-  MAX_REQUEST_AREA:        <%= Settings.max_request_area.to_json %>,
-  SERVER_PROTOCOL:         <%= Settings.server_protocol.to_json %>,
-  SERVER_URL:              <%= Settings.server_url.to_json %>,
-  API_VERSION:             <%= Settings.api_version.to_json %>,
-  STATUS:                  <%= Settings.status.to_json %>,
-  MAX_NOTE_REQUEST_AREA:   <%= Settings.max_note_request_area.to_json %>,
-  OVERPASS_URL:            <%= Settings.overpass_url.to_json %>,
-  OVERPASS_CREDENTIALS:    <%= Settings.overpass_credentials.to_json %>,
-  NOMINATIM_URL:           <%= Settings.nominatim_url.to_json %>,
-  GRAPHHOPPER_URL:         <%= Settings.graphhopper_url.to_json %>,
-  FOSSGIS_OSRM_URL:        <%= Settings.fossgis_osrm_url.to_json %>,
-  FOSSGIS_VALHALLA_URL:    <%= Settings.fossgis_valhalla_url.to_json %>,
-  DEFAULT_LOCALE:          <%= I18n.default_locale.to_json %>,
-
-<% if Settings.key?(:thunderforest_key) %>
-  THUNDERFOREST_KEY:       <%= Settings.thunderforest_key.to_json %>,
-<% end %>
-
-<% if Settings.key?(:tracestrack_key) %>
-  TRACESTRACK_KEY:         <%= Settings.tracestrack_key.to_json %>,
-<% end %>
-
-  LAYER_DEFINITIONS:       <%= YAML.load_file(Rails.root.join("config/layers.yml")).to_json %>,
-  LAYERS_WITH_MAP_KEY:     <%= YAML.load_file(Rails.root.join("config/key.yml")).keys.to_json %>,
-
-  MARKER_GREEN:            <%= image_path("marker-green.png").to_json %>,
-  MARKER_RED:              <%= image_path("marker-red.png").to_json %>,
-
-  MARKER_ICON:             <%= image_path("leaflet/dist/images/marker-icon.png").to_json %>,
-  MARKER_ICON_2X:          <%= image_path("leaflet/dist/images/marker-icon-2x.png").to_json %>,
-  MARKER_SHADOW:           <%= image_path("leaflet/dist/images/marker-shadow.png").to_json %>,
-
-  NEW_NOTE_MARKER:         <%= image_path("new_note_marker.svg").to_json %>,
-  OPEN_NOTE_MARKER:        <%= image_path("open_note_marker.svg").to_json %>,
-  CLOSED_NOTE_MARKER:      <%= image_path("closed_note_marker.svg").to_json %>,
+  ...<%=
+    %i[
+      matomo
+      max_request_area
+      server_protocol
+      server_url
+      api_version
+      status
+      max_note_request_area
+      overpass_url
+      overpass_credentials
+      nominatim_url
+      graphhopper_url
+      fossgis_osrm_url
+      fossgis_valhalla_url
+      thunderforest_key
+      tracestrack_key
+    ]
+      .each_with_object({}) do |key, hash|
+        hash[key.to_s.upcase] = Settings.send(key) if Settings.respond_to?(key)
+      end.to_json
+  %>,
+
+  DEFAULT_LOCALE: <%= I18n.default_locale.to_json %>,
+
+  LAYER_DEFINITIONS: <%= YAML.load_file(Rails.root.join("config/layers.yml")).to_json %>,
+  LAYERS_WITH_MAP_KEY: <%= YAML.load_file(Rails.root.join("config/key.yml")).keys.to_json %>,
+
+  MARKER_GREEN: <%= image_path("marker-green.png").to_json %>,
+  MARKER_RED: <%= image_path("marker-red.png").to_json %>,
+
+  MARKER_ICON: <%= image_path("leaflet/dist/images/marker-icon.png").to_json %>,
+  MARKER_ICON_2X: <%= image_path("leaflet/dist/images/marker-icon-2x.png").to_json %>,
+  MARKER_SHADOW: <%= image_path("leaflet/dist/images/marker-shadow.png").to_json %>,
+
+  NEW_NOTE_MARKER: <%= image_path("new_note_marker.svg").to_json %>,
+  OPEN_NOTE_MARKER: <%= image_path("open_note_marker.svg").to_json %>,
+  CLOSED_NOTE_MARKER: <%= image_path("closed_note_marker.svg").to_json %>,
 
   apiUrl: function (object) {
     var apiType = object.type === "note" ? "notes" : object.type;
@@ -58,28 +57,12 @@ OSM = {
   },
 
   params: function (search) {
-    var params = {};
-
-    search = (search || window.location.search).replace("?", "").split(/&|;/);
-
-    for (var i = 0; i < search.length; ++i) {
-      var pair = search[i],
-          j = pair.indexOf("="),
-          key = pair.slice(0, j),
-          val = pair.slice(++j);
-
-      try {
-        params[key] = decodeURIComponent(val);
-      } catch (e) {
-        // Ignore parse exceptions
-      }
-    }
-
-    return params;
+    var query = search || window.location.search;
+    return Object.fromEntries(new URLSearchParams(query));
   },
 
   mapParams: function (search) {
-    var params = OSM.params(search), mapParams = {}, match;
+    var params = OSM.params(search), mapParams = {};
 
     if (params.mlon && params.mlat) {
       mapParams.marker = true;
@@ -229,7 +212,7 @@ OSM = {
     return 6372795 * 2 * Math.asin(
       Math.sqrt(
         Math.pow(Math.sin(latdiff / 2), 2) +
-        Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(lngdiff / 2), 2)
+        (Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(lngdiff / 2), 2))
       ));
   }
 };
index 1d167d977fb70e85481179b0671e963c9bc1c08a..9366115cecb730d4465711bca85d71f7f2ccf45c 100644 (file)
@@ -64,7 +64,7 @@ $(document).ready(function () {
       map.on("click", function (e) {
         if (!$("#updatehome").is(":checked")) return;
 
-        const [lat, lon] = OSM.cropLocation(e.latlng);
+        const [lat, lon] = OSM.cropLocation(e.latlng, map.getZoom());
 
         $("#home_lat").val(lat);
         $("#home_lon").val(lon);
index 6477271d4e890788f3c260775cd27a3c3ad53a88..b7165b2fe69039342a3594f6bd4c5126075a8101 100644 (file)
@@ -2,14 +2,14 @@
 
 module Api
   class NodesController < ApiController
-    before_action :check_api_writable, :only => [:create, :update, :delete]
-    before_action :authorize, :only => [:create, :update, :delete]
+    before_action :check_api_writable, :only => [:create, :update, :destroy]
+    before_action :authorize, :only => [:create, :update, :destroy]
 
     authorize_resource
 
-    before_action :require_public_data, :only => [:create, :update, :delete]
-    before_action :set_request_formats, :except => [:create, :update, :delete]
-    before_action :check_rate_limit, :only => [:create, :update, :delete]
+    before_action :require_public_data, :only => [:create, :update, :destroy]
+    before_action :set_request_formats, :except => [:create, :update, :destroy]
+    before_action :check_rate_limit, :only => [:create, :update, :destroy]
 
     # Dump the details on many nodes whose ids are given in the "nodes" parameter.
     def index
@@ -68,7 +68,7 @@ module Api
     # Delete a node. Doesn't actually delete it, but retains its history
     # in a wiki-like way. We therefore treat it like an update, so the delete
     # method returns the new version number.
-    def delete
+    def destroy
       node = Node.find(params[:id])
       new_node = Node.from_xml(request.raw_post)
 
index ae101f373762b0c282ed1b6218b1bbe875cfc5b7..f712534f001aebdb747234abf27d1f68a87ce982 100644 (file)
@@ -1,13 +1,13 @@
 module Api
   class RelationsController < ApiController
-    before_action :check_api_writable, :only => [:create, :update, :delete]
-    before_action :authorize, :only => [:create, :update, :delete]
+    before_action :check_api_writable, :only => [:create, :update, :destroy]
+    before_action :authorize, :only => [:create, :update, :destroy]
 
     authorize_resource
 
-    before_action :require_public_data, :only => [:create, :update, :delete]
-    before_action :set_request_formats, :except => [:create, :update, :delete]
-    before_action :check_rate_limit, :only => [:create, :update, :delete]
+    before_action :require_public_data, :only => [:create, :update, :destroy]
+    before_action :set_request_formats, :except => [:create, :update, :destroy]
+    before_action :check_rate_limit, :only => [:create, :update, :destroy]
 
     def index
       raise OSM::APIBadUserInput, "The parameter relations is required, and must be of the form relations=id[,id[,id...]]" unless params["relations"]
@@ -57,7 +57,7 @@ module Api
       render :plain => relation.version.to_s
     end
 
-    def delete
+    def destroy
       relation = Relation.find(params[:id])
       new_relation = Relation.from_xml(request.raw_post)
       if new_relation && new_relation.id == relation.id
index 632fdb9a629637d1112831012e803e3f145605ba..285ed46046041af296b1dc223e9320293fcd14d5 100644 (file)
@@ -1,13 +1,13 @@
 module Api
   class WaysController < ApiController
-    before_action :check_api_writable, :only => [:create, :update, :delete]
-    before_action :authorize, :only => [:create, :update, :delete]
+    before_action :check_api_writable, :only => [:create, :update, :destroy]
+    before_action :authorize, :only => [:create, :update, :destroy]
 
     authorize_resource
 
-    before_action :require_public_data, :only => [:create, :update, :delete]
-    before_action :set_request_formats, :except => [:create, :update, :delete]
-    before_action :check_rate_limit, :only => [:create, :update, :delete]
+    before_action :require_public_data, :only => [:create, :update, :destroy]
+    before_action :set_request_formats, :except => [:create, :update, :destroy]
+    before_action :check_rate_limit, :only => [:create, :update, :destroy]
 
     def index
       raise OSM::APIBadUserInput, "The parameter ways is required, and must be of the form ways=id[,id[,id...]]" unless params["ways"]
@@ -60,7 +60,7 @@ module Api
     end
 
     # This is the API call to delete a way
-    def delete
+    def destroy
       way = Way.find(params[:id])
       new_way = Way.from_xml(request.raw_post)
 
index f828e5a4350b4a80bb64306190aa85ecc4cd18bd..d7005d75eb7571fcc1f6e2d20bc7f22d681c0115 100644 (file)
@@ -21,7 +21,8 @@
   <%= bootstrap_form_for @new_comment, :url => issue_comments_path(@issue) do |f| %>
     <%= f.richtext_field :body, :cols => 80, :rows => 20, :hide_label => true %>
     <%= f.form_group do %>
-      <%= f.check_box :reassign, { :label => t(".reassign_param"), :id => "reassign", :name => "reassign", :checked => false }, true, false %>
+      <%= f.check_box :reassign, { :label => @issue.assigned_role == "administrator" ? t(".reassign_to_moderators") : t(".reassign_to_administrators"),
+                                   :id => "reassign", :name => "reassign", :checked => false }, true, false %>
     <% end %>
     <%= f.primary %>
   <% end %>
index 3d9b4a9bace5aafa7d1a378abc6da3ed1a0bf54e..4f20cdd441a227329bc4fca28bcf572c3ad5d016 100644 (file)
@@ -5,7 +5,7 @@
 <div>
   <h4><%= t(".description") %></h4>
   <div class="overflow-hidden ms-2">
-    <%= h(note_description(@note.author, @note.description).to_html) %>
+    <%= note_description(@note.author, @note.description).to_html %>
   </div>
 
   <div class="details" data-coordinates="<%= @note.lat %>,<%= @note.lon %>" data-status="<%= @note.status %>">
index b4d0429e4ae8417db3fbfe4b3324fe721e20fc39..cb421a9920ce3874ab151c49e340c4ce189fdb1e 100644 (file)
@@ -1,9 +1,15 @@
 const globals = require("globals");
 const js = require("@eslint/js");
+const erb = require("eslint-plugin-erb");
+const stylisticJs = require("@stylistic/eslint-plugin-js");
 
 module.exports = [
   js.configs.recommended,
+  erb.configs.recommended,
   {
+    plugins: {
+      "@stylistic": stylisticJs
+    },
     languageOptions: {
       ecmaVersion: 2021,
       sourceType: "script",
@@ -15,38 +21,71 @@ module.exports = [
         L: "readonly",
         OSM: "writable",
         Matomo: "readonly",
-        Qs: "readonly",
         Turbo: "readonly",
         updateLinks: "readonly"
       }
     },
+    linterOptions: {
+      // The "unused disable directive" is set to "warn" by default.
+      // For the ERB plugin to work correctly, you must disable
+      // this directive to avoid issues described here
+      // https://github.com/eslint/eslint/discussions/18114
+      // If you're using the CLI, you might also use the following flag:
+      // --report-unused-disable-directives-severity=off
+      reportUnusedDisableDirectives: "off"
+    },
     rules: {
-      "accessor-pairs": "error",
-      "array-bracket-newline": ["error", "consistent"],
-      "array-bracket-spacing": "error",
-      "array-callback-return": "error",
-      "block-scoped-var": "error",
-      "block-spacing": "error",
-      "brace-style": ["error", "1tbs", { allowSingleLine: true }],
-      "comma-dangle": "error",
-      "comma-spacing": "error",
-      "comma-style": "error",
-      "computed-property-spacing": "error",
-      "curly": ["error", "multi-line", "consistent"],
-      "dot-location": ["error", "property"],
-      "dot-notation": "error",
-      "eol-last": "error",
-      "eqeqeq": ["error", "smart"],
-      "func-call-spacing": "error",
-      "indent": ["error", 2, {
+      "@stylistic/array-bracket-newline": ["error", "consistent"],
+      "@stylistic/array-bracket-spacing": "error",
+      "@stylistic/block-spacing": "error",
+      "@stylistic/brace-style": ["error", "1tbs", { allowSingleLine: true }],
+      "@stylistic/comma-dangle": "error",
+      "@stylistic/comma-spacing": "error",
+      "@stylistic/comma-style": "error",
+      "@stylistic/computed-property-spacing": "error",
+      "@stylistic/dot-location": ["error", "property"],
+      "@stylistic/eol-last": "error",
+      "@stylistic/func-call-spacing": "error",
+      "@stylistic/indent": ["error", 2, {
         SwitchCase: 1,
         VariableDeclarator: "first",
         FunctionDeclaration: { parameters: "first" },
         FunctionExpression: { parameters: "first" },
         CallExpression: { arguments: "first" }
       }],
-      "key-spacing": "error",
-      "keyword-spacing": "error",
+      "@stylistic/key-spacing": "error",
+      "@stylistic/keyword-spacing": "error",
+      "@stylistic/no-floating-decimal": "error",
+      "@stylistic/no-mixed-operators": "error",
+      "@stylistic/no-multiple-empty-lines": "error",
+      "@stylistic/no-multi-spaces": "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/operator-linebreak": ["error", "after"],
+      "@stylistic/padded-blocks": ["error", "never"],
+      "@stylistic/quote-props": ["error", "consistent-as-needed", { keywords: true, numbers: true }],
+      "@stylistic/quotes": ["error", "double"],
+      "@stylistic/semi": ["error", "always"],
+      "@stylistic/semi-spacing": "error",
+      "@stylistic/semi-style": "error",
+      "@stylistic/space-before-blocks": "error",
+      "@stylistic/space-before-function-paren": ["error", { named: "never" }],
+      "@stylistic/space-in-parens": "error",
+      "@stylistic/space-infix-ops": "error",
+      "@stylistic/space-unary-ops": "error",
+      "@stylistic/switch-colon-spacing": "error",
+      "@stylistic/wrap-iife": "error",
+      "@stylistic/wrap-regex": "error",
+
+      "accessor-pairs": "error",
+      "array-callback-return": "error",
+      "block-scoped-var": "error",
+      "curly": ["error", "multi-line", "consistent"],
+      "dot-notation": "error",
+      "eqeqeq": ["error", "smart"],
       "no-alert": "warn",
       "no-array-constructor": "error",
       "no-caller": "error",
@@ -57,7 +96,6 @@ module.exports = [
       "no-extend-native": "error",
       "no-extra-bind": "error",
       "no-extra-label": "error",
-      "no-floating-decimal": "error",
       "no-implicit-coercion": "warn",
       "no-implicit-globals": "warn",
       "no-implied-eval": "error",
@@ -68,25 +106,20 @@ module.exports = [
       "no-lone-blocks": "error",
       "no-lonely-if": "error",
       "no-loop-func": "error",
-      "no-mixed-operators": "error",
-      "no-multiple-empty-lines": "error",
-      "no-multi-spaces": "error",
       "no-multi-str": "error",
       "no-negated-condition": "error",
       "no-nested-ternary": "error",
       "no-new": "error",
       "no-new-func": "error",
-      "no-new-object": "error",
       "no-new-wrappers": "error",
+      "no-object-constructor": "error",
       "no-octal-escape": "error",
       "no-param-reassign": "error",
-      "no-process-env": "error",
       "no-proto": "error",
       "no-script-url": "error",
       "no-self-compare": "error",
       "no-sequences": "error",
       "no-throw-literal": "error",
-      "no-trailing-spaces": "error",
       "no-undef-init": "error",
       "no-undefined": "error",
       "no-unmodified-loop-condition": "error",
@@ -99,26 +132,7 @@ module.exports = [
       "no-use-before-define": ["error", { functions: false }],
       "no-void": "error",
       "no-warning-comments": "warn",
-      "no-whitespace-before-property": "error",
-      "object-curly-newline": ["error", { consistent: true }],
-      "object-curly-spacing": ["error", "always"],
-      "object-property-newline": ["error", { allowAllPropertiesOnSameLine: true }],
-      "operator-linebreak": ["error", "after"],
-      "padded-blocks": ["error", "never"],
-      "quote-props": ["error", "consistent-as-needed", { keywords: true, numbers: true }],
-      "quotes": ["error", "double"],
       "radix": ["error", "always"],
-      "semi": ["error", "always"],
-      "semi-spacing": "error",
-      "semi-style": "error",
-      "space-before-blocks": "error",
-      "space-before-function-paren": ["error", { named: "never" }],
-      "space-in-parens": "error",
-      "space-infix-ops": "error",
-      "space-unary-ops": "error",
-      "switch-colon-spacing": "error",
-      "wrap-iife": "error",
-      "wrap-regex": "error",
       "yoda": "error"
     }
   },
index c9d6a5dbac75d48b693e7b1927c19b1c9a297f30..f8f2b784d9ac7c17cd3d63755fca41b48ccbcdd6 100644 (file)
@@ -2,6 +2,10 @@ Sanitize::Config::OSM = Sanitize::Config.merge(
   Sanitize::Config::RELAXED,
   :elements => Sanitize::Config::RELAXED[:elements] - %w[div style],
   :remove_contents => %w[script style],
+  :attributes => Sanitize::Config.merge(
+    Sanitize::Config::RELAXED[:attributes],
+    "img" => Sanitize::Config::RELAXED[:attributes]["img"] + ["loading"]
+  ),
   :transformers => lambda do |env|
     style = env[:node]["style"] || ""
 
@@ -24,5 +28,7 @@ Sanitize::Config::OSM = Sanitize::Config.merge(
 
       env[:node]["rel"] = rel.split.select { |r| r == "me" }.append("nofollow", "noopener", "noreferrer").sort.join(" ")
     end
+
+    env[:node]["loading"] = "lazy" if env[:node_name] == "img"
   end
 )
index 40b4bfa142997eaa20c2eabba88093012d753dee..25794cc9fde873a5d041605d5f2db5efa7bf005a 100644 (file)
@@ -1551,7 +1551,8 @@ en:
       reopened: Issue status has been set to 'Open'
     comments:
       comment_from_html: "Comment from %{user_link} on %{comment_created_at}"
-      reassign_param: Reassign Issue?
+      reassign_to_moderators: Reassign Issue to Moderators
+      reassign_to_administrators: Reassign Issue to Administrators
     reports:
       reported_by_html: "Reported as %{category} by %{user} on %{updated_at}"
     helper:
index 19dfc9a7729519c26285160ff1d3d68a36c157c7..b562ca9f4a0c60b6d1984b5db21acf80aec0153a 100644 (file)
@@ -30,41 +30,38 @@ 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+/
 
-    put "node/create" => "nodes#create"
     get "node/:id/ways" => "ways#ways_for_node", :as => :node_ways, :id => /\d+/
     get "node/:id/relations" => "relations#relations_for_node", :as => :node_relations, :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+/
-    get "node/:id" => "nodes#show", :as => :api_node, :id => /\d+/
-    put "node/:id" => "nodes#update", :id => /\d+/
-    delete "node/:id" => "nodes#delete", :id => /\d+/
-    get "nodes" => "nodes#index"
 
-    put "way/create" => "ways#create"
     get "way/:id/history" => "old_ways#history", :as => :api_way_history, :id => /\d+/
     get "way/:id/full" => "ways#full", :as => :way_full, :id => /\d+/
     get "way/:id/relations" => "relations#relations_for_way", :as => :way_relations, :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+/
-    get "way/:id" => "ways#show", :as => :api_way, :id => /\d+/
-    put "way/:id" => "ways#update", :id => /\d+/
-    delete "way/:id" => "ways#delete", :id => /\d+/
-    get "ways" => "ways#index"
 
-    put "relation/create" => "relations#create"
     get "relation/:id/relations" => "relations#relations_for_relation", :as => :relation_relations, :id => /\d+/
     get "relation/:id/history" => "old_relations#history", :as => :api_relation_history, :id => /\d+/
     get "relation/:id/full" => "relations#full", :as => :relation_full, :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+/
-    get "relation/:id" => "relations#show", :as => :api_relation, :id => /\d+/
-    put "relation/:id" => "relations#update", :id => /\d+/
-    delete "relation/:id" => "relations#delete", :id => /\d+/
-    get "relations" => "relations#index"
   end
 
   namespace :api, :path => "api/0.6" do
+    resources :nodes, :only => [:index, :create]
+    resources :nodes, :path => "node", :id => /\d+/, :only => [:show, :update, :destroy]
+    put "node/create" => "nodes#create", :as => nil
+
+    resources :ways, :only => [:index, :create]
+    resources :ways, :path => "way", :id => /\d+/, :only => [:show, :update, :destroy]
+    put "way/create" => "ways#create", :as => nil
+
+    resources :relations, :only => [:index, :create]
+    resources :relations, :path => "relation", :id => /\d+/, :only => [:show, :update, :destroy]
+    put "relation/create" => "relations#create", :as => nil
+
     resource :map, :only => :show
 
     resources :tracepoints, :path => "trackpoints", :only => :index
index 790ef150d6e7b3220921348ee373857eb86356de..27b550e40bd1f00ff0be62f75c43d5c67f4d08a2 100644 (file)
@@ -10,7 +10,7 @@ end
 
 def js_files
   Rails.application.assets.each_file.select do |file|
-    file.ends_with?(".js") && !file.match?(%r{/(gems|vendor|i18n|node_modules)/})
+    (file.ends_with?(".js") || file.ends_with?(".js.erb")) && !file.match?(%r{/(gems|vendor|i18n|node_modules)/})
   end
 end
 
index 82cae94e55435fe5817b3c9e6ea1c6effa72545c..371d01aff40a1c24a000a65e0e906f498df93fcb 100644 (file)
@@ -10,6 +10,8 @@
   },
   "devDependencies": {
     "eslint": "^9.0.0",
+    "eslint-plugin-erb": "^2.1.0",
+    "@stylistic/eslint-plugin-js": "^3.0.0",
     "eslint-formatter-compact": "^8.40.0"
   }
 }
index 5c0e7c9c268dc82cb5ca2a3533a5c7dc21186e96..39b1f3cf81d26773d27c3a9d4dd8183b615bb8a9 100644 (file)
@@ -2130,7 +2130,7 @@ module Api
       # add a single node to it
       with_controller(NodesController.new) do
         xml = "<osm><node lon='0.1' lat='0.2' changeset='#{changeset_id}'/></osm>"
-        put node_create_path, :params => xml, :headers => auth_header
+        post api_nodes_path, :params => xml, :headers => auth_header
         assert_response :success, "Couldn't create node."
       end
 
@@ -2145,7 +2145,7 @@ module Api
       # add another node to it
       with_controller(NodesController.new) do
         xml = "<osm><node lon='0.2' lat='0.1' changeset='#{changeset_id}'/></osm>"
-        put node_create_path, :params => xml, :headers => auth_header
+        post api_nodes_path, :params => xml, :headers => auth_header
         assert_response :success, "Couldn't create second node."
       end
 
@@ -2515,7 +2515,7 @@ module Api
       with_controller(NodesController.new) do
         # create a new node
         xml = "<osm><node changeset='#{cs_id}' lat='0.0' lon='0.0'/></osm>"
-        put node_create_path, :params => xml, :headers => auth_header
+        post api_nodes_path, :params => xml, :headers => auth_header
         assert_response :success, "can't create a new node"
         node_id = @response.body.to_i
 
index 9896c34a5a70ac491e27e600694237cc22e7abc6..523498216884c42925739dddc5fda1d1e86faf14 100644 (file)
@@ -6,7 +6,15 @@ module Api
     # test all routes which lead to this controller
     def test_routes
       assert_routing(
-        { :path => "/api/0.6/node/create", :method => :put },
+        { :path => "/api/0.6/nodes", :method => :get },
+        { :controller => "api/nodes", :action => "index" }
+      )
+      assert_routing(
+        { :path => "/api/0.6/nodes.json", :method => :get },
+        { :controller => "api/nodes", :action => "index", :format => "json" }
+      )
+      assert_routing(
+        { :path => "/api/0.6/nodes", :method => :post },
         { :controller => "api/nodes", :action => "create" }
       )
       assert_routing(
@@ -23,18 +31,62 @@ module Api
       )
       assert_routing(
         { :path => "/api/0.6/node/1", :method => :delete },
-        { :controller => "api/nodes", :action => "delete", :id => "1" }
+        { :controller => "api/nodes", :action => "destroy", :id => "1" }
       )
-      assert_routing(
-        { :path => "/api/0.6/nodes", :method => :get },
-        { :controller => "api/nodes", :action => "index" }
-      )
-      assert_routing(
-        { :path => "/api/0.6/nodes.json", :method => :get },
-        { :controller => "api/nodes", :action => "index", :format => "json" }
+
+      assert_recognizes(
+        { :controller => "api/nodes", :action => "create" },
+        { :path => "/api/0.6/node/create", :method => :put }
       )
     end
 
+    ##
+    # test fetching multiple nodes
+    def test_index
+      node1 = create(:node)
+      node2 = create(:node, :deleted)
+      node3 = create(:node)
+      node4 = create(:node, :with_history, :version => 2)
+      node5 = create(:node, :deleted, :with_history, :version => 2)
+
+      # check error when no parameter provided
+      get api_nodes_path
+      assert_response :bad_request
+
+      # check error when no parameter value provided
+      get api_nodes_path(:nodes => "")
+      assert_response :bad_request
+
+      # test a working call
+      get api_nodes_path(:nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id}")
+      assert_response :success
+      assert_select "osm" do
+        assert_select "node", :count => 5
+        assert_select "node[id='#{node1.id}'][visible='true']", :count => 1
+        assert_select "node[id='#{node2.id}'][visible='false']", :count => 1
+        assert_select "node[id='#{node3.id}'][visible='true']", :count => 1
+        assert_select "node[id='#{node4.id}'][visible='true']", :count => 1
+        assert_select "node[id='#{node5.id}'][visible='false']", :count => 1
+      end
+
+      # test a working call with json format
+      get api_nodes_path(:nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id}", :format => "json")
+
+      js = ActiveSupport::JSON.decode(@response.body)
+      assert_not_nil js
+      assert_equal 5, js["elements"].count
+      assert_equal 5, (js["elements"].count { |a| a["type"] == "node" })
+      assert_equal 1, (js["elements"].count { |a| a["id"] == node1.id && a["visible"].nil? })
+      assert_equal 1, (js["elements"].count { |a| a["id"] == node2.id && a["visible"] == false })
+      assert_equal 1, (js["elements"].count { |a| a["id"] == node3.id && a["visible"].nil? })
+      assert_equal 1, (js["elements"].count { |a| a["id"] == node4.id && a["visible"].nil? })
+      assert_equal 1, (js["elements"].count { |a| a["id"] == node5.id && a["visible"] == false })
+
+      # check error when a non-existent node is included
+      get api_nodes_path(:nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id},0")
+      assert_response :not_found
+    end
+
     def test_create
       private_user = create(:user, :data_public => false)
       private_changeset = create(:changeset, :user => private_user)
@@ -49,7 +101,7 @@ module Api
       # create a minimal xml file
       xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
       assert_difference("OldNode.count", 0) do
-        put node_create_path, :params => xml
+        post api_nodes_path, :params => xml
       end
       # hope for unauthorized
       assert_response :unauthorized, "node upload did not return unauthorized status"
@@ -60,7 +112,7 @@ module Api
       # create a minimal xml file
       xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{private_changeset.id}'/></osm>"
       assert_difference("Node.count", 0) do
-        put node_create_path, :params => xml, :headers => auth_header
+        post api_nodes_path, :params => xml, :headers => auth_header
       end
       # hope for success
       assert_require_public_data "node create did not return forbidden status"
@@ -70,7 +122,7 @@ module Api
 
       # create a minimal xml file
       xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       # hope for success
       assert_response :success, "node upload did not return success status"
 
@@ -98,14 +150,14 @@ module Api
 
       # test that the upload is rejected when xml is valid, but osm doc isn't
       xml = "<create/>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       assert_response :bad_request, "node upload did not return bad_request status"
       assert_equal "Cannot parse valid node from xml string <create/>. XML doesn't contain an osm/node element.", @response.body
 
       # test that the upload is rejected when no lat is supplied
       # create a minimal xml file
       xml = "<osm><node lon='#{lon}' changeset='#{changeset.id}'/></osm>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       # hope for success
       assert_response :bad_request, "node upload did not return bad_request status"
       assert_equal "Cannot parse valid node from xml string <node lon=\"3.23\" changeset=\"#{changeset.id}\"/>. lat missing", @response.body
@@ -113,7 +165,7 @@ module Api
       # test that the upload is rejected when no lon is supplied
       # create a minimal xml file
       xml = "<osm><node lat='#{lat}' changeset='#{changeset.id}'/></osm>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       # hope for success
       assert_response :bad_request, "node upload did not return bad_request status"
       assert_equal "Cannot parse valid node from xml string <node lat=\"3.434\" changeset=\"#{changeset.id}\"/>. lon missing", @response.body
@@ -121,7 +173,7 @@ module Api
       # test that the upload is rejected when lat is non-numeric
       # create a minimal xml file
       xml = "<osm><node lat='abc' lon='#{lon}' changeset='#{changeset.id}'/></osm>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       # hope for success
       assert_response :bad_request, "node upload did not return bad_request status"
       assert_equal "Cannot parse valid node from xml string <node lat=\"abc\" lon=\"#{lon}\" changeset=\"#{changeset.id}\"/>. lat not a number", @response.body
@@ -129,30 +181,36 @@ module Api
       # test that the upload is rejected when lon is non-numeric
       # create a minimal xml file
       xml = "<osm><node lat='#{lat}' lon='abc' changeset='#{changeset.id}'/></osm>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       # hope for success
       assert_response :bad_request, "node upload did not return bad_request status"
       assert_equal "Cannot parse valid node from xml string <node lat=\"#{lat}\" lon=\"abc\" changeset=\"#{changeset.id}\"/>. lon not a number", @response.body
 
       # test that the upload is rejected when we have a tag which is too long
       xml = "<osm><node lat='#{lat}' lon='#{lon}' changeset='#{changeset.id}'><tag k='foo' v='#{'x' * 256}'/></node></osm>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       assert_response :bad_request, "node upload did not return bad_request status"
       assert_match(/ v: is too long \(maximum is 255 characters\) /, @response.body)
     end
 
-    def test_show
-      # check that a visible node is returned properly
-      get api_node_path(create(:node))
-      assert_response :success
+    def test_show_not_found
+      get api_node_path(0)
+      assert_response :not_found
+    end
 
-      # check that an deleted node is not returned
+    def test_show_deleted
       get api_node_path(create(:node, :deleted))
       assert_response :gone
+    end
 
-      # check chat a non-existent node is not returned
-      get api_node_path(0)
-      assert_response :not_found
+    def test_show
+      node = create(:node, :timestamp => "2021-02-03T00:00:00Z")
+
+      get api_node_path(node)
+
+      assert_response :success
+      assert_not_nil @response.header["Last-Modified"]
+      assert_equal "2021-02-03T00:00:00Z", Time.parse(@response.header["Last-Modified"]).utc.xmlschema
     end
 
     # Ensure the lat/lon is formatted as a decimal e.g. not 4.0e-05
@@ -166,7 +224,7 @@ module Api
 
     # this tests deletion restrictions - basic deletion is tested in the unit
     # tests for node!
-    def test_delete
+    def test_destroy
       private_user = create(:user, :data_public => false)
       private_user_changeset = create(:changeset, :user => private_user)
       private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
@@ -424,53 +482,6 @@ module Api
       assert_response :success, "a valid update request failed"
     end
 
-    ##
-    # test fetching multiple nodes
-    def test_index
-      node1 = create(:node)
-      node2 = create(:node, :deleted)
-      node3 = create(:node)
-      node4 = create(:node, :with_history, :version => 2)
-      node5 = create(:node, :deleted, :with_history, :version => 2)
-
-      # check error when no parameter provided
-      get nodes_path
-      assert_response :bad_request
-
-      # check error when no parameter value provided
-      get nodes_path(:nodes => "")
-      assert_response :bad_request
-
-      # test a working call
-      get nodes_path(:nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id}")
-      assert_response :success
-      assert_select "osm" do
-        assert_select "node", :count => 5
-        assert_select "node[id='#{node1.id}'][visible='true']", :count => 1
-        assert_select "node[id='#{node2.id}'][visible='false']", :count => 1
-        assert_select "node[id='#{node3.id}'][visible='true']", :count => 1
-        assert_select "node[id='#{node4.id}'][visible='true']", :count => 1
-        assert_select "node[id='#{node5.id}'][visible='false']", :count => 1
-      end
-
-      # test a working call with json format
-      get nodes_path(:nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id}", :format => "json")
-
-      js = ActiveSupport::JSON.decode(@response.body)
-      assert_not_nil js
-      assert_equal 5, js["elements"].count
-      assert_equal 5, (js["elements"].count { |a| a["type"] == "node" })
-      assert_equal 1, (js["elements"].count { |a| a["id"] == node1.id && a["visible"].nil? })
-      assert_equal 1, (js["elements"].count { |a| a["id"] == node2.id && a["visible"] == false })
-      assert_equal 1, (js["elements"].count { |a| a["id"] == node3.id && a["visible"].nil? })
-      assert_equal 1, (js["elements"].count { |a| a["id"] == node4.id && a["visible"].nil? })
-      assert_equal 1, (js["elements"].count { |a| a["id"] == node5.id && a["visible"] == false })
-
-      # check error when a non-existent node is included
-      get nodes_path(:nodes => "#{node1.id},#{node2.id},#{node3.id},#{node4.id},#{node5.id},0")
-      assert_response :not_found
-    end
-
     ##
     # test adding tags to a node
     def test_duplicate_tags
@@ -510,7 +521,7 @@ module Api
       xml = "<osm><node lat='0' lon='0' changeset='#{private_changeset.id}'>" \
             "<tag k='\#{@user.inspect}' v='0'/>" \
             "</node></osm>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       assert_require_public_data "Shouldn't be able to create with non-public user"
 
       ## Then try with the public data user
@@ -521,7 +532,7 @@ module Api
       xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'>" \
             "<tag k='\#{@user.inspect}' v='0'/>" \
             "</node></osm>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       assert_response :success
       nodeid = @response.body
 
@@ -556,7 +567,7 @@ module Api
 
       # try creating a node
       xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'/></osm>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       assert_response :success, "node create did not return success status"
 
       # get the id of the node we created
@@ -574,7 +585,7 @@ module Api
 
       # try creating a node, which should be rate limited
       xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'/></osm>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       assert_response :too_many_requests, "node create did not hit rate limit"
     end
 
@@ -603,7 +614,7 @@ module Api
 
       # try creating a node
       xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'/></osm>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       assert_response :success, "node create did not return success status"
 
       # get the id of the node we created
@@ -621,7 +632,7 @@ module Api
 
       # try creating a node, which should be rate limited
       xml = "<osm><node lat='0' lon='0' changeset='#{changeset.id}'/></osm>"
-      put node_create_path, :params => xml, :headers => auth_header
+      post api_nodes_path, :params => xml, :headers => auth_header
       assert_response :too_many_requests, "node create did not hit rate limit"
     end
 
index 5fb62d29f52550b5329581569b2329c035fcedbd..34953f7b7ac62841a1e27002ab383a9130393574 100644 (file)
@@ -6,16 +6,16 @@ module Api
     # test all routes which lead to this controller
     def test_routes
       assert_routing(
-        { :path => "/api/0.6/relation/create", :method => :put },
-        { :controller => "api/relations", :action => "create" }
+        { :path => "/api/0.6/relations", :method => :get },
+        { :controller => "api/relations", :action => "index" }
       )
       assert_routing(
-        { :path => "/api/0.6/relation/1/full", :method => :get },
-        { :controller => "api/relations", :action => "full", :id => "1" }
+        { :path => "/api/0.6/relations.json", :method => :get },
+        { :controller => "api/relations", :action => "index", :format => "json" }
       )
       assert_routing(
-        { :path => "/api/0.6/relation/1/full.json", :method => :get },
-        { :controller => "api/relations", :action => "full", :id => "1", :format => "json" }
+        { :path => "/api/0.6/relations", :method => :post },
+        { :controller => "api/relations", :action => "create" }
       )
       assert_routing(
         { :path => "/api/0.6/relation/1", :method => :get },
@@ -26,20 +26,20 @@ module Api
         { :controller => "api/relations", :action => "show", :id => "1", :format => "json" }
       )
       assert_routing(
-        { :path => "/api/0.6/relation/1", :method => :put },
-        { :controller => "api/relations", :action => "update", :id => "1" }
+        { :path => "/api/0.6/relation/1/full", :method => :get },
+        { :controller => "api/relations", :action => "full", :id => "1" }
       )
       assert_routing(
-        { :path => "/api/0.6/relation/1", :method => :delete },
-        { :controller => "api/relations", :action => "delete", :id => "1" }
+        { :path => "/api/0.6/relation/1/full.json", :method => :get },
+        { :controller => "api/relations", :action => "full", :id => "1", :format => "json" }
       )
       assert_routing(
-        { :path => "/api/0.6/relations", :method => :get },
-        { :controller => "api/relations", :action => "index" }
+        { :path => "/api/0.6/relation/1", :method => :put },
+        { :controller => "api/relations", :action => "update", :id => "1" }
       )
       assert_routing(
-        { :path => "/api/0.6/relations.json", :method => :get },
-        { :controller => "api/relations", :action => "index", :format => "json" }
+        { :path => "/api/0.6/relation/1", :method => :delete },
+        { :controller => "api/relations", :action => "destroy", :id => "1" }
       )
 
       assert_routing(
@@ -66,26 +66,144 @@ module Api
         { :path => "/api/0.6/relation/1/relations.json", :method => :get },
         { :controller => "api/relations", :action => "relations_for_relation", :id => "1", :format => "json" }
       )
+
+      assert_recognizes(
+        { :controller => "api/relations", :action => "create" },
+        { :path => "/api/0.6/relation/create", :method => :put }
+      )
+    end
+
+    ##
+    # test fetching multiple relations
+    def test_index
+      relation1 = create(:relation)
+      relation2 = create(:relation, :deleted)
+      relation3 = create(:relation, :with_history, :version => 2)
+      relation4 = create(:relation, :with_history, :version => 2)
+      relation4.old_relations.find_by(:version => 1).redact!(create(:redaction))
+
+      # check error when no parameter provided
+      get api_relations_path
+      assert_response :bad_request
+
+      # check error when no parameter value provided
+      get api_relations_path(:relations => "")
+      assert_response :bad_request
+
+      # test a working call
+      get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}")
+      assert_response :success
+      assert_select "osm" do
+        assert_select "relation", :count => 4
+        assert_select "relation[id='#{relation1.id}'][visible='true']", :count => 1
+        assert_select "relation[id='#{relation2.id}'][visible='false']", :count => 1
+        assert_select "relation[id='#{relation3.id}'][visible='true']", :count => 1
+        assert_select "relation[id='#{relation4.id}'][visible='true']", :count => 1
+      end
+
+      # test a working call with json format
+      get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}", :format => "json")
+
+      js = ActiveSupport::JSON.decode(@response.body)
+      assert_not_nil js
+      assert_equal 4, js["elements"].count
+      assert_equal 4, (js["elements"].count { |a| a["type"] == "relation" })
+      assert_equal 1, (js["elements"].count { |a| a["id"] == relation1.id && a["visible"].nil? })
+      assert_equal 1, (js["elements"].count { |a| a["id"] == relation2.id && a["visible"] == false })
+      assert_equal 1, (js["elements"].count { |a| a["id"] == relation3.id && a["visible"].nil? })
+      assert_equal 1, (js["elements"].count { |a| a["id"] == relation4.id && a["visible"].nil? })
+
+      # check error when a non-existent relation is included
+      get api_relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id},0")
+      assert_response :not_found
     end
 
     # -------------------------------------
     # Test showing relations.
     # -------------------------------------
 
-    def test_show
-      # check that a visible relation is returned properly
-      get api_relation_path(create(:relation))
-      assert_response :success
+    def test_show_not_found
+      get api_relation_path(0)
+      assert_response :not_found
+    end
 
-      # check that an invisible relation is not returned
+    def test_show_deleted
       get api_relation_path(create(:relation, :deleted))
       assert_response :gone
+    end
 
-      # check chat a non-existent relation is not returned
-      get api_relation_path(0)
+    def test_show
+      relation = create(:relation, :timestamp => "2021-02-03T00:00:00Z")
+      node = create(:node, :timestamp => "2021-04-05T00:00:00Z")
+      create(:relation_member, :relation => relation, :member => node)
+
+      get api_relation_path(relation)
+
+      assert_response :success
+      assert_not_nil @response.header["Last-Modified"]
+      assert_equal "2021-02-03T00:00:00Z", Time.parse(@response.header["Last-Modified"]).utc.xmlschema
+      assert_dom "node", :count => 0
+      assert_dom "relation", :count => 1 do
+        assert_dom "> @id", :text => relation.id.to_s
+      end
+    end
+
+    def test_full_not_found
+      get relation_full_path(999999)
       assert_response :not_found
     end
 
+    def test_full_deleted
+      get relation_full_path(create(:relation, :deleted))
+      assert_response :gone
+    end
+
+    def test_full_empty
+      relation = create(:relation)
+
+      get relation_full_path(relation)
+
+      assert_response :success
+      assert_dom "relation", :count => 1 do
+        assert_dom "> @id", :text => relation.id.to_s
+      end
+    end
+
+    def test_full_with_node_member
+      relation = create(:relation)
+      node = create(:node)
+      create(:relation_member, :relation => relation, :member => node)
+
+      get relation_full_path(relation)
+
+      assert_response :success
+      assert_dom "node", :count => 1 do
+        assert_dom "> @id", :text => node.id.to_s
+      end
+      assert_dom "relation", :count => 1 do
+        assert_dom "> @id", :text => relation.id.to_s
+      end
+    end
+
+    def test_full_with_way_member
+      relation = create(:relation)
+      way = create(:way_with_nodes)
+      create(:relation_member, :relation => relation, :member => way)
+
+      get relation_full_path(relation)
+
+      assert_response :success
+      assert_dom "node", :count => 1 do
+        assert_dom "> @id", :text => way.nodes[0].id.to_s
+      end
+      assert_dom "way", :count => 1 do
+        assert_dom "> @id", :text => way.id.to_s
+      end
+      assert_dom "relation", :count => 1 do
+        assert_dom "> @id", :text => relation.id.to_s
+      end
+    end
+
     ##
     # check that all relations containing a particular node, and no extra
     # relations, are returned from the relations_for_node call.
@@ -151,64 +269,6 @@ module Api
                                   [relation_with_relation, second_relation])
     end
 
-    def test_full
-      # check the "full" mode
-      get relation_full_path(:id => 999999)
-      assert_response :not_found
-
-      get relation_full_path(:id => create(:relation, :deleted).id)
-      assert_response :gone
-
-      get relation_full_path(:id => create(:relation).id)
-      assert_response :success
-      # FIXME: check whether this contains the stuff we want!
-    end
-
-    ##
-    # test fetching multiple relations
-    def test_index
-      relation1 = create(:relation)
-      relation2 = create(:relation, :deleted)
-      relation3 = create(:relation, :with_history, :version => 2)
-      relation4 = create(:relation, :with_history, :version => 2)
-      relation4.old_relations.find_by(:version => 1).redact!(create(:redaction))
-
-      # check error when no parameter provided
-      get relations_path
-      assert_response :bad_request
-
-      # check error when no parameter value provided
-      get relations_path(:relations => "")
-      assert_response :bad_request
-
-      # test a working call
-      get relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}")
-      assert_response :success
-      assert_select "osm" do
-        assert_select "relation", :count => 4
-        assert_select "relation[id='#{relation1.id}'][visible='true']", :count => 1
-        assert_select "relation[id='#{relation2.id}'][visible='false']", :count => 1
-        assert_select "relation[id='#{relation3.id}'][visible='true']", :count => 1
-        assert_select "relation[id='#{relation4.id}'][visible='true']", :count => 1
-      end
-
-      # test a working call with json format
-      get relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id}", :format => "json")
-
-      js = ActiveSupport::JSON.decode(@response.body)
-      assert_not_nil js
-      assert_equal 4, js["elements"].count
-      assert_equal 4, (js["elements"].count { |a| a["type"] == "relation" })
-      assert_equal 1, (js["elements"].count { |a| a["id"] == relation1.id && a["visible"].nil? })
-      assert_equal 1, (js["elements"].count { |a| a["id"] == relation2.id && a["visible"] == false })
-      assert_equal 1, (js["elements"].count { |a| a["id"] == relation3.id && a["visible"].nil? })
-      assert_equal 1, (js["elements"].count { |a| a["id"] == relation4.id && a["visible"].nil? })
-
-      # check error when a non-existent relation is included
-      get relations_path(:relations => "#{relation1.id},#{relation2.id},#{relation3.id},#{relation4.id},0")
-      assert_response :not_found
-    end
-
     # -------------------------------------
     # Test simple relation creation.
     # -------------------------------------
@@ -225,7 +285,7 @@ module Api
 
       # create an relation without members
       xml = "<osm><relation changeset='#{private_changeset.id}'><tag k='test' v='yes' /></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       # hope for forbidden, due to user
       assert_response :forbidden,
                       "relation upload should have failed with forbidden"
@@ -236,7 +296,7 @@ module Api
       xml = "<osm><relation changeset='#{private_changeset.id}'>" \
             "<member  ref='#{node.id}' type='node' role='some'/>" \
             "<tag k='test' v='yes' /></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       # hope for forbidden due to user
       assert_response :forbidden,
                       "relation upload did not return forbidden status"
@@ -246,7 +306,7 @@ module Api
       # need a role attribute to be included
       xml = "<osm><relation changeset='#{private_changeset.id}'>" \
             "<member  ref='#{node.id}' type='node'/><tag k='test' v='yes' /></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       # hope for forbidden due to user
       assert_response :forbidden,
                       "relation upload did not return forbidden status"
@@ -257,7 +317,7 @@ module Api
             "<member type='node' ref='#{node.id}' role='some'/>" \
             "<member type='way' ref='#{way.id}' role='other'/>" \
             "<tag k='test' v='yes' /></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       # hope for forbidden, due to user
       assert_response :forbidden,
                       "relation upload did not return success status"
@@ -267,7 +327,7 @@ module Api
 
       # create an relation without members
       xml = "<osm><relation changeset='#{changeset.id}'><tag k='test' v='yes' /></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       # hope for success
       assert_response :success,
                       "relation upload did not return success status"
@@ -295,7 +355,7 @@ module Api
       xml = "<osm><relation changeset='#{changeset.id}'>" \
             "<member  ref='#{node.id}' type='node' role='some'/>" \
             "<tag k='test' v='yes' /></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       # hope for success
       assert_response :success,
                       "relation upload did not return success status"
@@ -323,7 +383,7 @@ module Api
       # need a role attribute to be included
       xml = "<osm><relation changeset='#{changeset.id}'>" \
             "<member  ref='#{node.id}' type='node'/><tag k='test' v='yes' /></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       # hope for success
       assert_response :success,
                       "relation upload did not return success status"
@@ -352,7 +412,7 @@ module Api
             "<member type='node' ref='#{node.id}' role='some'/>" \
             "<member type='way' ref='#{way.id}' role='other'/>" \
             "<tag k='test' v='yes' /></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       # hope for success
       assert_response :success,
                       "relation upload did not return success status"
@@ -472,7 +532,7 @@ module Api
       xml = "<osm><relation changeset='#{changeset.id}'>" \
             "<member type='node' ref='0'/><tag k='test' v='yes' />" \
             "</relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       # expect failure
       assert_response :precondition_failed,
                       "relation upload with invalid node did not return 'precondition failed'"
@@ -493,7 +553,7 @@ module Api
       xml = "<osm><relation changeset='#{changeset.id}'>" \
             "<member type='type' ref='#{node.id}' role=''/>" \
             "<tag k='tester' v='yep'/></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       # expect failure
       assert_response :bad_request
       assert_match(/Cannot parse valid relation from xml string/, @response.body)
@@ -504,7 +564,7 @@ module Api
     # Test deleting relations.
     # -------------------------------------
 
-    def test_delete
+    def test_destroy
       private_user = create(:user, :data_public => false)
       private_user_closed_changeset = create(:changeset, :closed, :user => private_user)
       user = create(:user)
@@ -757,7 +817,7 @@ module Api
       OSM
       doc = XML::Parser.string(doc_str).parse
 
-      put relation_create_path, :params => doc.to_s, :headers => auth_header
+      post api_relations_path, :params => doc.to_s, :headers => auth_header
       assert_response :success, "can't create a relation: #{@response.body}"
       relation_id = @response.body.to_i
 
@@ -818,13 +878,13 @@ module Api
       ## First try with the private user
       auth_header = bearer_authorization_header private_user
 
-      put relation_create_path, :params => doc.to_s, :headers => auth_header
+      post api_relations_path, :params => doc.to_s, :headers => auth_header
       assert_response :forbidden
 
       ## Now try with the public user
       auth_header = bearer_authorization_header user
 
-      put relation_create_path, :params => doc.to_s, :headers => auth_header
+      post api_relations_path, :params => doc.to_s, :headers => auth_header
       assert_response :success, "can't create a relation: #{@response.body}"
       relation_id = @response.body.to_i
 
@@ -857,7 +917,7 @@ module Api
       doc = XML::Parser.string(doc_str).parse
       auth_header = bearer_authorization_header user
 
-      put relation_create_path, :params => doc.to_s, :headers => auth_header
+      post api_relations_path, :params => doc.to_s, :headers => auth_header
       assert_response :success, "can't create a relation: #{@response.body}"
       relation_id = @response.body.to_i
 
@@ -929,7 +989,7 @@ module Api
             "<member  ref='#{node1.id}' type='node' role='some'/>" \
             "<member  ref='#{node2.id}' type='node' role='some'/>" \
             "<tag k='test' v='yes' /></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       assert_response :success, "relation create did not return success status"
 
       # get the id of the relation we created
@@ -953,7 +1013,7 @@ module Api
             "<member  ref='#{node1.id}' type='node' role='some'/>" \
             "<member  ref='#{node2.id}' type='node' role='some'/>" \
             "<tag k='test' v='yes' /></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       assert_response :too_many_requests, "relation create did not hit rate limit"
     end
 
@@ -989,7 +1049,7 @@ module Api
             "<member  ref='#{node1.id}' type='node' role='some'/>" \
             "<member  ref='#{node2.id}' type='node' role='some'/>" \
             "<tag k='test' v='yes' /></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       assert_response :success, "relation create did not return success status"
 
       # get the id of the relation we created
@@ -1013,7 +1073,7 @@ module Api
             "<member  ref='#{node1.id}' type='node' role='some'/>" \
             "<member  ref='#{node2.id}' type='node' role='some'/>" \
             "<tag k='test' v='yes' /></relation></osm>"
-      put relation_create_path, :params => xml, :headers => auth_header
+      post api_relations_path, :params => xml, :headers => auth_header
       assert_response :too_many_requests, "relation create did not hit rate limit"
     end
 
index 6aa4bdfa5d3b17173edfe8f7abbe8fa3f08c148a..2ff5e6f29f786a94b3c7080535fcc371ce9150d3 100644 (file)
@@ -6,16 +6,16 @@ module Api
     # test all routes which lead to this controller
     def test_routes
       assert_routing(
-        { :path => "/api/0.6/way/create", :method => :put },
-        { :controller => "api/ways", :action => "create" }
+        { :path => "/api/0.6/ways", :method => :get },
+        { :controller => "api/ways", :action => "index" }
       )
       assert_routing(
-        { :path => "/api/0.6/way/1/full", :method => :get },
-        { :controller => "api/ways", :action => "full", :id => "1" }
+        { :path => "/api/0.6/ways.json", :method => :get },
+        { :controller => "api/ways", :action => "index", :format => "json" }
       )
       assert_routing(
-        { :path => "/api/0.6/way/1/full.json", :method => :get },
-        { :controller => "api/ways", :action => "full", :id => "1", :format => "json" }
+        { :path => "/api/0.6/ways", :method => :post },
+        { :controller => "api/ways", :action => "create" }
       )
       assert_routing(
         { :path => "/api/0.6/way/1", :method => :get },
@@ -26,67 +26,26 @@ module Api
         { :controller => "api/ways", :action => "show", :id => "1", :format => "json" }
       )
       assert_routing(
-        { :path => "/api/0.6/way/1", :method => :put },
-        { :controller => "api/ways", :action => "update", :id => "1" }
+        { :path => "/api/0.6/way/1/full", :method => :get },
+        { :controller => "api/ways", :action => "full", :id => "1" }
       )
       assert_routing(
-        { :path => "/api/0.6/way/1", :method => :delete },
-        { :controller => "api/ways", :action => "delete", :id => "1" }
+        { :path => "/api/0.6/way/1/full.json", :method => :get },
+        { :controller => "api/ways", :action => "full", :id => "1", :format => "json" }
       )
       assert_routing(
-        { :path => "/api/0.6/ways", :method => :get },
-        { :controller => "api/ways", :action => "index" }
+        { :path => "/api/0.6/way/1", :method => :put },
+        { :controller => "api/ways", :action => "update", :id => "1" }
       )
       assert_routing(
-        { :path => "/api/0.6/ways.json", :method => :get },
-        { :controller => "api/ways", :action => "index", :format => "json" }
+        { :path => "/api/0.6/way/1", :method => :delete },
+        { :controller => "api/ways", :action => "destroy", :id => "1" }
       )
-    end
-
-    # -------------------------------------
-    # Test showing ways.
-    # -------------------------------------
-
-    def test_show
-      # check that a visible way is returned properly
-      get api_way_path(create(:way))
-      assert_response :success
-
-      # check that an invisible way is not returned
-      get api_way_path(create(:way, :deleted))
-      assert_response :gone
-
-      # check chat a non-existent way is not returned
-      get api_way_path(0)
-      assert_response :not_found
-    end
-
-    ##
-    # check the "full" mode
-    def test_full
-      way = create(:way_with_nodes, :nodes_count => 3)
-
-      get way_full_path(way)
-
-      assert_response :success
-
-      # Check the way is correctly returned
-      assert_select "osm way[id='#{way.id}'][version='1'][visible='true']", 1
-
-      # check that each node in the way appears once in the output as a
-      # reference and as the node element.
-      way.nodes.each do |n|
-        assert_select "osm way nd[ref='#{n.id}']", 1
-        assert_select "osm node[id='#{n.id}'][version='1'][lat='#{format('%<lat>.7f', :lat => n.lat)}'][lon='#{format('%<lon>.7f', :lon => n.lon)}']", 1
-      end
-    end
-
-    def test_full_deleted
-      way = create(:way, :deleted)
-
-      get way_full_path(way)
 
-      assert_response :gone
+      assert_recognizes(
+        { :controller => "api/ways", :action => "create" },
+        { :path => "/api/0.6/way/create", :method => :put }
+      )
     end
 
     ##
@@ -98,15 +57,15 @@ module Api
       way4 = create(:way)
 
       # check error when no parameter provided
-      get ways_path
+      get api_ways_path
       assert_response :bad_request
 
       # check error when no parameter value provided
-      get ways_path(:ways => "")
+      get api_ways_path(:ways => "")
       assert_response :bad_request
 
       # test a working call
-      get ways_path(:ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id}")
+      get api_ways_path(:ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id}")
       assert_response :success
       assert_select "osm" do
         assert_select "way", :count => 4
@@ -117,7 +76,7 @@ module Api
       end
 
       # test a working call with json format
-      get ways_path(:ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id}", :format => "json")
+      get api_ways_path(:ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id}", :format => "json")
 
       js = ActiveSupport::JSON.decode(@response.body)
       assert_not_nil js
@@ -129,10 +88,80 @@ module Api
       assert_equal 1, (js["elements"].count { |a| a["id"] == way4.id && a["visible"].nil? })
 
       # check error when a non-existent way is included
-      get ways_path(:ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id},0")
+      get api_ways_path(:ways => "#{way1.id},#{way2.id},#{way3.id},#{way4.id},0")
+      assert_response :not_found
+    end
+
+    # -------------------------------------
+    # Test showing ways.
+    # -------------------------------------
+
+    def test_show_not_found
+      get api_way_path(0)
       assert_response :not_found
     end
 
+    def test_show_deleted
+      get api_way_path(create(:way, :deleted))
+      assert_response :gone
+    end
+
+    def test_show
+      way = create(:way, :timestamp => "2021-02-03T00:00:00Z")
+      node = create(:node, :timestamp => "2021-04-05T00:00:00Z")
+      create(:way_node, :way => way, :node => node)
+
+      get api_way_path(way)
+
+      assert_response :success
+      assert_not_nil @response.header["Last-Modified"]
+      assert_equal "2021-02-03T00:00:00Z", Time.parse(@response.header["Last-Modified"]).utc.xmlschema
+    end
+
+    def test_show_json
+      way = create(:way_with_nodes, :nodes_count => 3)
+
+      get api_way_path(way, :format => "json")
+
+      assert_response :success
+
+      js = ActiveSupport::JSON.decode(@response.body)
+      assert_not_nil js
+      assert_equal 1, js["elements"].count
+      js_ways = js["elements"].filter { |e| e["type"] == "way" }
+      assert_equal 1, js_ways.count
+      assert_equal way.id, js_ways[0]["id"]
+      assert_equal 1, js_ways[0]["version"]
+    end
+
+    ##
+    # check the "full" mode
+    def test_full
+      way = create(:way_with_nodes, :nodes_count => 3)
+
+      get way_full_path(way)
+
+      assert_response :success
+
+      # Check the way is correctly returned
+      assert_select "osm way[id='#{way.id}'][version='1'][visible='true']", 1
+
+      # check that each node in the way appears once in the output as a
+      # reference and as the node element.
+      way.nodes.each do |n|
+        assert_select "osm way nd[ref='#{n.id}']", 1
+        assert_select "osm node[id='#{n.id}'][version='1'][lat='#{format('%<lat>.7f', :lat => n.lat)}'][lon='#{format('%<lon>.7f', :lon => n.lon)}']", 1
+      end
+    end
+
+    def test_full_deleted
+      way = create(:way, :deleted)
+
+      get way_full_path(way)
+
+      assert_response :gone
+    end
+
     # -------------------------------------
     # Test simple way creation.
     # -------------------------------------
@@ -155,7 +184,7 @@ module Api
       xml = "<osm><way changeset='#{changeset_id}'>" \
             "<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
             "<tag k='test' v='yes' /></way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       # hope for failure
       assert_response :forbidden,
                       "way upload did not return forbidden status"
@@ -170,7 +199,7 @@ module Api
       xml = "<osm><way changeset='#{changeset_id}'>" \
             "<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
             "<tag k='test' v='yes' /></way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       # hope for success
       assert_response :success,
                       "way upload did not return success status"
@@ -213,7 +242,7 @@ module Api
       # create a way with non-existing node
       xml = "<osm><way changeset='#{private_open_changeset.id}'>" \
             "<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       # expect failure
       assert_response :forbidden,
                       "way upload with invalid node using a private user did not return 'forbidden'"
@@ -221,7 +250,7 @@ module Api
       # create a way with no nodes
       xml = "<osm><way changeset='#{private_open_changeset.id}'>" \
             "<tag k='test' v='yes' /></way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       # expect failure
       assert_response :forbidden,
                       "way upload with no node using a private userdid not return 'forbidden'"
@@ -229,7 +258,7 @@ module Api
       # create a way inside a closed changeset
       xml = "<osm><way changeset='#{private_closed_changeset.id}'>" \
             "<nd ref='#{node.id}'/></way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       # expect failure
       assert_response :forbidden,
                       "way upload to closed changeset with a private user did not return 'forbidden'"
@@ -241,7 +270,7 @@ module Api
       # create a way with non-existing node
       xml = "<osm><way changeset='#{open_changeset.id}'>" \
             "<nd ref='0'/><tag k='test' v='yes' /></way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       # expect failure
       assert_response :precondition_failed,
                       "way upload with invalid node did not return 'precondition failed'"
@@ -250,7 +279,7 @@ module Api
       # create a way with no nodes
       xml = "<osm><way changeset='#{open_changeset.id}'>" \
             "<tag k='test' v='yes' /></way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       # expect failure
       assert_response :precondition_failed,
                       "way upload with no node did not return 'precondition failed'"
@@ -259,7 +288,7 @@ module Api
       # create a way inside a closed changeset
       xml = "<osm><way changeset='#{closed_changeset.id}'>" \
             "<nd ref='#{node.id}'/></way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       # expect failure
       assert_response :conflict,
                       "way upload to closed changeset did not return 'conflict'"
@@ -269,7 +298,7 @@ module Api
             "<nd ref='#{node.id}'/>" \
             "<tag k='foo' v='#{'x' * 256}'/>" \
             "</way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       # expect failure
       assert_response :bad_request,
                       "way upload to with too long tag did not return 'bad_request'"
@@ -279,7 +308,7 @@ module Api
     # Test deleting ways.
     # -------------------------------------
 
-    def test_delete
+    def test_destroy
       private_user = create(:user, :data_public => false)
       private_open_changeset = create(:changeset, :user => private_user)
       private_closed_changeset = create(:changeset, :closed, :user => private_user)
@@ -696,7 +725,7 @@ module Api
       way_str << "</way></osm>"
 
       # try and upload it
-      put way_create_path, :params => way_str, :headers => auth_header
+      post api_ways_path, :params => way_str, :headers => auth_header
       assert_response :forbidden,
                       "adding new duplicate tags to a way with a non-public user should fail with 'forbidden'"
 
@@ -711,7 +740,7 @@ module Api
       way_str << "</way></osm>"
 
       # try and upload it
-      put way_create_path, :params => way_str, :headers => auth_header
+      post api_ways_path, :params => way_str, :headers => auth_header
       assert_response :bad_request,
                       "adding new duplicate tags to a way should fail with 'bad request'"
       assert_equal "Element way/ has duplicate tags with key addr:housenumber", @response.body
@@ -775,7 +804,7 @@ module Api
       xml = "<osm><way changeset='#{changeset.id}'>" \
             "<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
             "<tag k='test' v='yes' /></way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       assert_response :success, "way create did not return success status"
 
       # get the id of the way we created
@@ -797,7 +826,7 @@ module Api
       xml = "<osm><way changeset='#{changeset.id}'>" \
             "<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
             "<tag k='test' v='yes' /></way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       assert_response :too_many_requests, "way create did not hit rate limit"
     end
 
@@ -832,7 +861,7 @@ module Api
       xml = "<osm><way changeset='#{changeset.id}'>" \
             "<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
             "<tag k='test' v='yes' /></way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       assert_response :success, "way create did not return success status"
 
       # get the id of the way we created
@@ -854,7 +883,7 @@ module Api
       xml = "<osm><way changeset='#{changeset.id}'>" \
             "<nd ref='#{node1.id}'/><nd ref='#{node2.id}'/>" \
             "<tag k='test' v='yes' /></way></osm>"
-      put way_create_path, :params => xml, :headers => auth_header
+      post api_ways_path, :params => xml, :headers => auth_header
       assert_response :too_many_requests, "way create did not hit rate limit"
     end
 
index a961ea80f7a54099597920e2073c0055a573cad9..e26ae89ac6556cbec289f25e52ed445ac98e33b6 100644 (file)
@@ -114,15 +114,15 @@ class IssuesTest < ApplicationSystemTestCase
     assert_equal("test comment", issue.comments.first.body)
   end
 
-  def test_reassign_issue
-    issue = create(:issue)
-    assert_equal "administrator", issue.assigned_role
+  def test_reassign_issue_to_moderators
+    issue = create(:issue, :assigned_role => "administrator")
     sign_in_as(create(:administrator_user))
 
     visit issue_path(issue)
 
+    assert_unchecked_field "Reassign Issue to Moderators"
     fill_in :issue_comment_body, :with => "reassigning to moderators"
-    check :reassign
+    check "Reassign Issue to Moderators"
     click_on "Add Comment"
 
     assert_content "and the issue was reassigned"
@@ -132,6 +132,24 @@ class IssuesTest < ApplicationSystemTestCase
     assert_equal "moderator", issue.assigned_role
   end
 
+  def test_reassign_issue_to_administrators
+    issue = create(:issue, :assigned_role => "moderator")
+    sign_in_as(create(:moderator_user))
+
+    visit issue_path(issue)
+
+    assert_unchecked_field "Reassign Issue to Administrators"
+    fill_in :issue_comment_body, :with => "reassigning to administrators"
+    check "Reassign Issue to Administrators"
+    click_on "Add Comment"
+
+    assert_content "and the issue was reassigned"
+    assert_current_path issues_path(:status => "open")
+
+    issue.reload
+    assert_equal "administrator", issue.assigned_role
+  end
+
   def test_reassign_issue_as_super_user
     issue = create(:issue)
     sign_in_as(create(:super_user))
@@ -139,7 +157,7 @@ class IssuesTest < ApplicationSystemTestCase
     visit issue_path(issue)
 
     fill_in :issue_comment_body, :with => "reassigning to moderators"
-    check :reassign
+    check "Reassign Issue to Moderators"
     click_on "Add Comment"
 
     assert_content "and the issue was reassigned"
index c9bf043b1f19ad2c806c758e5c4e83f6df3af434..c15c97e29b3c446b7835d4e34d130e056b94bc7b 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
   resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b"
   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==
+  dependencies:
+    eslint-visitor-keys "^4.2.0"
+    espree "^10.3.0"
+
 "@types/estree@^1.0.6":
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
@@ -217,6 +225,11 @@ eslint-formatter-compact@^8.40.0:
   resolved "https://registry.yarnpkg.com/eslint-formatter-compact/-/eslint-formatter-compact-8.40.0.tgz#d7455b2d75fd70e8c0e7a98a5e189f168e9dfe2d"
   integrity sha512-cwGUs113TgmTQXecx5kfRjB7m0y2wkDLSadPTE2pK6M/wO4N8PjmUaoWOFNCP9MHgsiZwgqd5bZFnDCnszC56Q==
 
+eslint-plugin-erb@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-erb/-/eslint-plugin-erb-2.1.1.tgz#8a0a6c2bcaf3a8573c381b595969145aff93cfc6"
+  integrity sha512-AhznaVwRpQqR8NADjN4SZnKNbaIdAbGxTjCg6cj3UhwGyQOUJ6kXwhYrl1LYrGDNx7Ouyd8xuEG7wepFZyPgFw==
+
 eslint-scope@^8.2.0:
   version "8.2.0"
   resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442"