calmlens 0.18.3 → 0.20.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.
@@ -5,12 +5,15 @@ module CalmLens
5
5
  # project: a `flag` list reports matches, a `block` list also blocks close matches. Reach it through
6
6
  # {ServerClient#hash_lists}.
7
7
  class HashLists < Resource
8
+ # The prefix of this resource's operations in Internal::OPTION_KEYS.
9
+ OPERATIONS = 'hashLists'
10
+
8
11
  # Lists one page of the project's hash lists, newest first.
9
12
  # @option options [String] :cursor the previous page's `nextCursor`
10
13
  # @option options [Integer] :page_size 1 to 100, default 50
11
14
  # @return [Hash] a cursor page: items, nextCursor, hasMore, pageSize and ordering.
12
15
  def list(options = {})
13
- call(options) do |api, request|
16
+ call(options, __method__) do |api, request|
14
17
  response = request('GET', project('hash-lists'), query: api, request_options: request)
15
18
  valid = response.is_a?(Hash) && response['items'].is_a?(Array)
16
19
  raise InvalidResponseError.new(SdkErrorCodes::INVALID_RESPONSE, 'Invalid hash list page') unless valid
@@ -22,7 +25,7 @@ module CalmLens
22
25
  # @option options [String] :hash_list_id
23
26
  # @return [Hash] the hash list
24
27
  def retrieve(options)
25
- call(options) { |api, request| request('GET', list_path(api), request_options: request) }
28
+ call(options, __method__) { |api, request| request('GET', list_path(api), request_options: request) }
26
29
  end
27
30
 
28
31
  # @option options [String] :name
@@ -30,7 +33,7 @@ module CalmLens
30
33
  # @option options [String, nil] :description
31
34
  # @return [Hash] the hash list
32
35
  def create(options)
33
- call(options) { |api, request| request('POST', project('hash-lists'), body: api, request_options: request) }
36
+ call(options, __method__) { |api, request| request('POST', project('hash-lists'), body: api, request_options: request) }
34
37
  end
35
38
 
36
39
  # Renames a list or changes what its matches do. The new action applies to later uploads.
@@ -40,7 +43,7 @@ module CalmLens
40
43
  # @option options [String] :action `flag` or `block`
41
44
  # @return [Hash] the hash list
42
45
  def update(options)
43
- call(options) do |api, request|
46
+ call(options, __method__) do |api, request|
44
47
  request('PATCH', list_path(api), body: without(api, 'hashListId'), request_options: request)
45
48
  end
46
49
  end
@@ -49,7 +52,7 @@ module CalmLens
49
52
  # @option options [String] :hash_list_id
50
53
  # @return [nil]
51
54
  def delete(options)
52
- call(options) do |api, request|
55
+ call(options, __method__) do |api, request|
53
56
  request('DELETE', list_path(api), request_options: request)
54
57
  nil
55
58
  end
@@ -61,7 +64,7 @@ module CalmLens
61
64
  # @option options [Integer] :page_size 1 to 100, default 50
62
65
  # @return [Hash] a cursor page: items, nextCursor, hasMore, pageSize and ordering.
63
66
  def list_entries(options)
64
- call(options) do |api, request|
67
+ call(options, __method__) do |api, request|
65
68
  request('GET', "#{list_path(api)}/entries", query: without(api, 'hashListId'), request_options: request)
66
69
  end
67
70
  end
@@ -74,7 +77,7 @@ module CalmLens
74
77
  # @option options [String] :label
75
78
  # @return [Hash] the entry
76
79
  def add_entry(options)
77
- call(options) do |api, request|
80
+ call(options, __method__) do |api, request|
78
81
  request('POST', "#{list_path(api)}/entries", body: without(api, 'hashListId'), request_options: request)
79
82
  end
80
83
  end
@@ -82,7 +85,7 @@ module CalmLens
82
85
  # @option options [String] :entry_id
83
86
  # @return [nil]
84
87
  def remove_entry(options)
85
- call(options) do |api, request|
88
+ call(options, __method__) do |api, request|
86
89
  request('DELETE', "/hash-list-entries/#{Internal.segment(api['entryId'])}", request_options: request)
