Module: Rospatent::InputValidator

Included in:
Client, Search
Defined in:
lib/rospatent/input_validator.rb

Overview

Module for validating input parameters and converting types

Instance Method Summary collapse

Instance Method Details

#validate_array(value, field_name, max_size: nil, element_validator: nil) ⇒ Array

Validate array parameter

Parameters:

  • value (Array, nil)

    Array to validate

  • field_name (String)

    Name of the field for error messages

  • max_size (Integer, nil) (defaults to: nil)

    Maximum array size

  • element_validator (Proc, nil) (defaults to: nil)

    Proc to validate each element

Returns:

  • (Array)

    Validated array

Raises:

  • (ValidationError)

    If array is invalid



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/rospatent/input_validator.rb', line 200

def validate_array(value, field_name, max_size: nil, element_validator: nil)
  return nil if value.nil?

  unless value.is_a?(Array)
    raise Errors::ValidationError,
          "Invalid #{field_name} type. Expected Array, got #{value.class}"
  end

  raise Errors::ValidationError, "#{field_name.capitalize} cannot be empty" if value.empty?

  if max_size && value.size > max_size
    raise Errors::ValidationError,
          "#{field_name.capitalize} cannot contain more than #{max_size} items"
  end

  if element_validator
    value.each_with_index do |element, index|
      element_validator.call(element)
    rescue Errors::ValidationError => e
      raise Errors::ValidationError,
            "Invalid #{field_name}[#{index}]: #{e.message}"
    rescue StandardError => e
      raise Errors::ValidationError,
            "Invalid #{field_name}[#{index}]: #{e.message}"
    end
  end

  value
end

#validate_date(date, field_name = "date") ⇒ Date

Validate and normalize date input

Parameters:

  • date (String, Date, nil)

    Date input to validate

  • field_name (String) (defaults to: "date")

    Name of the field for error messages

Returns:

  • (Date)

    Normalized Date object

Raises:

  • (ValidationError)

    If date format is invalid



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/rospatent/input_validator.rb', line 13

def validate_date(date, field_name = "date")
  return nil if date.nil?
  return date if date.is_a?(Date)

  if date.is_a?(String)
    begin
      return Date.parse(date)
    rescue Date::Error
      raise Errors::ValidationError,
            "Invalid #{field_name} format. Expected YYYY-MM-DD or Date object"
    end
  end

  raise Errors::ValidationError,
        "Invalid #{field_name} type. Expected String or Date, got #{date.class}"
end

#validate_enum(value, allowed_values, field_name) ⇒ Symbol

Validate enum value

Parameters:

  • value (Symbol, String, nil)

    Value to validate

  • allowed_values (Array)

    Array of allowed values

  • field_name (String)

    Name of the field for error messages

Returns:

  • (Symbol)

    Validated symbol

Raises:

  • (ValidationError)

    If value is not in allowed list



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/rospatent/input_validator.rb', line 156

def validate_enum(value, allowed_values, field_name)
  return nil if value.nil?

  # Convert to symbol for consistency
  value = value.to_sym if value.respond_to?(:to_sym)

  # Convert allowed values to symbols for comparison
  allowed_symbols = allowed_values.map(&:to_sym)

  unless allowed_symbols.include?(value)
    raise Errors::ValidationError,
          "Invalid #{field_name}. Allowed values: #{allowed_values.join(', ')}"
  end

  value
end

#validate_hash(value, field_name, required_keys: [], allowed_keys: nil) ⇒ Hash

Validate hash parameter

Parameters:

  • value (Hash, nil)

    Hash to validate

  • field_name (String)

    Name of the field for error messages

  • required_keys (Array) (defaults to: [])

    Required keys in the hash

  • allowed_keys (Array, nil) (defaults to: nil)

    Allowed keys (if nil, any keys allowed)

Returns:

  • (Hash)

    Validated hash

Raises:

  • (ValidationError)

    If hash is invalid



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/rospatent/input_validator.rb', line 260

def validate_hash(value, field_name, required_keys: [], allowed_keys: nil)
  return nil if value.nil?

  unless value.is_a?(Hash)
    raise Errors::ValidationError,
          "Invalid #{field_name} type. Expected Hash, got #{value.class}"
  end

  # Check required keys
  missing_keys = required_keys.map(&:to_s) - value.keys.map(&:to_s)
  unless missing_keys.empty?
    raise Errors::ValidationError,
          "Missing required #{field_name} keys: #{missing_keys.join(', ')}"
  end

  # Check allowed keys if specified
  if allowed_keys
    invalid_keys = value.keys.map(&:to_s) - allowed_keys.map(&:to_s)
    unless invalid_keys.empty?
      raise Errors::ValidationError,
            "Invalid #{field_name} keys: #{invalid_keys.join(', ')}"
    end
  end

  value
end

#validate_params(params, validations) ⇒ Hash

Validate multiple parameters at once

Examples:

