screenshotscout 0.3.0 → 0.4.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: cd3adf5658d59ec5e3c12edc3b0cbe74803d1aee6fbe58786672f2692eb4424d
4
- data.tar.gz: e4a6840918f1d719be82c7217fb4fbdbc018a34ab1680ff0e463343222097f5d
3
+ metadata.gz: 8a670447d2ee6dc8b14865d9161623661cdcd7f8ca0139a5741d61872821d05a
4
+ data.tar.gz: 9c514cbb7f1f5e0eabc76669c7a83faffbcff07501689749238c6f527dbaa6cb
5
5
  SHA512:
6
- metadata.gz: c5df1b40ef5da9932e68bf8e5d0a6e8d743c9efa251c59abba8ab9e21af1b5428477dd8154741bc7c2b457f3eb1e256c542b717de40d21d4d2aa49821c9c0b56
7
- data.tar.gz: 0335a8cb2be523fdcda670fd44e3a376b45c2f154459594e4e8223dd314c9151977cc6d6a1e3aec2b319e6065ce3e746a0e2f46e74421ee2818e87f0fa89838c
6
+ metadata.gz: d3606891244c9ec6eb473af8eda9ff48d07b4990ff7263e577920ddf8db12abc787a177bccf8448d3e64f9396640378a2a2e16e0c57f3e78e330d06dbaac5066
7
+ data.tar.gz: 4e07a85d4ed239504f3629f4d897f93585cbc32c5279898e4819f3b3346c1a6cc4eadecabe5b652504b26d775af5aeb9990dc690f8bbb470a13c51927c159378
data/README.md CHANGED
@@ -29,7 +29,7 @@ gem install screenshotscout
29
29
  store them securely. The access key is required. The secret key is optional and enables