87
90
  nil
88
91
  end
@@ -79,6 +79,16 @@ module CalmLens
79
79
  value.is_a?(Numeric) && value.real? && value.to_f.finite? && value.positive?
80
80
  end
81
81
 
82
+ # Whether the API accepts an upload of this MIME type: an image, audio, video, text, document or
83
+ # website type, not a container such as `application/octet-stream`. It refuses any other type
84
+ # with `upload/invalid-mime-type`.
85
+ def accepted_upload_mime_type?(type)
86
+ mime = type.to_s.split(';', 2).first.to_s.strip.downcase
87
+ return false if UPLOAD_MIME_REJECTED.include?(mime)
88
+
89
+ mime.start_with?(*UPLOAD_MIME_PREFIXES) || mime.end_with?(*UPLOAD_MIME_SUFFIXES) || UPLOAD_MIME_TYPES.include?(mime)
90
+ end
91
+
82
92
  # Fetches a required option.
83
93
  def required(options, key, code = SdkErrorCodes::INVALID_ARGUMENT)
84
94
  raise invalid("#{key} is required", code) unless options.key?(key)
@@ -4,12 +4,16 @@ module CalmLens
4
4
  # Lens definitions, cached per client. Used through {ServerClient#ensure_lenses} and {ServerClient#ensure_lens}.
5
5
  # @api private
6
6
  class Lenses < Resource
7
+ # The prefix of this resource's operations in Internal::OPTION_KEYS.
8
+ OPERATIONS = nil
9
+
10
+ # Lens definitions are sent as given, nil fields included.
7
11
  def ensure_lenses(options)
8
- call(options) { |api, request| @session.ensure_lenses(api['lenses'], request) }
12
+ call(options, __method__, keep_nil: true) { |api, request| @session.ensure_lenses(api['lenses'], request) }
9
13
  end
10
14
 
11
15
  def ensure_lens(options)
12
- call(options) { |api, request| @session.ensure_lenses([api], request).first }
16
+ call(options, __method__, keep_nil: true) { |api, request| @session.ensure_lenses([api], request).first }
13
17
  end
14
18
  end