validate_params(
  { limit: "10", offset: "0" },
  {
    limit: { type: :positive_integer, max_value: 100 },
    offset: { type: :positive_integer, min_value: 0 }
  }
)

Parameters:

  • params (Hash)

    Parameters to validate

  • validations (Hash)

    Validation rules for each parameter

Returns:

  • (Hash)

    Hash of validated parameters

Raises:

  • (ValidationError)

    If any validation fails



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/rospatent/input_validator.rb', line 322

def validate_params(params, validations)
  validated = {}
  errors = {}

  validations.each do |param_name, rules|
    value = params[param_name]
    validated[param_name] = case rules[:type]
                            when :positive_integer
                              validate_positive_integer(
                                value,
                                param_name.to_s,
                                min_value: rules[:min_value] || 1,
                                max_value: rules[:max_value]
                              )
                            when :string
                              validate_string(
                                value,
                                param_name.to_s,
                                max_length: rules[:max_length]
                              )
                            when :string_or_array
                              validate_string_or_array(
                                value,
                                param_name.to_s,
                                max_length: rules[:max_length],
                                max_size: rules[:max_size]
                              )
                            when :enum
                              validate_enum(value, rules[:allowed_values], param_name.to_s)
                            when :string_enum
                              validate_string_enum(value, rules[:allowed_values], param_name.to_s)
                            when :date
                              validate_date(value, param_name.to_s)
                            when :array
                              validate_array(
                                value,
                                param_name.to_s,
                                max_size: rules[:max_size],
                                element_validator: rules[:element_validator]
                              )
                            when :hash
                              validate_hash(
                                value,
                                param_name.to_s,
                                required_keys: rules[:required_keys] || [],
                                allowed_keys: rules[:allowed_keys]
                              )
                            when :filter
                              validate_filter(value, param_name.to_s)
                            when :boolean
                              # Convert to boolean, nil values remain nil
                              value.nil? ? nil : !!value
                            else
                              value
                            end
  rescue Errors::ValidationError => e
    errors[param_name] = e.message
  end

  raise Errors::ValidationError.new("Validation failed", errors) unless errors.empty?

  validated.compact
end

#validate_patent_id(document_id) ⇒ String

Validate patent ID format

Parameters:

  • document_id (String)

    Patent document ID

Returns:

  • (String)

    Validated document ID

Raises:

  • (ValidationError)

    If format is invalid



291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/rospatent/input_validator.rb', line 291

def validate_patent_id(document_id)
  raise Errors::ValidationError, "Document_id is required" if document_id.nil?

  value = validate_string(document_id, "document_id")
  return nil if value.nil?

  # Regex pattern for patent IDs
  # Format: {country code (2 letters)}{publication number (alphanumeric)}{document type (letter+digits)}_{date (YYYYMMDD)}
  pattern = /^[A-Z]{2}[A-Z0-9]+[A-Z]\d*_\d{8}$/

  unless value.match?(pattern)
    raise Errors::ValidationError,
          "Invalid patent ID format. Expected format: 'XX12345Y1_YYYYMMDD' (country code + alphanumeric publication number + document type + date)"
  end

  value
end

#validate_positive_integer(value, field_name, min_value: 1, max_value: nil) ⇒ Integer

Validate positive integer

Parameters:

  • value (Integer, String, nil)

    Value to validate

  • field_name (String)

    Name of the field for error messages

  • min_value (Integer) (defaults to: 1)

    Minimum allowed value

  • max_value (Integer, nil) (defaults to: nil)

    Maximum allowed value (optional)

Returns:

  • (Integer)

    Validated integer

Raises:

  • (ValidationError)

    If value is invalid



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/rospatent/input_validator.rb', line 48

def validate_positive_integer(value, field_name, min_value: 1, max_value: nil)
  return nil if value.nil?

  # Convert string to integer if possible
  if value.is_a?(String)
    begin
      value = Integer(value)
    rescue ArgumentError
      raise Errors::ValidationError,
            "Invalid #{field_name}. Expected integer, got non-numeric string"
    end
  end

  unless value.is_a?(Integer)
    raise Errors::ValidationError,
          "Invalid #{field_name} type. Expected Integer, got #{value.class}"
  end

  if value < min_value
    raise Errors::ValidationError,
          "#{field_name.capitalize} must be at least #{min_value}"
  end

  if max_value && value > max_value
    raise Errors::ValidationError,
          "#{field_name.capitalize} must be at most #{max_value}"
  end

  value
end

#validate_required_date(date, field_name = "date") ⇒ Date

Validate and normalize required date input (does not allow nil)

Parameters:

  • date (String, Date)

    Date input to validate

  • field_name (String) (defaults to: "date")

    Name of the field for error messages

Returns:

  • (Date)

    Normalized Date object

Raises:

  • (ValidationError)

    If date format is invalid or nil



35
36
37
38
39
# File 'lib/rospatent/input_validator.rb', line 35

