screenshotscout 0.1.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: f9d728fc9965f9d5d7821a429288aaec98c8f802be65afec9b85e57979f5a865
4
- data.tar.gz: 85c40db2478f3f8302ed41a412994bf56c777ec4d0097b575a0fe1d29143fc83
3
+ metadata.gz: 8a670447d2ee6dc8b14865d9161623661cdcd7f8ca0139a5741d61872821d05a
4
+ data.tar.gz: 9c514cbb7f1f5e0eabc76669c7a83faffbcff07501689749238c6f527dbaa6cb
5
5
  SHA512:
6
- metadata.gz: 409cf55714fe4190fa3ec809c5736499288f913762a08a54cbc34a517e83dd743ea3b9b3a8e2518da21d0a1e178e77a2fe01181b8f0347be189ee6fd12466e1b
7
- data.tar.gz: 7312bc75e5a225910031eb2021a476ec23754169de29580c42bf724cd46d9beec24a72c419098ec8f1f083882618a918796780052eb0242229e6579f47b1fb79
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
 
@@ -155,7 +153,7 @@ capture:
155
153
  - DOM changes: `hide_selectors`, `click_selectors`, `click_all_selectors`, `inject_css`, `inject_js`, `bypass_csp`
156
154
  - Framing: `selector`, `clip_x`, `clip_y`, `clip_width`, `clip_height`
157
155
  - Image output: `image_width`, `image_height`, `image_mode`, `image_anchor`, `image_allow_upscale`, `image_background`, `image_quality`
158
- - PDF: `pdf_paper_format`, `pdf_landscape`, `pdf_print_background`, `pdf_margin`, `pdf_margin_top`, `pdf_margin_right`, `pdf_margin_bottom`, `pdf_margin_left`, `pdf_scale`
156
+ - PDF: `pdf_paper_format`, `pdf_landscape`, `pdf_print_background`, `pdf_margin`, `pdf_margin_top`, `pdf_margin_right`, `pdf_margin_bottom`, `pdf_margin_left`, `pdf_scale`, `pdf_page_ranges`
159
157
  - Caching: `cache`, `cache_ttl`, `cache_key`
160
158
  - Storage: `storage_mode`, `storage_endpoint`, `storage_bucket`, `storage_region`, `storage_object_key`
161
159
 
@@ -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
  )
@@ -13,9 +13,9 @@ module ScreenshotScout
13
13
  click_selectors click_all_selectors inject_css inject_js bypass_csp selector clip_x clip_y
14
14
  clip_width clip_height image_width image_height image_mode image_anchor image_allow_upscale
15
15
  image_background image_quality pdf_paper_format pdf_landscape pdf_print_background
16
- pdf_margin pdf_margin_top pdf_margin_right pdf_margin_bottom pdf_margin_left pdf_scale cache
17
- cache_ttl cache_key storage_mode storage_endpoint storage_bucket storage_region
18
- storage_object_key
16
+ pdf_margin pdf_margin_top pdf_margin_right pdf_margin_bottom pdf_margin_left pdf_scale
17
+ pdf_page_ranges cache cache_ttl cache_key storage_mode storage_endpoint storage_bucket
18
+ storage_region storage_object_key
19
19
  ].freeze
20
20
 
21
21
  attr_reader(*ATTRIBUTE_NAMES)
@@ -60,7 +60,8 @@ module ScreenshotScout
60
60
  module CapturePdfPaperFormat
61
61
  A3 = "a3"
62
62
  A4 = "a4"
63
- CONTENT = "content"
63
+ A5 = "a5"
64
+ A6 = "a6"
64
65
  LEGAL = "legal"
65
66
  LETTER = "letter"
66
67
  TABLOID = "tabloid"
@@ -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
@@ -67,6 +67,7 @@ module ScreenshotScout
67
67
  pdf_margin_bottom: "pdf_margin_bottom",
68
68
  pdf_margin_left: "pdf_margin_left",
69
69
  pdf_scale: "pdf_scale",
70
+ pdf_page_ranges: "pdf_page_ranges",
70
71
  cache: "cache",
71
72
  cache_ttl: "cache_ttl",
72
73
  cache_key: "cache_key",
@@ -121,7 +122,7 @@ module ScreenshotScout
121
122
  end
122
123
 
123
124
  def encode_query(pairs)
124
- 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("&")
125
126
  end
126
127
 
127
128
  def append_repeated(pairs, body, property, wire_name, value)
@@ -198,7 +199,11 @@ module ScreenshotScout
198
199
  raise SerializationError.new(message, option: property)
199
200
  end
200
201
 
201
- 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
202
207
  rescue EncodingError => e
203
208
  message = if target
204
209
  "The capture target URL must contain valid UTF-8."
@@ -209,19 +214,18 @@ module ScreenshotScout
209
214
  end
210
215
  private_class_method :string_value
211
216
 
212
- 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)
213
219
  value.bytes.map do |byte|
214
220
  if byte.between?(65, 90) || byte.between?(97, 122) ||
215
- byte.between?(48, 57) || [42, 45, 46, 95].include?(byte)
221
+ byte.between?(48, 57) || [45, 46, 95, 126].include?(byte)
216
222
  byte.chr
217
- elsif byte == 32
218
- "+"
219
223
  else
220
224
  format("%%%02X", byte)
221
225
  end
222
226
  end.join
223
227
  end
224
- private_class_method :form_encode
228
+ private_class_method :rfc3986_encode
225
229
  end
226
230
  end
227
231
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ScreenshotScout
4
- VERSION = "0.1.0"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -64,7 +64,8 @@ module ScreenshotScout
64
64
  module CapturePdfPaperFormat
65
65
  A3: String
66
66
  A4: String
67
- CONTENT: String
67
+ A5: String
68
+ A6: String
68
69
  LEGAL: String
69
70
  LETTER: String
70
71
  TABLOID: String
@@ -135,6 +136,7 @@ module ScreenshotScout
135
136
  attr_reader pdf_margin_bottom: String?
136
137
  attr_reader pdf_margin_left: String?
137
138
  attr_reader pdf_scale: number?
139
+ attr_reader pdf_page_ranges: String?
138
140
  attr_reader cache: bool?
139
141
  attr_reader cache_ttl: Integer?
140
142
  attr_reader cache_key: String?
@@ -204,6 +206,7 @@ module ScreenshotScout
204
206
  ?pdf_margin_bottom: String?,
205
207
  ?pdf_margin_left: String?,
206
208
  ?pdf_scale: number?,
209
+ ?pdf_page_ranges: String?,
207
210
  ?cache: bool?,
208
211
  ?cache_ttl: Integer?,
209
212
  ?cache_key: String?,
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.1.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oleksii Velykyi