15
19
  private_constant :Lenses
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by packages/sdk-contract/generate.ts from packages/shared/src/PublicApiSchemas.ts.
4
+ # Do not edit by hand: run `bun packages/sdk-contract/generate.ts`.
5
+ module CalmLens
6
+ module Internal
7
+ # The camelCase option keys each method takes, besides `request_options`.
8
+ OPTION_KEYS = {
9
+ 'assets.batch' => %w[assetIds operation].freeze,
10
+ 'assets.classify' => %w[
11
+ authorId content context description externalId externalUrl folderId isFinal metadata name part
12
+ processingOptions sizeInBytes totalSizeInBytes uploadPresetId url visibility
13
+ ].freeze,
14
+ 'assets.create' => %w[
15
+ authorId content context description externalId externalUrl folderId isFinal metadata name part
16
+ processingOptions sizeInBytes totalSizeInBytes uploadPresetId url visibility
17
+ ].freeze,
18
+ 'assets.createSignedUrl' => %w[assetId expiresInSeconds lens resource transform viewerClaims].freeze,
19
+ 'assets.decide' => %w[assetId confirmPurge decision note].freeze,
20
+ 'assets.delete' => %w[assetId].freeze,
21
+ 'assets.deleteContent' => %w[assetId].freeze,
22
+ 'assets.each' => %w[cursor filter folderId ordering pageSize].freeze,
23
+ 'assets.eachChild' => %w[assetId cursor ordering pageSize].freeze,
24
+ 'assets.explain' => %w[assetId rerun].freeze,
25
+ 'assets.list' => %w[cursor filter folderId ordering pageSize].freeze,
26
+ 'assets.listChildren' => %w[assetId cursor ordering pageSize].freeze,
27
+ 'assets.move' => %w[assetId folderId].freeze,
28
+ 'assets.retrieve' => %w[assetId].freeze,
29
+ 'assets.retry' => %w[assetId].freeze,
30
+ 'assets.search' => %w[limit mode query].freeze,
31
+ 'assets.update' => %w[assetId description externalId externalUrl metadata name tags visibility].freeze,
32
+ 'assets.uploadPart' => %w[assetId part queue].freeze,
33
+ 'ensureLens' => %w[active description identifier name transform type version].freeze,
34
+ 'ensureLenses' => %w[lenses].freeze,
35
+ 'events.each' => %w[cursor pageSize].freeze,
36
+ 'events.eachPage' => %w[cursor pageSize].freeze,
37
+ 'events.list' => %w[cursor pageSize].freeze,
38
+ 'folders.create' => %w[name parentId].freeze,
39
+ 'folders.delete' => %w[folderId].freeze,
40
+ 'folders.list' => %w[cursor ordering pageSize parentId search].freeze,
41
+ 'folders.move' => %w[folderId parentId].freeze,
42
+ 'folders.retrieve' => %w[folderId].freeze,
43
+ 'folders.update' => %w[folderId name].freeze,
44
+ 'hashLists.addEntry' => %w[assetId hash hashListId label quality].freeze,
45
+ 'hashLists.create' => %w[action description name].freeze,
46
+ 'hashLists.delete' => %w[hashListId].freeze,
47
+ 'hashLists.list' => %w[cursor ordering pageSize].freeze,
48
+ 'hashLists.listEntries' => %w[cursor hashListId ordering pageSize].freeze,
49
+ 'hashLists.removeEntry' => %w[entryId].freeze,
50
+ 'hashLists.retrieve' => %w[hashListId].freeze,
51
+ 'hashLists.update' => %w[action description hashListId name].freeze,
52
+ 'policies.applyPreset' => %w[operation policyId presetVersion revision].freeze,
53
+ 'policies.assign' => %w[policyId].freeze,
54
+ 'policies.create' => %w[description name presetId settings].freeze,
55
+ 'policies.delete' => %w[policyId].freeze,
56
+ 'policies.list' => %w[].freeze,
57
+ 'policies.retrieve' => %w[policyId].freeze,
58
+ 'policies.setAutomaticUpdates' => %w[enabled policyId presetVersion revision].freeze,
59
+ 'policies.update' => %w[description name policyId revision settings].freeze,
60
+ 'text.classify' => %w[
61
+ authorId context description externalId externalUrl folderId metadata name processingOptions text
62
+ uploadPresetId visibility
63
+ ].freeze
64
+ }.freeze
65
+
66
+ # The option keys each method sends as null when given nil; any other option given as nil
67
+ # is left out, as the API refuses null for it.
68
+ NULLABLE_OPTION_KEYS = {
69
+ 'assets.classify' => %w[
70
+ authorId context description externalId folderId metadata processingOptions uploadPresetId visibility
71
+ ].freeze,
72
+ 'assets.create' => %w[
73
+ authorId context description externalId folderId metadata processingOptions uploadPresetId visibility
74
+ ].freeze,
75
+ 'assets.each' => %w[filter folderId].freeze,
76
+ 'assets.list' => %w[filter folderId].freeze,
77
+ 'assets.move' => %w[folderId].freeze,
78
+ 'assets.update' => %w[description externalId externalUrl metadata tags visibility].freeze,
79
+ 'folders.create' => %w[parentId].freeze,
80
+ 'folders.move' => %w[parentId].freeze,
81
+ 'hashLists.addEntry' => %w[label quality].freeze,
82
+ 'hashLists.create' => %w[description].freeze,
83
+ 'hashLists.update' => %w[description].freeze,
84
+ 'text.classify' => %w[
85
+ authorId context description externalId folderId metadata processingOptions uploadPresetId visibility
86
+ ].freeze
87
+ }.freeze
88
+ end
89
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CalmLens
4
+ # Moderation policies belong to the project's organisation and its projects share them. Every
5
+ # change sends the policy's `revision`, so a policy changed since is refused with
6
+ # `policy/revision-conflict`. Through a project's key you can change only policies no other
7
+ # project uses or owns. Reach it through {ServerClient#policies}.
8
+ class Policies < Resource
9
+ # The prefix of this resource's operations in Internal::OPTION_KEYS.
10
+ OPERATIONS = 'policies'
11
+
12
+ # Lists the organisation's policies, oldest first, and the ID of the one the project uses.
13
+ # @return [Hash] `policies` and `projectPolicyId`
14
+ def list(options = {})
15
+ call(options, __method__) { |_api, request| request('GET', project('policies'), request_options: request) }
16
+ end
17
+
18
+ # @option options [String] :policy_id
19
+ # @return [Hash] the policy
20
+ def retrieve(options)
21
+ call(options, __method__) { |api, request| request('GET', policy_path(api), request_options: request) }
22
+ end
23
+
24
+ # Creates a policy from `settings` (defining every category) with a `name`, or a copy of the
25
+ # latest release of a built-in preset with `preset_id`. It moderates nothing until it is
26
+ # assigned to a project or named on an upload preset.
27
+ # @option options [String] :name required with `settings`; a preset copy defaults to its name
28
+ # @option options [String] :description
29
+ # @option options [Hash] :settings
30
+ # @option options [String] :preset_id `general-community`, `children-platform`, `education-support`
31
+ # or `wholesome-community`
32
+ # @return [Hash] the policy
33
+ def create(options)
34
+ call(options, __method__) { |api, request| request('POST', project('policies'), body: api, request_options: request) }
35
+ end
36
+
37
+ # Changes the name, description or settings (complete, defining every category); later
38
+ # moderation uses them. Saving a policy that follows automatic updates pins it as a copy.
39
+ # @option options [String] :policy_id
40
+ # @option options [Integer] :revision the policy's current revision
41
+ # @option options [String] :name
42
+ # @option options [String] :description
43
+ # @option options [Hash] :settings
44
+ # @return [Hash] the policy
45
+ def update(options)
46
+ call(options, __method__) do |api, request|
47
+ request('PATCH', policy_path(api), body: without(api, 'policyId'), request_options: request)
48
+ end
49
+ end
50
+
51
+ # Resets a preset copy to its pinned preset version, or updates an unchanged copy to the latest
52
+ # preset release.
53
+ # @option options [String] :policy_id
54
+ # @option options [String] :operation `reset` or `update`
55
+ # @option options [Integer] :revision the policy's current revision
56
+ # @option options [Integer] :preset_version `preset.version` to reset, `preset.latestVersion` to update
57
+ # @return [Hash] the policy
58
+ def apply_preset(options)
59
+ call(options, __method__) do |api, request|
60
+ request('POST', "#{policy_path(api)}/preset", body: without(api, 'policyId'), request_options: request)
61
+ end
62
+ end
63
+
64
+ # Switches automatic updates on or off for the organisation's own preset policy. Switching on
65
+ # replaces the saved copy with the latest preset settings under the same ID; switching off pins
66
+ # the current preset version as a copy with unchanged settings.
67
+ # @option options [String] :policy_id
68
+ # @option options [Boolean] :enabled
69
+ # @option options [Integer] :revision the policy's current revision
70
+ # @option options [Integer] :preset_version the preset's `latestVersion`
71
+ # @return [Hash] the policy
72
+ def set_automatic_updates(options) # rubocop:disable Naming/AccessorMethodName -- the shared SDK surface name
73
+ call(options, __method__) do |api, request|
74
+ request('PUT', "#{policy_path(api)}/automatic-updates", body: without(api, 'policyId'), request_options: request)
75
+ end
76
+ end
77
+
78
+ # Deletes a policy that no project or upload preset uses.
79
+ # @option options [String] :policy_id
80
+ # @return [nil]
81
+ def delete(options)
82
+ call(options, __method__) do |api, request|
83
+ request('DELETE', policy_path(api), request_options: request)
84
+ nil
85
+ end
86
+ end
87
+
88
+ # Chooses the policy the project moderates with. Later uploads use it, except those through an
89
+ # upload preset that names its own policy.
90
+ # @option options [String] :policy_id
91
+ # @return [Hash] the policy
92
+ def assign(options)
93
+ call(options, __method__) { |api, request| request('PUT', project('policy'), body: api, request_options: request) }
94
+ end
95
+
96
+ private
97
+
98
+ def policy_path(api) = project("policies/#{Internal.segment(api['policyId'])}")
99
+ end
100
+ end
@@ -15,9 +15,28 @@ module CalmLens
15
15
 
