katalyst-google-apis 1.1.1 → 1.2.1

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: 5794e6da69ca70b6edb9a8e7bc30e75b8536d9d436422f3db7e311997aa44fee
4
- data.tar.gz: 47e4870067a6c655821415606a2446f80535b76623900290c16fe35441865966
3
+ metadata.gz: 134e4c5aacc319e53670df643c6252452ab1e865c77132a3de28427b6f762a70
4
+ data.tar.gz: f6e3756bbc7a329c4920340d4a9e446a81fe47f999afbf8437e6cbced7ad824c
5
5
  SHA512:
6
- metadata.gz: 14b904f1003fe047a112aea03619c29a9bdca4ec9c486e4143b1c2f02d4995837d0033a1319335d4036beb53e12c6e49483d6a23c9cfdc0303711fc8bbab3d3d
7
- data.tar.gz: 5d7693c3922f2573b711a033a5313b35dc0fdfb0156ee4b44f00d1c5df1316df611a9062ad1a7b1750e319b7242d34f9500d4ec4c0cac226ad841ba9de2dc521
6
+ metadata.gz: 6e5ea2e4950182c34aeacce541c724e33781ff220f71140260aec7fabf103188a667099d644cc49c87df53d76752c9c25423a3114ea22595c94ee5a00c10dfac
7
+ data.tar.gz: 57940e6e4e33caccb19179975047d1380d6d7a76162e373df7dd15403aa787835f1d6b23d2ad3d2f0b38321bebf1b8d6307c972e7dc0c93606add6b4d907200a
@@ -111,20 +111,21 @@ function getMetaElement(name) {
111
111
  function getCspNonce() {
112
112
  const element = getMetaElement("csp-nonce");
113
113
  if (element) {
114
- const {nonce: nonce, content: content} = element;
114
+ const { nonce: nonce, content: content } = element;
115
115
  return nonce === "" ? content : nonce;
116
116
  }
117
117
  }
118
118
 
119
119
  function createRecaptchaScriptElement() {
120
120
  const element = document.createElement("script");
121
- element.src = "https://www.google.com/recaptcha/enterprise.js?render=explicit";
121
+ element.src =
122
+ "https://www.google.com/recaptcha/enterprise.js?render=explicit";
122
123
  const cspNonce = getCspNonce();
123
124
  if (cspNonce) {
124
125
  element.setAttribute("nonce", cspNonce);
125
126
  }
126
- element.async = true
127
- element.defer = true
127
+ element.async = true;
128
+ element.defer = true;
128
129
 
129
130
  return element;
130
131
  }
@@ -133,7 +134,9 @@ function createRecaptchaScriptElement() {
133
134
  * Inject recaptcha enterprise script into the head, unless it is already there.
134
135
  */
135
136
  function injectRecaptchaScripts() {
136
- let element = document.head.querySelector("script[src*='recaptcha/enterprise']");
137
+ let element = document.head.querySelector(
138
+ "script[src*='recaptcha/enterprise']",
139
+ );
137
140
  if (!element) {
138
141
  element = createRecaptchaScriptElement();
139
142
  document.head.appendChild(element);
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module GoogleApis
5
+ class Error < StandardError
6
+ attr_reader :code, :status
7
+
8
+ def initialize(code:, status:, message:)
9
+ super(message)
10
+
11
+ @code = code
12
+ @status = status
13
+ end
14
+
15
+ def inspect
16
+ "#<#{self.class.name} code=#{code.inspect} status=#{status.inspect} message=#{message.inspect}>"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module GoogleApis
5
+ module Gemini
6
+ # Use Gemini GPT to generate a response to a prompt.
7
+ class GenerateContentService
8
+ attr_reader :response, :result, :error, :content_text
9
+
10
+ def self.call(parent:, model:, payload:, credentials: Katalyst::GoogleApis.credentials)
11
+ new(parent:, model:, credentials:).call(payload:)
12
+ end
13
+
14
+ def initialize(credentials:, model:, parent:)
15
+ @credentials = credentials
16
+ @model = model
17
+ @parent = parent
18
+ end
19
+
20
+ def call(payload:)
21
+ @response = Curl.post(url, payload.to_json) do |http|
22
+ http.headers["Content-Type"] = "application/json; UTF-8"
23
+ @credentials.apply!(http.headers)
24
+ end
25
+
26
+ if %r{^application\/json}.match?(@response.content_type)
27
+ @result = JSON.parse(response.body, symbolize_names: true)
28
+ else
29
+ raise GoogleApis::Error.new(
30
+ code: response.response_code,
31
+ status: Rack::Utils::HTTP_STATUS_CODES[response.response_code],
32
+ message: "Unexpected HTTP response received (#{response.response_code}, #{@response.content_type})",
33
+ )
34
+ end
35
+
36
+ if result[:error].present?
37
+ @error = GoogleApis::Error.new(**result[:error])
38
+ else
39
+ @content_text = result.dig(:candidates, 0, :content, :parts, 0, :text)
40
+ end
41
+
42
+ self
43
+ rescue StandardError => e
44
+ @error = e
45
+ raise e
46
+ ensure
47
+ report_error
48
+ end
49
+
50
+ def success?
51
+ result.present? && content_text.present?
52
+ end
53
+
54
+ def content_json
55
+ return @content_json if instance_variable_defined?(:@content_json)
56
+
57
+ @content_json = JSON.parse(content_text, symbolize_names: true)
58
+ end
59
+
60
+ def inspect
61
+ "#<#{self.class.name} result: #{@result.inspect} error: #{@error.inspect}>"
62
+ end
63
+
64
+ private
65
+
66
+ def url
67
+ "https://aiplatform.googleapis.com/v1#{@parent}#{@model}:generateContent"
68
+ end
69
+
70
+ def report_error
71
+ return if error.nil?
72
+
73
+ if defined?(Sentry)
74
+ Sentry.add_breadcrumb(sentry_breadcrumb(error))
75
+ else
76
+ Rails.logger.error(error)
77
+ end
78
+ end
79
+
80
+ def sentry_breadcrumb(error)
81
+ Sentry::Breadcrumb.new(
82
+ type: "http",
83
+ category: "gemini",
84
+ data: {
85
+ url:,
86
+ method: "POST",
87
+ status_code: error.code,
88
+ reason: error.message,
89
+ },
90
+ )
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -8,8 +8,8 @@ module Katalyst
8
8
  class AssessmentService
9
9
  attr_accessor :response, :result, :error
10
10
 
11
- def self.call(parent:, credentials: GoogleApis.credentials, **)
12
- new(credentials:, parent:).call(**)
11
+ def self.call(assessment:, parent:, credentials: GoogleApis.credentials)
12
+ new(credentials:, parent:).call(assessment:)
13
13
  end
14
14
 
15
15
  def initialize(credentials:, parent:)
@@ -23,18 +23,22 @@ module Katalyst
23
23
  @credentials.apply!(http.headers)
24
24
  end
25
25
 
26
- @result = JSON.parse(@response.body, symbolize_names: true)
27
-
28
- self
29
- rescue Curl::Easy::Error => e
30
- if defined?(Sentry)
31
- Sentry.add_breadcrumb(sentry_breadcrumb(e))
26
+ if %r{^application\/json}.match?(@response.content_type)
27
+ @result = JSON.parse(response.body, symbolize_names: true)
32
28
  else
33
- Rails.logger.error(e)
29
+ raise GoogleApis::Error.new(
30
+ code: response.response_code,
31
+ status: Rack::Utils::HTTP_STATUS_CODES[response.response_code],
32
+ message: "Unexpected HTTP response received (#{response.response_code}, #{@response.content_type})",
33
+ )
34
34
  end
35
35
 
36
- @error = e
37
36
  self
37
+ rescue StandardError => e
38
+ @error = e
39
+ raise e
40
+ ensure
41
+ report_error
38
42
  end
39
43
 
40
44
  def valid?
@@ -63,14 +67,26 @@ module Katalyst
63
67
  "https://recaptchaenterprise.googleapis.com/v1/#{@parent}/assessments"
64
68
  end
65
69
 
70
+ def report_error
71
+ return if error.nil?
72
+
73
+ if defined?(Sentry)
74
+ Sentry.add_breadcrumb(sentry_breadcrumb(error))
75
+ else
76
+ Rails.logger.error(error)
77
+ end
78
+ end
79
+
66
80
  def sentry_breadcrumb(error)
67
81
  Sentry::Breadcrumb.new(
68
- type: "http",
69
- category: "recaptcha",
70
- url:,
71
- method: "POST",
72
- status_code: error.code,
73
- reason: error.message,
82
+ type: "http",
83
+ category: "recaptcha",
84
+ data: {
85
+ url:,
86
+ method: "POST",
87
+ status_code: error.code,
88
+ reason: error.message,
89
+ },
74
90
  )
75
91
  end
76
92
  end
data/config/ci.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Run using bin/ci
4
+
5
+ CI.run do
6
+ step "Setup", "bin/setup"
7
+
8
+ step "Style: Ruby", "bundle exec rubocop --no-server"
9
+
10
+ step "Style: JS/CSS", "bundle exec rake prettier:lint"
11
+
12
+ step "Security: Brakeman vulnerability audit", "bundle exec brakeman -q -w2"
13
+
14
+ step "Tests: rspec", "bundle exec rspec"
15
+ end
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.1.1
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katalyst Interactive
@@ -77,8 +77,11 @@ files:
77
77
  - app/helpers/katalyst/google_apis/form_builder.rb
78
78
  - app/helpers/katalyst/google_apis/govuk_form_builder.rb
79
79
  - app/services/katalyst/google_apis/credentials.rb
80
+ - app/services/katalyst/google_apis/error.rb
81
+ - app/services/katalyst/google_apis/gemini/generate_content_service.rb
80
82
  - app/services/katalyst/google_apis/recaptcha/assessment_service.rb
81
83
  - app/validators/recaptcha_validator.rb
84
+ - config/ci.rb
82
85
  - config/importmap.rb
83
86
  - config/locales/en.yml
84
87
  - lib/katalyst-google-apis.rb