katalyst-google-apis 1.2.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6b30a2c9af798f8d02b09f74e4c9e080daf199723b644ba70e79f2fb7038ac6
4
- data.tar.gz: f3936fbcb87a624e5564c882a25871c06426ba6224441c9d5bfb11470d75ff25
3
+ metadata.gz: 249511e97a7e28426cbf5ad750c3c77c1dc5f882c885a37868537bfea975a9e7
4
+ data.tar.gz: 86758221a5cdc2a554d767a7e126b1acd48d3034b87100e23dd88876e291e713
5
5
  SHA512:
6
- metadata.gz: bf8944b1d3888a5d25d66e65bd3737602b90b6890a138a7e5a941bdef954054379f10233309e71a354e51d31cc298a10bd21b1e16aada8439d4c146483dec100
7
- data.tar.gz: 84d17ab4cea53c9686b4c9c04faa8fcf3ad5031ec5b72ec817dbd9972c8f1e732f7c9c87f606143f60c24534fe98f37dd4e829d9b382890bfa4572b1d91761d3
6
+ metadata.gz: da31ead0972e8152ab79e04c2a18ed875dcc367187f8ee03a60f7d89337d85956f95f944f524852eddd8fbe940da4e45884bd39dae471d499134a19faaccea59
7
+ data.tar.gz: ece80e3217d17d22c28f8b787c10ad843200b3c322b6dd1427b2a13ef3bdcbd4df484adeccef53ef83c5d12d104bbfc012fd0a49e39a5834e4a7dda7b71e393a
@@ -7,7 +7,8 @@ module Katalyst
7
7
  class GenerateContentService
8
8
  attr_reader :response, :result, :error, :content_text
9
9
 
10
- def self.call(parent:, model:, payload:, credentials: Katalyst::GoogleApis.credentials,
10
+ def self.call(parent:, model:, payload:,
11
+ credentials: Katalyst::GoogleApis.credentials,
11
12
  retries: 5,
12
13
  jitter: 1_000)
13
14
  new(parent:, model:, credentials:, attempt: 0, retries:, jitter:).call(payload:)
@@ -45,22 +46,9 @@ module Katalyst
45
46
  end
46
47
 
47
48
  self
48
- rescue GoogleApis::Error => e
49
- @error = e
50
- if e.code == 429 && @attempt < @retries
51
- Kernel.sleep(backoff)
52
-
53
- @response = nil
54
- @result = nil
55
- @error = nil
56
- @attempt += 1
57
- retry
58
- else
59
- raise e
60
- end
61
49
  rescue StandardError => e
62
50
  @error = e
63
- raise e
51
+ retry?(e) ? retry : raise
64
52
  ensure
65
53
  report_error
66
54
  end
@@ -102,12 +90,26 @@ module Katalyst
102
90
  data: {
103
91
  url:,
104
92
  method: "POST",
105
- status_code: error.code,
93
+ status_code: error.try(:code),
106
94
  reason: error.message,
107
95
  },
108
96
  )
109
97
  end
110
98
 
99
+ def retry?(error)
100
+ if error.is_a?(GoogleApis::Error) && error.code == 429 && @attempt < @retries
101
+ Kernel.sleep(backoff)
102
+
103
+ @response = nil
104
+ @result = nil
105
+ @error = nil
106
+ @attempt += 1
107
+ true
108
+ else
109
+ false
110
+ end
111
+ end
112
+
111
113
  def backoff
112
114
  [@jitter, response_headers.fetch(:retry_after, 0).to_i * 1000].max + (@jitter * rand)
113
115
  end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "curb"