16
16
  private
17
17
 
18
+ # The option keys of methods that send no request of their own, or take options of their own
19
+ # beside a request's; Internal::OPTION_KEYS, generated from the API, has the rest.
20
+ LOCAL_OPTION_KEYS = {
21
+ 'assets.waitForDecision' => %w[assetId timeout].freeze,
22
+ 'cdn.getAssetUrl' => %w[assetIdentifier lens resource transform viewerClaims].freeze,
23
+ 'cdn.serve' => %w[assetIdentifier init lens resource transform viewerClaims].freeze,
24
+ 'uploadTokens.sign' => %w[expiresInSeconds externalId uploadPresetId].freeze
25
+ }.freeze
26
+ private_constant :LOCAL_OPTION_KEYS
27
+
18
28
  # Runs a public method: splits options, and raises every argument problem as {InvalidArgumentError}.
19
- def call(options)
29
+ # `method` names the public method: an option it doesn't take, such as a misspelt one, raises
30
+ # `sdk/invalid-argument` instead of being silently dropped.
31
+ # An option given as nil is left out, as if omitted, unless the API takes null for it
32
+ # (Internal::NULLABLE_OPTION_KEYS), such as `description: nil` to clear a description;
33
+ # `keep_nil: true` sends every option as given, as for a lens definition.
34
+ def call(options, method, keep_nil: false)
35
+ operation = [self.class::OPERATIONS, Internal.camelize(method)].compact.join('.')
36
+ check_option_keys(options, operation)
20
37
  api, request = Internal.options(options)
