Просмотр исходного кода

Fixes #5078 - Geo Location Service does not work.

Dusan Vuckovic 11 месяцев назад
Родитель
Сommit
91292e93a4

+ 16 - 0
db/migrate/20240320115157_geo_location_backend_osm.rb

@@ -0,0 +1,16 @@
+# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+
+class GeoLocationBackendOsm < ActiveRecord::Migration[7.0]
+  def change
+    # return if it's a new setup
+    return if !Setting.exists?(name: 'system_init_done')
+
+    Setting.find_by(name: 'geo_location_backend')&.tap do |setting|
+      setting.state_current = { 'value' => 'Service::GeoLocation::Osm' } if setting.state_current['value'] == 'Service::GeoLocation::Gmaps'
+      setting.state_initial = { 'value' => 'Service::GeoLocation::Osm' }
+      setting.options['form'][0]['options'].delete('Service::GeoLocation::Gmaps')
+      setting.options['form'][0]['options']['Service::GeoLocation::Osm'] = 'OpenStreetMap (ODbL 1.0, http://osm.org/copyright)'
+      setting.save!
+    end
+  end
+end

+ 3 - 3
db/seeds/settings.rb

@@ -435,13 +435,13 @@ Setting.create_if_not_exists(
         name:    'geo_location_backend',
         tag:     'select',
         options: {
-          ''                            => '-',
-          'Service::GeoLocation::Gmaps' => __('Google Maps'),
+          ''                          => '-',
+          'Service::GeoLocation::Osm' => __('OpenStreetMap (ODbL 1.0, http://osm.org/copyright)'),
         },
       },
     ],
   },