def validate_required_date(date, field_name = "date")
  raise Errors::ValidationError, "#{field_name.capitalize} is required" if date.nil?

  validate_date(date, field_name)
end

#validate_required_string(value, field_name, max_length: nil) ⇒ String

Validate required non-empty string (does not allow nil)

Parameters:

  • value (String, nil)

    String to validate

  • field_name (String)

    Name of the field for error messages

  • max_length (Integer, nil) (defaults to: nil)

    Maximum allowed length

Returns:

  • (String)

    Validated string

Raises:

  • (ValidationError)

    If string is invalid or nil



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/rospatent/input_validator.rb', line 132

def validate_required_string(value, field_name, max_length: nil)
  raise Errors::ValidationError, "#{field_name.capitalize} is required" if value.nil?

  unless value.is_a?(String)
    raise Errors::ValidationError,
          "Invalid #{field_name} type. Expected String, got #{value.class}"
  end

  raise Errors::ValidationError, "#{field_name.capitalize} cannot be empty" if value.empty?

  if max_length && value.length > max_length
    raise Errors::ValidationError,
          "#{field_name.capitalize} cannot exceed #{max_length} characters"
  end

  value.strip
end

#validate_string(value, field_name, max_length: nil) ⇒ String

Validate non-empty string

Parameters:

  • value (String, nil)

    String to validate

  • field_name (String)

    Name of the field for error messages

  • max_length (Integer, nil) (defaults to: nil)

    Maximum allowed length

Returns:

  • (String)

    Validated string

Raises:

  • (ValidationError)

    If string is invalid



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/rospatent/input_validator.rb', line 85

def validate_string(value, field_name, max_length: nil)
  return nil if value.nil?

  unless value.is_a?(String)
    raise Errors::ValidationError,
          "Invalid #{field_name} type. Expected String, got #{value.class}"
  end

  raise Errors::ValidationError, "#{field_name.capitalize} cannot be empty" if value.empty?

  if max_length && value.length > max_length
    raise Errors::ValidationError,
          "#{field_name.capitalize} cannot exceed #{max_length} characters"
  end

  value.strip
end

#validate_string_enum(value, allowed_values, field_name) ⇒ String

Validate string enum value (preserves string type)

Parameters:

  • value (String, nil)

    Value to validate

  • allowed_values (Array<String>)

    Array of allowed string values

  • field_name (String)

    Name of the field for error messages

Returns:

  • (String)

    Validated string

Raises:

  • (ValidationError)

    If value is not in allowed list



179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/rospatent/input_validator.rb', line 179

def validate_string_enum(value, allowed_values, field_name)
  return nil if value.nil?

  # Ensure value is a string
  value = value.to_s if value.respond_to?(:to_s)

  unless allowed_values.include?(value)
    raise Errors::ValidationError,
          "Invalid #{field_name}. Allowed values: #{allowed_values.join(', ')}"
  end

  value
end

#validate_string_or_array(value, field_name, max_length: nil, max_size: nil) ⇒ String, Array

Validate string or array parameter (for highlight tags)

Parameters:

  • value (String, Array, nil)

    String or Array to validate

  • field_name (String)

    Name of the field for error messages

  • max_length (Integer, nil) (defaults to: nil)

    Maximum string length (for string values)

  • max_size (Integer, nil) (defaults to: nil)

    Maximum array size (for array values)

Returns:

  • (String, Array)

    Validated string or array

Raises:

  • (ValidationError)

    If value is invalid



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/rospatent/input_validator.rb', line 237

def validate_string_or_array(value, field_name, max_length: nil, max_size: nil)
  return nil if value.nil?

  case value
  when String
    validate_string(value, field_name, max_length: max_length)
  when Array
    validate_array(value, field_name, max_size: max_size) do |element|
      validate_string(element, "#{field_name} element", max_length: max_length)
    end
  else
    raise Errors::ValidationError,
          "Invalid #{field_name} type. Expected String or Array, got #{value.class}"
  end
end

#validate_text_with_word_count(value, field_name, min_words:, max_length: nil) ⇒ String

Validate text with word count requirements

Parameters:

  • value (String, nil)

    Text to validate

  • field_name (String)

    Name of the field for error messages

  • min_words (Integer)

    Minimum required word count

  • max_length (Integer, nil) (defaults to: nil)

    Maximum allowed character length

Returns:

  • (String)

    Validated text

Raises:

  • (ValidationError)

    If text is invalid or has insufficient words



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/rospatent/input_validator.rb', line 110

def validate_text_with_word_count(value, field_name, min_words:, max_length: nil)
  # First, apply standard string validation
  validated_text = validate_string(value, field_name, max_length: max_length)
  return nil if validated_text.nil?

  # Count words by splitting on whitespace
  word_count = count_words(validated_text)

  if word_count < min_words
    raise Errors::ValidationError,
          "#{field_name.capitalize} must contain at least #{min_words} words (currently has #{word_count})"
  end

  validated_text
end