38
+ nullable = Internal::NULLABLE_OPTION_KEYS.fetch(operation, [])
39
+ api = api.reject { |key, value| value.nil? && !nullable.include?(key) } unless keep_nil
21
40
  yield api, request
22
41
  rescue Error
23
42
  raise
@@ -25,6 +44,18 @@ module CalmLens
25
44
  raise InvalidArgumentError.new(SdkErrorCodes::INVALID_ARGUMENT, e.message), cause: e
26
45
  end
27
46
 
47
+ def check_option_keys(options, operation)
48
+ return unless options.is_a?(Hash)
49
+
50
+ allowed = LOCAL_OPTION_KEYS.fetch(operation) { Internal::OPTION_KEYS.fetch(operation) }
51
+ unknown = options.keys.reject do |key|
52
+ %w[request_options requestOptions].include?(key.to_s) || allowed.include?(Internal.camelize(key))
53
+ end
54
+ return if unknown.empty?
55
+
56
+ raise Internal.invalid("Unknown option#{'s' if unknown.size > 1} for #{operation}: #{unknown.join(', ')}")
57
+ end
58
+
28
59
  def request(...) = @session.request(...)
29
60
  def project(path) = @session.project(path)
30
61
 
@@ -25,6 +25,11 @@ module CalmLens
25
25
  RETRY_AFTER_MAX = 60
26
26
  # The largest response body read, in bytes (32 MiB).
27
27
  DEFAULT_MAX_RESPONSE_BYTES = 32 * 1024 * 1024
28
+ # Content larger than this many bytes is uploaded in parts of this size (8 MiB).
29
+ DEFAULT_PART_SIZE_BYTES = 8 * 1024 * 1024
30
+ # The smallest and largest `part_size_bytes`: every part but the last is at least 5 MiB.
31
+ MIN_PART_SIZE_BYTES = 5 * 1024 * 1024
32
+ MAX_PART_SIZE_BYTES = 64 * 1024 * 1024
28
33
  # The most lens definitions {#ensure_lenses} sends in one request.
29
34
  ENSURE_LENSES_MAX_PER_REQUEST = 100
30
35
 
@@ -34,6 +39,8 @@ module CalmLens
34
39
  attr_reader :folders
35
40
  # @return [HashLists]