30
30
  [signed requests](#signed-requests).
31
31
 
32
- The examples below read `SCREENSHOTSCOUT_ACCESS_KEY` and, when available,
32
+ The examples below read `SCREENSHOTSCOUT_ACCESS_KEY` and, where they sign,
33
33
  `SCREENSHOTSCOUT_SECRET_KEY` from environment variables.
34
34
 
35
35
  ## Capture a screenshot
@@ -40,10 +40,7 @@ the full page and saves the returned image:
40
40
  ```ruby
41
41
  require "screenshotscout"
42
42
 
43
- client = ScreenshotScout::Client.new(
44
- access_key: ENV.fetch("SCREENSHOTSCOUT_ACCESS_KEY"),
45
- secret_key: ENV.fetch("SCREENSHOTSCOUT_SECRET_KEY", nil)
46
- )
43
+ client = ScreenshotScout::Client.new(access_key: ENV.fetch("SCREENSHOTSCOUT_ACCESS_KEY"))
47
44
  response = client.capture(
48
45
  "https://example.com",
49
46
  ScreenshotScout::CaptureOptions.new(full_page: true)
@@ -64,10 +61,7 @@ image or PDF output:
64
61
  ```ruby
65
62
  require "screenshotscout"
66
63
 
67
- client = ScreenshotScout::Client.new(
68
- access_key: ENV.fetch("SCREENSHOTSCOUT_ACCESS_KEY"),
69
- secret_key: ENV.fetch("SCREENSHOTSCOUT_SECRET_KEY", nil)
70
- )
64
+ client = ScreenshotScout::Client.new(access_key: ENV.fetch("SCREENSHOTSCOUT_ACCESS_KEY"))
71
65
  options = ScreenshotScout::CaptureOptions.new(
72
66
  response_type: ScreenshotScout::CaptureResponseType::JSON
73
67
  )
@@ -122,8 +116,9 @@ on the [API Keys page](https://screenshotscout.com/app/api-keys).
122
116
 
123
117
  ## Signed requests
124
118
 
125
- Pass the API key's secret key to sign GET requests, POST requests, and generated capture URLs
126
- automatically. The secret is used locally and is never transmitted.
119
+ Pass the API key's secret key to sign generated capture URLs and GET requests automatically. The
120
+ secret is used locally and is never transmitted. POST requests can't be signed, so capture with
121
+ `method: CaptureHttpMethod::GET`:
127
122
 
128
123
  ```ruby
129
124
  require "screenshotscout"
@@ -133,7 +128,10 @@ client = ScreenshotScout::Client.new(
133
128
  secret_key: ENV.fetch("SCREENSHOTSCOUT_SECRET_KEY")
134
129
  )
135
130
 
136
- response = client.capture("https://example.com")
131
+ response = client.capture(
132
+ "https://example.com",
133
+ method: ScreenshotScout::CaptureHttpMethod::GET
134
+ )
137
135
  File.binwrite("signed-screenshot.png", response.bytes)
138
136
  ```
139
137
 
@@ -164,10 +162,7 @@ Use the provided constants for documented values, or pass a string:
164
162
  ```ruby
165
163
  require "screenshotscout"
166
164
 
167
- client = ScreenshotScout::Client.new(
168
- access_key: ENV.fetch("SCREENSHOTSCOUT_ACCESS_KEY"),
169
- secret_key: ENV.fetch("SCREENSHOTSCOUT_SECRET_KEY", nil)
170
- )
165
+ client = ScreenshotScout::Client.new(access_key: ENV.fetch("SCREENSHOTSCOUT_ACCESS_KEY"))
171
166
  options = ScreenshotScout::CaptureOptions.new(
172
167
  format: ScreenshotScout::CaptureFormat::WEBP,
173
168
  wait_until: ScreenshotScout::CaptureWaitUntil::LOAD
@@ -240,7 +235,6 @@ end
240
235
 
241
236
  client = ScreenshotScout::Client.new(
242
237
  access_key: ENV.fetch("SCREENSHOTSCOUT_ACCESS_KEY"),
243
- secret_key: ENV.fetch("SCREENSHOTSCOUT_SECRET_KEY", nil),
244
238
  transport: ApplicationTransport.new(open_timeout: 10, read_timeout: 300)
245
239
  )
246
240
 
@@ -256,10 +250,7 @@ headers, content type, and body. `APIError` provides the same response details.
256
250
  ```ruby
257
251
  require "screenshotscout"
258
252
 
259
- client = ScreenshotScout::Client.new(
260
- access_key: ENV.fetch("SCREENSHOTSCOUT_ACCESS_KEY"),
261
- secret_key: ENV.fetch("SCREENSHOTSCOUT_SECRET_KEY", nil)
262
- )
253
+ client = ScreenshotScout::Client.new(access_key: ENV.fetch("SCREENSHOTSCOUT_ACCESS_KEY"))
263
254
 
264
255
  begin
265
256
  client.capture("https://example.com")
data/examples/README.md CHANGED
@@ -14,22 +14,21 @@ From the repository root, install the dependencies:
14
14
  bundle install
15
15
  ```
16
16
 
17
- Set your access key. Set the optional secret key to use
18
- [signed requests](../README.md#signed-requests) and signed capture URLs. The secret key is required
19
- when **Require signed requests** is enabled for the API key.
17
+ Set your access key. To sign the URL that `capture_url.rb` prints, also set the optional secret
18
+ key.
20
19
 
21
20
  PowerShell:
22
21
 
23
22
  ```powershell
24
23
  $env:SCREENSHOTSCOUT_ACCESS_KEY = "YOUR_ACCESS_KEY"
25
- $env:SCREENSHOTSCOUT_SECRET_KEY = "YOUR_SECRET_KEY"
24
+ $env:SCREENSHOTSCOUT_SECRET_KEY = "YOUR_SECRET_KEY" # Optional, used by capture_url.rb
26
25
  ```
27
26
 
28
27
  macOS or Linux:
29
28
 
30
29
  ```shell
31
30
  export SCREENSHOTSCOUT_ACCESS_KEY="YOUR_ACCESS_KEY"
32
- export SCREENSHOTSCOUT_SECRET_KEY="YOUR_SECRET_KEY"
31
+ export SCREENSHOTSCOUT_SECRET_KEY="YOUR_SECRET_KEY" # Optional, used by capture_url.rb
33
32
  ```
34
33
 
35
34
  Run any example:
data/examples/binary.rb CHANGED
@@ -3,8 +3,7 @@
3
3
  require "screenshotscout"
4
4
 
5
5
  access_key = ENV.fetch("SCREENSHOTSCOUT_ACCESS_KEY")
6
- secret_key = ENV.fetch("SCREENSHOTSCOUT_SECRET_KEY", nil)
7
- client = ScreenshotScout::Client.new(access_key: access_key, secret_key: secret_key)
6
+ client = ScreenshotScout::Client.new(access_key: access_key)
8
7
  options = ScreenshotScout::CaptureOptions.new(full_page: true)
9
8
  response = client.capture("https://example.com", options)
10
9
 
data/examples/json.rb CHANGED
@@ -3,8 +3,7 @@
3
3
  require "screenshotscout"
4
4
 
5
5
  access_key = ENV.fetch("SCREENSHOTSCOUT_ACCESS_KEY")
6
- secret_key = ENV.fetch("SCREENSHOTSCOUT_SECRET_KEY", nil)
7
- client = ScreenshotScout::Client.new(access_key: access_key, secret_key: secret_key)
6
+ client = ScreenshotScout::Client.new(access_key: access_key)
8
7
  options = ScreenshotScout::CaptureOptions.new(
9
8
  response_type: ScreenshotScout::CaptureResponseType::JSON
10
9
  )
@@ -19,11 +19,13 @@ module ScreenshotScout
19
19
  @transport = transport || NetHttpTransport.new
20
20
  end
21
21
 
22
- # Sends one GET or POST request and waits for the final inline response.
22
+ # Sends one GET or POST request and waits for the final inline response. A client with a secret key
23
+ # signs GET requests and raises ConfigurationError for POST, because POST requests can't be signed.
23
24
  def capture(url, options = nil, method: CaptureHttpMethod::POST)
24
25
  serialized = Internal::Serializer.serialize(url, options)
25
26
  method = CaptureHttpMethod::POST if method.nil?
26
27
  validate_method(method)
28
+ reject_signed_post(method)
27
29
  expected_kind = expected_response_kind(options)
28
30
  signature = create_signature(serialized.pairs)
29
31
  request = create_request(serialized, method, signature)
@@ -76,6 +78,14 @@ module ScreenshotScout
76
78
  )
77
79
  end
78
80
 
81
+ def reject_signed_post(method)
82
+ return if @secret_key.nil? || method != CaptureHttpMethod::POST
83
+
84
+ raise ConfigurationError,
85
+ "POST requests can't be signed. This client has a secret key: send this capture with method GET, " \
86
+ "or use a client without a secret key and an API key that doesn't require signed requests."
87
+ end
88
+
79
89
  def create_signature(pairs)
80
90
  return nil if @secret_key.nil?
81
91
 
@@ -95,14 +105,12 @@ module ScreenshotScout
95
105
  url = "#{CAPTURE_ENDPOINT}?#{Internal::Serializer.encode_query(pairs)}"
96
106
  TransportRequest.new(method: method, url: url, headers: headers)
97
107
  else
98
- body = serialized.body.dup
99
- body["signature"] = signature unless signature.nil?
100
108
  headers["Content-Type"] = "application/json"
101
109
  TransportRequest.new(
102
110
  method: method,
103
111
  url: CAPTURE_ENDPOINT,
104
112
  headers: headers,
105
- body: Internal::JsonCodec.encode_wire_object(body)
113
+ body: Internal::JsonCodec.encode_wire_object(serialized.body)
106
114
  )
107
115
  end
108
116
  rescue Error
@@ -122,7 +122,7 @@ module ScreenshotScout
122
122
  end
123
123
 
124
124
  def encode_query(pairs)
125
- pairs.map { |pair| "#{form_encode(pair.name)}=#{form_encode(pair.value)}" }.join("&")
125
+ pairs.map { |pair| "#{rfc3986_encode(pair.name)}=#{rfc3986_encode(pair.value)}" }.join("&")
126
126
  end
127
127
 
128
128
  def append_repeated(pairs, body, property, wire_name, value)
@@ -199,7 +199,11 @@ module ScreenshotScout
199
199
  raise SerializationError.new(message, option: property)
200
200
  end
201
201
 
202
- value.encode(Encoding::UTF_8)
202
+ string = value.encode(Encoding::UTF_8)
203
+ # encode doesn't check the bytes of a string that is already tagged as UTF-8.
204
+ raise Encoding::InvalidByteSequenceError, "invalid byte sequence in UTF-8" unless string.valid_encoding?
205
+
206
+ string
203
207
  rescue EncodingError => e
204
208
  message = if target
205
209
  "The capture target URL must contain valid UTF-8."
@@ -210,19 +214,18 @@ module ScreenshotScout
210
214
  end
211
215
  private_class_method :string_value
212
216
 
213
- def form_encode(value)
217
+ # Percent-encodes UTF-8 bytes, leaving only the RFC 3986 unreserved characters as they are.
218
+ def rfc3986_encode(value)
214
219
  value.bytes.map do |byte|
215
220
  if byte.between?(65, 90) || byte.between?(97, 122) ||
216
- byte.between?(48, 57) || [42, 45, 46, 95].include?(byte)
221
+ byte.between?(48, 57) || [45, 46, 95, 126].include?(byte)
217
222
  byte.chr
218
- elsif byte == 32
219
- "+"
220
223
  else
221
224
  format("%%%02X", byte)
222
225
  end
223
226
  end.join
224
227
  end
225
- private_class_method :form_encode
228
+ private_class_method :rfc3986_encode
226
229
  end
227
230
  end
228
231
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ScreenshotScout
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: screenshotscout
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oleksii Velykyi