-  state:       'Service::GeoLocation::Gmaps',
+  state:       'Service::GeoLocation::Osm',
   preferences: {
     prio:       3,
     permission: ['admin.system'],

+ 4 - 4
i18n/zammad.pot

@@ -6040,10 +6040,6 @@ msgstr ""
 msgid "Google Client Secret"
 msgstr ""
 
-#: db/seeds/settings.rb
-msgid "Google Maps"
-msgstr ""
-
 #: app/assets/javascripts/app/controllers/ticket_zoom/highlighter.coffee
 msgid "Green"
 msgstr ""
@@ -8962,6 +8958,10 @@ msgstr ""
 msgid "Open your authenticator app and scan the QR code below:"
 msgstr ""
 
+#: db/seeds/settings.rb
+msgid "OpenStreetMap (ODbL 1.0, http://osm.org/copyright)"
+msgstr ""
+
 #: app/frontend/apps/mobile/pages/ticket/components/TicketDetailView/TicketViewersDialog.vue
 msgid "Opened in tabs"
 msgstr ""

+ 11 - 9
lib/service/geo_location/gmaps.rb → lib/service/geo_location/osm.rb

@@ -1,9 +1,11 @@
 # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
 
-class Service::GeoLocation::Gmaps
+class Service::GeoLocation::Osm
+  OSM_SEARCH_URL = 'https://nominatim.openstreetmap.org/search?q=%s&format=jsonv2'.freeze
+  OSM_REVERSE_URL = 'https://nominatim.openstreetmap.org/reverse?lat=%s&lon=%s&format=jsonv2'.freeze
 
   def self.geocode(address)
-    url = "http://maps.googleapis.com/maps/api/geocode/json?address=#{CGI.escape address}&sensor=true"
+    url = format(OSM_SEARCH_URL, CGI.escape(address))
     response = UserAgent.get(
       url,
       {},
@@ -18,17 +20,16 @@ class Service::GeoLocation::Gmaps
 
     result = JSON.parse(response.body)
 
-    return if !result
-    return if !result['results']
-    return if !result['results'].first
+    return if !result || !result.first
+
+    lat = result.first['lat'].to_f
+    lng = result.first['lon'].to_f
 
-    lat = result['results'].first['geometry']['location']['lat']
-    lng = result['results'].first['geometry']['location']['lng']
     [lat, lng]
   end
 
   def self.reverse_geocode(lat, lng)
-    url = "http://maps.googleapis.com/maps/api/geocode/json?latlng=#{lat},#{lng}&sensor=true"
+    url = format(OSM_REVERSE_URL, lat, lng)
     response = UserAgent.get(
       url,
       {},
@@ -44,7 +45,8 @@ class Service::GeoLocation::Gmaps
 
     result = JSON.parse(response.body)
 
-    result['results'].first['address_components'].first['long_name']
+    return if !result
 
+    result['display_name']
   end
 end

+ 3 - 1
lib/whatsapp/webhook/message/location.rb

@@ -1,10 +1,12 @@
 # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
 
 class Whatsapp::Webhook::Message::Location < Whatsapp::Webhook::Message
+  OSM_MARKER_URL = 'https://www.openstreetmap.org/?mlat=%s&mlon=%s#map=17/%s/%s'.freeze
+
   private
 
   def body
-    "📍 <a href='https://www.google.com/maps/search/?api=1&query=#{message[:latitude]},#{message[:longitude]}' target='_blank'>#{message[:name] || 'Location'}</a>"
+    "📍 <a href='#{format(OSM_MARKER_URL, message[:latitude], message[:longitude], message[:latitude], message[:longitude])}' target='_blank'>#{message[:name] || 'Location'}</a>"
   end
 
   def content_type

+ 45 - 0
spec/db/migrate/geo_location_backend_osm_spec.rb

@@ -0,0 +1,45 @@
+# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+
+require 'rails_helper'
+
+RSpec.describe GeoLocationBackendOsm, type: :db_migration do
+  let(:setting)       { Setting.find_by(name: 'geo_location_backend') }
+  let(:current_value) { 'Service::GeoLocation::Gmaps' }
+
+  def revert_setting
+    setting.state_current = { 'value' => current_value }
+    setting.state_initial = { 'value' => 'Service::GeoLocation::Gmaps' }
+    setting.options['form'][0]['options'].delete('Service::GeoLocation::Osm')
+    setting.options['form'][0]['options']['Service::GeoLocation::Gmaps'] = 'Google Maps'
+    setting.save!
+  end
+
+  before do
+    revert_setting
+    migrate
+  end
+
+  it 'migrates the setting' do
+    expect(setting.reload).to have_attributes(
+      state_current: { 'value' => 'Service::GeoLocation::Osm' },
+      state_initial: { 'value' => 'Service::GeoLocation::Osm' },
+      options:       {
+        'form' => [
+          include('options' => include(
+            'Service::GeoLocation::Osm' => 'OpenStreetMap (ODbL 1.0, http://osm.org/copyright)',
+          ))
+        ]
+      }
+    ).and have_attributes(options: { 'form' => [include('options' => not_include('Service::GeoLocation::Gmaps' => 'Google Maps'))] })
+  end
+
+  context 'with non-default value' do
+    let(:current_value) { '' }
+
+    it 'does not change setting value' do
+      expect(setting.reload).to have_attributes(
+        state_current: { 'value' => '' },
+      )
+    end
+  end
+end

+ 55 - 0
spec/lib/service/geo_location/osm_spec.rb

@@ -0,0 +1,55 @@
+# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+
+require 'rails_helper'
+
+RSpec.describe Service::GeoLocation::Osm, :integration, use_vcr: true do
+  describe '#geocode' do
+    subject(:geocode) { described_class.geocode(address) }
+
+    context 'with a german address' do
+      let(:address) { 'Marienstrasse 13, 10117 Berlin' }
+      let(:result)  { [52.5220514, 13.3832091] }
+
+      it { is_expected.to eq(result) }
+
+      context 'without a separator between street, zipcode and city' do
+        let(:address) { 'Marienstrasse 13 10117 Berlin' }
+
+        it { is_expected.to eq(result) }
+      end
+    end
+
+    context 'with a swiss address' do
+      let(:address) { 'Martinsbruggstrasse 35, 9016 St. Gallen' }
+      let(:result)  { [47.43664765, 9.409934047751209] }
+
+      it { is_expected.to eq(result) }
+
+      context 'without a separator between street, zipcode and city' do
+        let(:address) { 'Martinsbruggstrasse 35 9016 St. Gallen' }
+
+        it { is_expected.to eq(result) }
+      end
+    end
+  end
+
+  describe '#reverse_geocode' do
+    subject(:reverse_geocode) { described_class.reverse_geocode(latitude, longitude) }
+
+    context 'with german coordinates' do
+      let(:latitude)  { 52.5220514 }
+      let(:longitude) { 13.3832091 }
+      let(:result)    { '13, Marienstraße, Dorotheenstadt, Mitte, Berlin, 10117, Deutschland' }
+
+      it { is_expected.to eq(result) }
+    end
+
+    context 'with swiss coordinates' do
+      let(:latitude)  { 47.43664765 }
+      let(:longitude) { 9.409934047751209 }
+      let(:result)    { '35, Martinsbruggstrasse, Neudorf, St. Gallen, Wahlkreis St. Gallen, St. Gallen, 9016, Schweiz/Suisse/Svizzera/Svizra' }
+
+      it { is_expected.to eq(result) }
+    end
+  end
+end

+ 27 - 89
spec/lib/service/geo_location_spec.rb

@@ -2,121 +2,59 @@
 
 require 'rails_helper'
 
-RSpec.describe Service::GeoLocation, integration: true, retry: 5, retry_wait: 30.seconds do
+RSpec.describe Service::GeoLocation do
   describe '#geocode' do
-    subject(:lookup_result) { described_class.geocode(address) }
+    subject(:geocode) { described_class.geocode(address) }
 
     context 'when checking simple results' do
-      let(:expected_result) { [ latitude, longitude ] }
-      let(:request_url)     { "http://maps.googleapis.com/maps/api/geocode/json?address=#{CGI.escape(address)}&sensor=true" }
-      let(:response_payload) do
-        {
-          'results' => [
-            {
-              'geometry' => {
-                'location' => {
-                  'lat' => latitude,
-                  'lng' => longitude,
-                },
-              },
-            },
-          ],
-        }
-      end
+      let(:address)   { 'Marienstrasse 13, 10117 Berlin' }
+      let(:latitude)  { 52.5220514 }
+      let(:longitude) { 13.3832091 }
+      let(:result)    { [latitude, longitude] }
 
       before do
-        stub_request(:get, request_url).to_return(status: 200, body: response_payload.to_json, headers: {})
+        allow(Service::GeoLocation::Osm).to receive(:geocode).and_return(result)
       end
 
-      context 'with German addresses' do
-        let(:address) { 'Marienstrasse 13, 10117 Berlin' }
-        let(:latitude)  { 52.5219143 }
-        let(:longitude) { 13.3832647 }
-
-        it { is_expected.to eq(expected_result) }
-
-        context 'without separator between street and zipcode + city' do
-          let(:address) { 'Marienstrasse 13 10117 Berlin' }
-
-          it { is_expected.to eq(expected_result) }
-        end
-
-        context 'when address field in user preferences is filled' do
-          let(:user) { create(:user, address: address) }
-
-          it 'stores correct values for latitude + longitude' do
-            expect(user.preferences).to include({ 'lat' => latitude, 'lng' => longitude })
-          end
-        end
-
-        context 'when street, city and zip fields in user preferences are filled' do
-          let(:address) { 'Marienstrasse 13, 10117, Berlin' }
-          let(:address_parts) { address.split(%r{\.?\s+}, 4) }
-          let(:street)        { "#{address_parts.first} #{address_parts[1].chop}" }
-          let(:zip)           { address_parts[2].chop }
-          let(:city)          { address_parts.last }
+      it { is_expected.to eq(result) }
 
-          let(:user) { create(:user, street: street, zip: zip, city: city) }
+      context 'when address field in user preferences is filled' do
+        let(:user) { create(:user, address: address) }
 
-          it 'stores correct values for latitude + longitude' do
-            expect(user.preferences).to include({ 'lat' => latitude, 'lng' => longitude })
-          end
+        it 'stores correct values for latitude + longitude' do
+          expect(user.preferences).to include({ 'lat' => latitude, 'lng' => longitude })
         end
       end
 
-      context 'with Swiss addresses' do
-        let(:address)   { 'Martinsbruggstrasse 35, 9016 St. Gallen' }
-        let(:latitude)  { 47.4366557 }
-        let(:longitude) { 9.4098904 }
-
-        it { is_expected.to eq(expected_result) }
+      context 'when street, city and zip fields in user preferences are filled' do
+        let(:address)       { 'Marienstrasse 13, 10117, Berlin' }
+        let(:address_parts) { address.split(%r{\.?\s+}, 4) }
+        let(:street)        { "#{address_parts.first} #{address_parts[1].chop}" }
+        let(:zip)           { address_parts[2].chop }
+        let(:city)          { address_parts.last }
 
-        context 'without separator between street and zipcode + city' do
-          let(:address) { 'Martinsbruggstrasse 35 9016 St. Gallen' }
+        let(:user) { create(:user, street: street, zip: zip, city: city) }
 
-          it { is_expected.to eq(expected_result) }
+        it 'stores correct values for latitude + longitude' do
+          expect(user.preferences).to include({ 'lat' => latitude, 'lng' => longitude })
         end
       end
     end
   end
 
   describe '#reverse_geocode' do
-    subject(:lookup_result) { described_class.reverse_geocode(latitude, longitude) }
+    subject(:reverse_geocode) { described_class.reverse_geocode(latitude, longitude) }
 
     context 'when checking simple results' do
-      let(:expected_result) { address }
-      let(:request_url)     { "http://maps.googleapis.com/maps/api/geocode/json?latlng=#{latitude},#{longitude}&sensor=true" }
-      let(:response_payload) do
-        {
-          'results' => [
-            {
-              'address_components' => [
-                'long_name' => address,
-              ],
-            },
-          ],
-        }
-      end
+      let(:latitude)        { 52.5220514 }
+      let(:longitude)       { 13.3832091 }
+      let(:result)          { '13, Marienstraße, Dorotheenstadt, Mitte, Berlin, 10117, Deutschland' }
 
       before do
-        stub_request(:get, request_url).to_return(status: 200, body: response_payload.to_json, headers: {})
-      end
-
-      context 'with German addresses' do
-        let(:address) { 'Marienstrasse 13, 10117 Berlin' }
-        let(:latitude)  { 52.5219143 }
-        let(:longitude) { 13.3832647 }
-
-        it { is_expected.to eq(expected_result) }
+        allow(Service::GeoLocation::Osm).to receive(:reverse_geocode).and_return(result)
       end
 
-      context 'with Swiss addresses' do
-        let(:address) { 'Martinsbruggstrasse 35, 9016 St. Gallen' }
-        let(:latitude)  { 47.4366557 }
-        let(:longitude) { 9.4098904 }
-
-        it { is_expected.to eq(expected_result) }
-      end
+      it { is_expected.to eq(result) }
     end
   end
 end

+ 1 - 1
spec/lib/whatsapp/webhook/message/location_spec.rb

@@ -78,7 +78,7 @@ RSpec.describe Whatsapp::Webhook::Message::Location, :aggregate_failures, curren
         )
 
         expect(Ticket::Article.second_to_last.body).to include('Langenbach Arena')
-          .and include('https://www.google.com/maps')
+          .and include('https://www.openstreetmap.org')
           .and include('50.697254180908')
           .and include('7.9327116012573')
 

Разница между файлами не показана из-за своего большого размера
+ 37 - 0
test/data/vcr_cassettes/lib/service/geo_location/osm/service_geolocation_osm_geocode_with_a_german_address_example_at_spec_lib_service_geo_location_osm_spec_rb_13.yml


Некоторые файлы не были показаны из-за большого количества измененных файлов