36
41
  attr_reader :hash_lists
42
+ # @return [Policies]
43
+ attr_reader :policies
37
44
  # @return [Text]
38
45
  attr_reader :text
39
46
  # @return [UploadTokens]
@@ -62,14 +69,18 @@ module CalmLens
62
69
  # `Retry-After`. Other writes are never retried. 0 disables retries.
63
70
  # @param retry_base_delay [Numeric] seconds before the first retry when the API gives no
64
71
  # `Retry-After`; later retries wait up to twice as long each time, at most 8 seconds.
72
+ # @param part_size_bytes [Integer] content larger than this many bytes whose size the client knows
73
+ # and can read again (a binary String, base64, or a File, StringIO or other seekable IO) is
74
+ # uploaded in parts of this size: each part is retried on its own, and calling again with the
75
+ # same `idempotency_key` resumes an interrupted upload. From 5 MiB to 64 MiB; default 8 MiB.
65
76
  # @param transport [#call, nil] a custom {Transport}; defaults to {Transport::NetHTTP}.
66
77
  # @raise [ConfigurationError] `sdk/invalid-config` for a missing or malformed setting.
67
78
  def initialize(api_key:, project_id:, base_url: DEFAULT_BASE_URL, cdn_base_url: DEFAULT_CDN_BASE_URL,
68
79
  timeout: DEFAULT_TIMEOUT, open_timeout: DEFAULT_OPEN_TIMEOUT, upload_timeout: nil,
69
80
  max_retries: DEFAULT_MAX_RETRIES, retry_base_delay: DEFAULT_RETRY_BASE_DELAY,
70
- max_response_bytes: DEFAULT_MAX_RESPONSE_BYTES, transport: nil)
81
+ max_response_bytes: DEFAULT_MAX_RESPONSE_BYTES, part_size_bytes: DEFAULT_PART_SIZE_BYTES, transport: nil)
71
82
  check_config(api_key, project_id, timeout, open_timeout, max_retries, retry_base_delay, transport)
72
- check_limits(upload_timeout, max_response_bytes)
83
+ check_limits(upload_timeout, max_response_bytes, part_size_bytes)
73
84
  @project_id = project_id
74
85
  @base_url = ServerClient.check_url(base_url, 'base_url')
75
86
  @cdn_base_url = ServerClient.check_url(cdn_base_url, 'cdn_base_url')
@@ -77,10 +88,11 @@ module CalmLens
77
88
  cdn_base_url: @cdn_base_url, timeout: timeout, open_timeout: open_timeout,
78
89
  max_retries: max_retries, retry_base_delay: retry_base_delay,
79
90
  max_response_bytes: max_response_bytes, upload_timeout: upload_timeout,
80
- transport: transport || Transport::NetHTTP.new)
91
+ part_size_bytes: part_size_bytes, transport: transport || Transport::NetHTTP.new)
81
92
  @assets = Assets.new(@session)
82
93
  @folders = Folders.new(@session)
83
94
  @hash_lists = HashLists.new(@session)
95
+ @policies = Policies.new(@session)
84
96
  @text = Text.new(@session, @assets)
85
97
  @upload_tokens = UploadTokens.new(@session, api_key)
86
98
  @events = Events.new(@session)
@@ -107,11 +119,11 @@ module CalmLens
107
119
  nil
108
120
  end
109
121
 
110
- # Creates or updates named lenses (saved transform pipelines) and returns them. Definitions
122
+ # Creates or updates named lenses (saved transforms) and returns them. Definitions
111
123
  # this client already saved are not sent again. Supplying the same definition and version is
112
124
  # idempotent; changing a lens needs a higher `version`. More than 100 new definitions are sent in
113
125
  # consecutive requests, in order.
114
- # @option options [Array<Hash>] :lenses `[{identifier:, name:, type:, version:, transforms:, ...}]`
126
+ # @option options [Array<Hash>] :lenses `[{identifier:, name:, type:, version:, transform:, ...}]`
115
127
  # @return [Array<Hash>] the saved lenses, in the given order.
116
128
  def ensure_lenses(options) = @lenses.ensure_lenses(options)