4
+
5
+ module Katalyst
6
+ module GoogleApis
7
+ module Geocoding
8
+ # Use Google Maps Geocoding API to find a location from lat/lng.
9
+ class ReverseService
10
+ attr_reader :response, :result, :error
11
+
12
+ def self.scope
13
+ "https://www.googleapis.com/auth/maps-platform.geocode.location"
14
+ end
15
+
16
+ def self.call(latlng:, credentials: Katalyst::GoogleApis.credentials(scope:))
17
+ new(credentials:).call(latlng:)
18
+ end
19
+
20
+ def initialize(credentials:)
21
+ @credentials = credentials
22
+ end
23
+
24
+ def call(latlng:)
25
+ @latlng = latlng
26
+
27
+ @response = Curl.get(url) do |http|
28
+ http.headers["Content-Type"] = "application/json; UTF-8"
29
+ @credentials.apply!(http.headers)
30
+ end
31
+
32
+ if %r{^application/json}.match?(@response.content_type)
33
+ @result = JSON.parse(response.body, symbolize_names: true)
34
+ else
35
+ raise GoogleApis::Error.new(
36
+ code: response.response_code,
37
+ status: Rack::Utils::HTTP_STATUS_CODES[response.response_code],
38
+ message: "Unexpected HTTP response received (#{response.response_code}, #{@response.content_type})",
39
+ )
40
+ end
41
+
42
+ if result[:error].present?
43
+ api_error = result.fetch(:error)
44
+
45
+ raise GoogleApis::Error.new(
46
+ code: api_error.fetch(:code, response.response_code),
47
+ status: api_error.fetch(:status, Rack::Utils::HTTP_STATUS_CODES[response.response_code]),
48
+ message: api_error.fetch(:message, "Unexpected API error"),
49
+ details: api_error.fetch(:details, nil),
50
+ )
51
+ end
52
+
53
+ self
54
+ rescue StandardError => e
55
+ @error = e
56
+ raise
57
+ ensure
58
+ report_error
59
+ end
60
+
61
+ def locations
62
+ @result&.fetch(:results, nil)
63
+ end
64
+
65
+ def first_location
66
+ locations&.first
67
+ end
68
+
69
+ def formatted_address
70
+ first_location&.dig(:formattedAddress)
71
+ end
72
+
73
+ def latlng
74
+ location = first_location&.dig(:location)
75
+ return if location.blank?
76
+
77
+ latitude = location[:latitude]
78
+ longitude = location[:longitude]
79
+ return if latitude.nil? || longitude.nil?
80
+
81
+ [latitude, longitude].join(",")
82
+ end
83
+
84
+ def inspect
85
+ "#<#{self.class.name} result: #{@result.inspect} error: #{@error.inspect}>"
86
+ end
87
+
88
+ private
89
+
90
+ def url
91
+ "https://geocode.googleapis.com/v4beta/geocode/location/#{@latlng}"
92
+ end
93
+
94
+ def report_error
95
+ return if error.nil?
96
+
97
+ if defined?(Sentry)
98
+ Sentry.add_breadcrumb(sentry_breadcrumb(error))
99
+ else
100
+ Rails.logger.error(error)
101
+ end
102
+ end
103
+
104
+ def sentry_breadcrumb(error)
105
+ Sentry::Breadcrumb.new(
106
+ type: "http",
107
+ category: "geocode",
108
+ data: {
109
+ url:,
110
+ method: "GET",
111
+ status_code: error.try(:code),
112
+ reason: error.message,
113
+ },
114
+ )
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "curb"
4
+
5
+ module Katalyst
6
+ module GoogleApis
7
+ module Geocoding
8
+ # Use Google Maps Geocoding API to find a location from an address.
9
+ class SearchService
10
+ attr_reader :response, :result, :error
11
+
12
+ def self.scope
13
+ "https://www.googleapis.com/auth/maps-platform.geocode.address"
14
+ end
15
+
16
+ def self.call(address:, bounds:, credentials: Katalyst::GoogleApis.credentials(scope:))
17
+ new(credentials:).call(address:, bounds:)
18
+ end
19
+
20
+ def initialize(credentials:)
21
+ @credentials = credentials
22
+ end
23
+
24
+ def call(address:, bounds:)
25
+ @address = address
26
+ @bounds = bounds
27
+
28
+ @response = Curl.get(url, **params) do |http|
29
+ http.headers["Content-Type"] = "application/json; UTF-8"
30
+ @credentials.apply!(http.headers)
31
+ end
32
+
33
+ if %r{^application/json}.match?(@response.content_type)
34
+ @result = JSON.parse(response.body, symbolize_names: true)
35
+ else
36
+ raise GoogleApis::Error.new(
37
+ code: response.response_code,
38
+ status: Rack::Utils::HTTP_STATUS_CODES[response.response_code],
39
+ message: "Unexpected HTTP response received (#{response.response_code}, #{@response.content_type})",
40
+ )
41
+ end
42
+
43
+ if result[:error].present?
44
+ api_error = result.fetch(:error)
45
+
46
+ raise GoogleApis::Error.new(
47
+ code: api_error.fetch(:code, response.response_code),
48
+ status: api_error.fetch(:status, Rack::Utils::HTTP_STATUS_CODES[response.response_code]),
49
+ message: api_error.fetch(:message, "Unexpected API error"),
50
+ details: api_error.fetch(:details, nil),
51
+ )
52
+ end
53
+
54
+ self
55
+ rescue StandardError => e
56
+ @error = e
57
+ raise
58
+ ensure
59
+ report_error
60
+ end
61
+
62
+ def locations
63
+ @result&.fetch(:results, nil)
64
+ end
65
+
66
+ def first_location
67
+ locations&.first
68
+ end
69
+
70
+ def formatted_address
71
+ first_location&.dig(:formattedAddress)
72
+ end
73
+
74
+ def latlng
75
+ location = first_location&.dig(:location)
76
+ return if location.blank?
77
+
78
+ latitude = location[:latitude]
79
+ longitude = location[:longitude]
80
+ return if latitude.nil? || longitude.nil?
81
+
82
+ [latitude, longitude].join(",")
83
+ end
84
+
85
+ def inspect
86
+ "#<#{self.class.name} result: #{@result.inspect} error: #{@error.inspect}>"
87
+ end
88
+
89
+ private
90
+
91
+ def url
92
+ "https://geocode.googleapis.com/v4beta/geocode/address/#{URI.encode_uri_component(@address)}"
93
+ end
94
+
95
+ def params
96
+ low, high = @bounds.split("|")
97
+
98
+ low_lat, low_lng = low.split(",")
99
+ high_lat, high_lng = high.split(",")
100
+
101
+ {
102
+ "locationBias.rectangle.low.latitude" => low_lat,
103
+ "locationBias.rectangle.low.longitude" => low_lng,
104
+ "locationBias.rectangle.high.latitude" => high_lat,
105
+ "locationBias.rectangle.high.longitude" => high_lng,
106
+ }
107
+ end
108
+
109
+ def report_error
110
+ return if error.nil?
111
+
112
+ if defined?(Sentry)
113
+ Sentry.add_breadcrumb(sentry_breadcrumb(error))
114
+ else
115
+ Rails.logger.error(error)
116
+ end
117
+ end
118
+
119
+ def sentry_breadcrumb(error)
120
+ Sentry::Breadcrumb.new(
121
+ type: "http",
122
+ category: "geocode",
123
+ data: {
124
+ url:,
125
+ method: "GET",
126
+ status_code: error.try(:code),
127
+ reason: error.message,
128
+ },
129
+ )
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -14,7 +14,7 @@ module Katalyst
14
14
 
15
15
  def initialize(credentials:, parent:)
16
16
  @credentials = credentials
17
- @parent = parent
17
+ @parent = parent
18
18
  end
19
19
 
20
20
  def call(assessment:)
@@ -84,7 +84,7 @@ module Katalyst
84
84
  data: {
85
85
  url:,
86
86
  method: "POST",
87
- status_code: error.code,
87
+ status_code: error.try(:code),
88
88
  reason: error.message,
89
89
  },
90
90
  )
data/config/boot.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
4
+
5
+ require "bundler/setup" # Set up gems listed in the Gemfile.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katalyst-google-apis
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katalyst Interactive
@@ -79,8 +79,11 @@ files:
79
79
  - app/services/katalyst/google_apis/credentials.rb
80
80
  - app/services/katalyst/google_apis/error.rb
81
81
  - app/services/katalyst/google_apis/gemini/generate_content_service.rb
82
+ - app/services/katalyst/google_apis/geocoding/reverse_service.rb
83
+ - app/services/katalyst/google_apis/geocoding/search_service.rb
82
84
  - app/services/katalyst/google_apis/recaptcha/assessment_service.rb
83
85
  - app/validators/recaptcha_validator.rb
86
+ - config/boot.rb
84
87
  - config/ci.rb
85
88
  - config/importmap.rb
86
89
  - config/locales/en.yml