117
129
 
@@ -135,10 +147,15 @@ module CalmLens
135
147
 
136
148
  private
137
149
 
138
- def check_limits(upload_timeout, max_response_bytes)
150
+ def check_limits(upload_timeout, max_response_bytes, part_size_bytes)
139
151
  unless upload_timeout.nil? || Internal.positive?(upload_timeout)
140
152
  raise ConfigurationError.new(SdkErrorCodes::INVALID_CONFIG, 'upload_timeout must be nil or a positive number of seconds')
141
153
  end
154
+ unless part_size_bytes.is_a?(Integer) && part_size_bytes.between?(MIN_PART_SIZE_BYTES, MAX_PART_SIZE_BYTES)
155
+ raise ConfigurationError.new(SdkErrorCodes::INVALID_CONFIG,
156
+ "part_size_bytes must be an Integer from #{MIN_PART_SIZE_BYTES} (5 MiB) to " \
157
+ "#{MAX_PART_SIZE_BYTES} (64 MiB)")
158
+ end
142
159
  return if max_response_bytes.is_a?(Integer) && max_response_bytes.positive?
143
160
 
144
161
  raise ConfigurationError.new(SdkErrorCodes::INVALID_CONFIG, 'max_response_bytes must be a positive Integer')
@@ -25,13 +25,16 @@ module CalmLens
25
25
  end
26
26
 
27
27
  attr_reader :project_id, :base_url, :cdn_base_url
28
+ # Content larger than this many bytes that can be read again is uploaded in parts of this size.
29
+ attr_reader :part_size_bytes
28
30
  # Leaves the upload size checks the API also makes (part and image sizes) to the API, so an
29
31
  # offline stand-in answers them like the API does. {MockServerClient} sets it.
30
32
  attr_accessor :defer_upload_checks
31
33
 
32
34
  def initialize(api_key:, project_id:, base_url:, cdn_base_url:, timeout:, open_timeout:, max_retries:,
33
- retry_base_delay:, max_response_bytes:, upload_timeout:, transport:)
35
+ retry_base_delay:, max_response_bytes:, upload_timeout:, part_size_bytes:, transport:)
34
36
  @api_key = api_key
37
+ @part_size_bytes = part_size_bytes
35
38
  @project_id = project_id
36
39
  @base_url = base_url
37
40
  @cdn_base_url = cdn_base_url
@@ -161,10 +164,10 @@ module CalmLens
161
164
  private
162
165
 
163
166
  # The fields that identify a lens definition, as canonical JSON: an unset description or
164
- # transforms list is the same definition as an explicit null.
167
+ # transform is the same definition as an explicit null.
165
168
  def lens_key(lens)
166
169
  fields = { 'identifier' => lens['identifier'], 'name' => lens['name'], 'description' => lens['description'],
167
- 'type' => lens['type'], 'transforms' => lens['transforms'] }
170
+ 'type' => lens['type'], 'transform' => lens['transform'] }
168
171
  %w[active version].each { |name| fields[name] = lens[name] if lens.key?(name) }
169
172
  JSON.generate(canonical(fields))
170
173
  end
@@ -17,5 +17,8 @@ module CalmLens
17
17
  "personal-information" => ["personal-information-contact", "personal-information-email", "personal-information-phone", "personal-information-government-id", "personal-information-financial", "personal-information-location", "personal-information-identifiers", "personal-information-credentials", "personal-information-request", "personal-information-self-disclosure", "personal-information-third-party", "personal-information-historical", "personal-information-documentary", "personal-information-educational", "personal-information-fictional", "personal-information-support-prevention"].freeze,
18
18
  "profanity" => ["profanity-mild", "profanity-strong", "profanity-slurs", "profanity-obfuscated", "profanity-historical", "profanity-documentary", "profanity-educational", "profanity-fictional", "profanity-support-prevention"].freeze,
19
19
  }.freeze
20
+
21
+ # Qualifiers the input type sets, which a test input scenario cannot list.
22
+ INPUT_QUALIFIERS = ["sexual-text", "violence-text"].freeze
20
23
  end
21
24
  end