rospatent 1.2.0 → 1.3.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.
@@ -3,22 +3,46 @@
3
3
  # Rospatent API client configuration
4
4
  # Documentation: https://online.rospatent.gov.ru/open-data/open-api
5
5
  Rospatent.configure do |config|
6
- # API URL (default: https://searchplatform.rospatent.gov.ru)
7
- # config.api_url = ENV.fetch("ROSPATENT_API_URL", "https://searchplatform.rospatent.gov.ru")
8
-
9
6
  # JWT Bearer token for API authorization - REQUIRED
10
- # Obtain this from the Rospatent API administration
11
- config.token = Rails.application.credentials.rospatent_api_token || ENV.fetch(
12
- "ROSPATENT_API_TOKEN", nil
13
- )
7
+ # Priority: Rails credentials > Environment variable > Manual setting
8
+ config.token = Rails.application.credentials.rospatent_token ||
9
+ ENV["ROSPATENT_TOKEN"] ||
10
+ ENV.fetch("ROSPATENT_API_TOKEN", nil)
11
+
12
+ # Environment configuration - respect environment variables
13
+ config.environment = ENV.fetch("ROSPATENT_ENV", Rails.env)
14
+
15
+ # Cache configuration - respect environment variables or use Rails defaults
16
+ config.cache_enabled = if ENV.key?("ROSPATENT_CACHE_ENABLED")
17
+ ENV["ROSPATENT_CACHE_ENABLED"] == "true"
18
+ else
19
+ Rails.env.production?
20
+ end
14
21
 
15
- # Rails-specific environment integration
16
- config.environment = Rails.env
17
- config.cache_enabled = Rails.env.production?
18
- config.log_level = Rails.env.production? ? :warn : :debug
22
+ # Logging configuration - CRITICAL: Respect environment variables first!
23
+ config.log_level = if ENV.key?("ROSPATENT_LOG_LEVEL")
24
+ ENV["ROSPATENT_LOG_LEVEL"].to_sym
25
+ else
26
+ Rails.env.production? ? :warn : :debug
27
+ end
19
28
 
20
- # Optional: Override defaults if needed
21
- # config.timeout = 30
22
- # config.retry_count = 3
23
- # config.user_agent = "YourApp/1.0"
29
+ config.log_requests = if ENV.key?("ROSPATENT_LOG_REQUESTS")
30
+ ENV["ROSPATENT_LOG_REQUESTS"] == "true"
31
+ else
32
+ !Rails.env.production?
33
+ end
34
+
35
+ config.log_responses = if ENV.key?("ROSPATENT_LOG_RESPONSES")
36
+ ENV["ROSPATENT_LOG_RESPONSES"] == "true"
37
+ else
38
+ Rails.env.development?
39
+ end
40
+
41
+ # Optional: Override other defaults if needed
42
+ # config.api_url = ENV.fetch("ROSPATENT_API_URL", "https://searchplatform.rospatent.gov.ru")
43
+ # config.timeout = ENV.fetch("ROSPATENT_TIMEOUT", "30").to_i
44
+ # config.retry_count = ENV.fetch("ROSPATENT_RETRY_COUNT", "3").to_i
45
+ # config.cache_ttl = ENV.fetch("ROSPATENT_CACHE_TTL", "300").to_i
46
+ # config.cache_max_size = ENV.fetch("ROSPATENT_CACHE_MAX_SIZE", "1000").to_i
47
+ # config.connection_pool_size = ENV.fetch("ROSPATENT_POOL_SIZE", "5").to_i
24
48
  end
@@ -150,7 +150,7 @@ module Rospatent
150
150
  # @param text [String] The text to find similar patents to (minimum 50 words required)
151
151
  # @param count [Integer] Maximum number of results to return (default: 100)
152
152
  # @return [Hash] The similar search results
153
- # @raise [Rospatent::Errors::ValidationError] If text has insufficient words or other validation errors
153
+ # @raise [Rospatent::Errors::ValidationError] If text has insufficient words or errors
154
154
  def similar_patents_by_text(text, count: 100)
155
155
  # Validate inputs - text must have at least 50 words for the API
156
156
  validated_text = validate_text_with_word_count(text, "search_text", min_words: 50,
@@ -507,12 +507,13 @@ module Rospatent
507
507
  qn: { type: :string, max_length: 1000 },
508
508
  limit: { type: :positive_integer, min_value: 1, max_value: 100 },
509
509
  offset: { type: :positive_integer, min_value: 0, max_value: 10_000 },
510
- pre_tag: { type: :string, max_length: 50 },
511
- post_tag: { type: :string, max_length: 50 },
510
+ pre_tag: { type: :string_or_array, max_length: 50, max_size: 10 },
511
+ post_tag: { type: :string_or_array, max_length: 50, max_size: 10 },
512
512
  sort: { type: :enum, allowed_values: %i[relevance pub_date filing_date] },
513
- group_by: { type: :enum, allowed_values: [:patent_family] },
513
+ group_by: { type: :string_enum, allowed_values: %w[family:docdb family:dwpi] },
514
514
  include_facets: { type: :boolean },
515
- highlight: { type: :boolean },
515
+ highlight: { type: :hash },
516
+ filter: { type: :filter },
516
517
  datasets: { type: :array, max_size: 10 }
517
518
  }
518
519
 
@@ -632,7 +633,8 @@ module Rospatent
632
633
 
633
634
  error_msg = begin
634
635
  data = JSON.parse(response.body)
635
- data["error"] || data["message"] || "Unknown error"
636
+ # Try different possible error message fields used by Rospatent API
637
+ data["result"] || data["error"] || data["message"] || "Unknown error"
636
638
  rescue JSON::ParserError
637
639
  response.body
638
640
  end
@@ -668,7 +670,8 @@ module Rospatent
668
670
  # For binary endpoints, error responses might still be JSON
669
671
  error_msg = begin
670
672
  data = JSON.parse(response.body)
671
- data["error"] || data["message"] || "Unknown error"
673
+ # Try different possible error message fields used by Rospatent API
674
+ data["result"] || data["error"] || data["message"] || "Unknown error"
672
675
  rescue JSON::ParserError
673
676
  "Binary request failed"
674
677
  end
@@ -712,7 +715,8 @@ module Rospatent
712
715
  # @return [Hash] Field-specific validation errors
713
716
  def extract_validation_errors(response)
714
717
  data = JSON.parse(response.body)
715
- data["errors"] || data["validation_errors"] || {}
718
+ # Check various possible validation error fields
719
+ data["errors"] || data["validation_errors"] || data["details"] || {}
716
720
  rescue JSON::ParserError
717
721
  {}
718
722
  end
@@ -31,7 +31,7 @@ module Rospatent
31
31
  # Initialize a new configuration with default values
32
32
  def initialize
33
33
  @api_url = "https://searchplatform.rospatent.gov.ru"
34
- @token = nil
34
+ @token = ENV["ROSPATENT_TOKEN"] || ENV.fetch("ROSPATENT_API_TOKEN", nil)
35
35
  @timeout = 30
36
36
  @retry_count = 3
37
37
  @user_agent = "Rospatent Ruby Client/#{Rospatent::VERSION}"
@@ -106,6 +106,7 @@ module Rospatent
106
106
  private
107
107
 
108
108
  # Load environment-specific configuration
109
+ # Only override values that weren't explicitly set by environment variables
109
110
  def load_environment_config
110
111
  unless valid_environment?
111
112
  raise ArgumentError, "Invalid environment: #{@environment}. " \
@@ -116,20 +117,20 @@ module Rospatent
116
117
  when "production"
117
118
  @timeout = 60
118
119
  @retry_count = 5
119
- @log_level = :warn
120
- @cache_ttl = 600 # 10 minutes in production
120
+ @log_level = :warn unless ENV.key?("ROSPATENT_LOG_LEVEL")
121
+ @cache_ttl = 600 unless ENV.key?("ROSPATENT_CACHE_TTL") # 10 minutes in production
121
122
  when "staging"
122
123
  @timeout = 45
123
124
  @retry_count = 3
124
- @log_level = :info
125
- @cache_ttl = 300 # 5 minutes in staging
125
+ @log_level = :info unless ENV.key?("ROSPATENT_LOG_LEVEL")
126
+ @cache_ttl = 300 unless ENV.key?("ROSPATENT_CACHE_TTL") # 5 minutes in staging
126
127
  when "development"
127
128
  @timeout = 10
128
129
  @retry_count = 1
129
- @log_level = :debug
130
- @log_requests = true
131
- @log_responses = true
132
- @cache_ttl = 60 # 1 minute in development
130
+ @log_level = :debug unless ENV.key?("ROSPATENT_LOG_LEVEL")
131
+ @log_requests = true unless ENV.key?("ROSPATENT_LOG_REQUESTS")
132
+ @log_responses = true unless ENV.key?("ROSPATENT_LOG_RESPONSES")
133
+ @cache_ttl = 60 unless ENV.key?("ROSPATENT_CACHE_TTL") # 1 minute in development
133
134
  end
134
135
  end
135
136
  end
@@ -170,6 +170,26 @@ module Rospatent
170
170
  value
171
171
  end
172
172
 
173
+ # Validate string enum value (preserves string type)
174
+ # @param value [String, nil] Value to validate
175
+ # @param allowed_values [Array<String>] Array of allowed string values
176
+ # @param field_name [String] Name of the field for error messages
177
+ # @return [String] Validated string
178
+ # @raise [ValidationError] If value is not in allowed list
179
+ def validate_string_enum(value, allowed_values, field_name)
180
+ return nil if value.nil?
181
+
182
+ # Ensure value is a string
183
+ value = value.to_s if value.respond_to?(:to_s)
184
+
185
+ unless allowed_values.include?(value)
186
+ raise Errors::ValidationError,
187
+ "Invalid #{field_name}. Allowed values: #{allowed_values.join(', ')}"
188
+ end
189
+
190
+ value
191
+ end
192
+
173
193
  # Validate array parameter
174
194
  # @param value [Array, nil] Array to validate
175
195
  # @param field_name [String] Name of the field for error messages
@@ -207,6 +227,29 @@ module Rospatent
207
227
  value
208
228
  end
209
229
 
230
+ # Validate string or array parameter (for highlight tags)
231
+ # @param value [String, Array, nil] String or Array to validate
232
+ # @param field_name [String] Name of the field for error messages
233
+ # @param max_length [Integer, nil] Maximum string length (for string values)
234
+ # @param max_size [Integer, nil] Maximum array size (for array values)
235
+ # @return [String, Array] Validated string or array
236
+ # @raise [ValidationError] If value is invalid
237
+ def validate_string_or_array(value, field_name, max_length: nil, max_size: nil)
238
+ return nil if value.nil?
239
+
240
+ case value
241
+ when String
242
+ validate_string(value, field_name, max_length: max_length)
243
+ when Array
244
+ validate_array(value, field_name, max_size: max_size) do |element|
245
+ validate_string(element, "#{field_name} element", max_length: max_length)
246
+ end
247
+ else
248
+ raise Errors::ValidationError,
249
+ "Invalid #{field_name} type. Expected String or Array, got #{value.class}"
250
+ end
251
+ end
252
+
210
253
  # Validate hash parameter
211
254
  # @param value [Hash, nil] Hash to validate
212
255
  # @param field_name [String] Name of the field for error messages
@@ -296,8 +339,17 @@ module Rospatent
296
339
  param_name.to_s,
297
340
  max_length: rules[:max_length]
298
341
  )
342
+ when :string_or_array
343
+ validate_string_or_array(
344
+ value,
345
+ param_name.to_s,
346
+ max_length: rules[:max_length],
347
+ max_size: rules[:max_size]
348
+ )
299
349
  when :enum
300
350
  validate_enum(value, rules[:allowed_values], param_name.to_s)
351
+ when :string_enum
352
+ validate_string_enum(value, rules[:allowed_values], param_name.to_s)
301
353
  when :date
302
354
  validate_date(value, param_name.to_s)
303
355
  when :array
@@ -314,6 +366,11 @@ module Rospatent
314
366
  required_keys: rules[:required_keys] || [],
315
367
  allowed_keys: rules[:allowed_keys]
316
368
  )
369
+ when :filter
370
+ validate_filter(value, param_name.to_s)
371
+ when :boolean
372
+ # Convert to boolean, nil values remain nil
373
+ value.nil? ? nil : !!value
317
374
  else
318
375
  value
319
376
  end
@@ -334,5 +391,183 @@ module Rospatent
334
391
  def count_words(text)
335
392
  text.split.size
336
393
  end
394
+
395
+ # Validate filter parameter according to Rospatent API specification
396
+ # @param filter [Hash, nil] Filter hash to validate
397
+ # @param field_name [String] Name of the field for error messages
398
+ # @return [Hash] Validated filter hash
399
+ # @raise [ValidationError] If filter structure is invalid
400
+ def validate_filter(filter, field_name = "filter")
401
+ return nil if filter.nil?
402
+
403
+ unless filter.is_a?(Hash)
404
+ raise Errors::ValidationError,
405
+ "Invalid #{field_name} type. Expected Hash, got #{filter.class}"
406
+ end
407
+
408
+ validated_filter = {}
409
+
410
+ filter.each do |filter_field, filter_value|
411
+ case filter_field.to_s
412
+ when "authors", "patent_holders", "country", "kind", "ids",
413
+ "classification.ipc", "classification.ipc_group", "classification.ipc_subclass",
414
+ "classification.cpc", "classification.cpc_group", "classification.cpc_subclass"
415
+ # These fields use {"values": [...]} format
416
+ validated_filter[filter_field] = validate_filter_values(filter_value, filter_field)
417
+ when "date_published", "application.filing_date"
418
+ # These fields use {"range": {"gt": "20000101"}} format
419
+ validated_filter[filter_field] = validate_filter_range(filter_value, filter_field)
420
+ else
421
+ raise Errors::ValidationError,
422
+ "Invalid filter field '#{filter_field}'. Allowed fields: authors, patent_holders, " \
423
+ "country, kind, ids, date_published, application.filing_date, classification.ipc, " \
424
+ "classification.ipc_group, classification.ipc_subclass, classification.cpc, " \
425
+ "classification.cpc_group, classification.cpc_subclass"
426
+ end
427
+ end
428
+
429
+ validated_filter
430
+ end
431
+
432
+ # Validate filter values structure (for list-based filters)
433
+ # @param filter_value [Hash] Filter value to validate
434
+ # @param filter_field [String] Filter field name for error messages
435
+ # @return [Hash] Validated filter value
436
+ # @raise [ValidationError] If structure is invalid
437
+ def validate_filter_values(filter_value, filter_field)
438
+ unless filter_value.is_a?(Hash)
439
+ raise Errors::ValidationError,
440
+ "Invalid #{filter_field} filter structure. Expected Hash with 'values' key, got #{filter_value.class}"
441
+ end
442
+
443
+ unless filter_value.key?("values") || filter_value.key?(:values)
444
+ raise Errors::ValidationError,
445
+ "Missing required 'values' key in #{filter_field} filter. Expected format: {\"values\": [...]}"
446
+ end
447
+
448
+ values = filter_value["values"] || filter_value[:values]
449
+
450
+ unless values.is_a?(Array)
451
+ raise Errors::ValidationError,
452
+ "Invalid 'values' type in #{filter_field} filter. Expected Array, got #{values.class}"
453
+ end
454
+
455
+ if values.empty?
456
+ raise Errors::ValidationError,
457
+ "Empty 'values' array in #{filter_field} filter. At least one value must be provided"
458
+ end
459
+
460
+ # Validate each value is a string
461
+ values.each_with_index do |value, index|
462
+ unless value.is_a?(String) || value.is_a?(Symbol)
463
+ raise Errors::ValidationError,
464
+ "Invalid value type at index #{index} in #{filter_field} filter. Expected String, got #{value.class}"
465
+ end
466
+ end
467
+
468
+ { "values" => values.map(&:to_s) }
469
+ end
470
+
471
+ # Validate filter range structure (for date-based filters)
472
+ # @param filter_value [Hash] Filter value to validate
473
+ # @param filter_field [String] Filter field name for error messages
474
+ # @return [Hash] Validated filter value
475
+ # @raise [ValidationError] If structure is invalid
476
+ def validate_filter_range(filter_value, filter_field)
477
+ unless filter_value.is_a?(Hash)
478
+ raise Errors::ValidationError,
479
+ "Invalid #{filter_field} filter structure. Expected Hash with 'range' key, got #{filter_value.class}"
480
+ end
481
+
482
+ unless filter_value.key?("range") || filter_value.key?(:range)
483
+ raise Errors::ValidationError,
484
+ "Missing required 'range' key in #{filter_field} filter. Expected format: {\"range\": {\"gt\": \"20000101\"}}"
485
+ end
486
+
487
+ range = filter_value["range"] || filter_value[:range]
488
+
489
+ unless range.is_a?(Hash)
490
+ raise Errors::ValidationError,
491
+ "Invalid 'range' type in #{filter_field} filter. Expected Hash, got #{range.class}"
492
+ end
493
+
494
+ # Allowed range operators
495
+ allowed_operators = %w[gt gte lt lte]
496
+ validated_range = {}
497
+
498
+ if range.empty?
499
+ raise Errors::ValidationError,
500
+ "Empty 'range' object in #{filter_field} filter. At least one operator (gt, gte, lt, lte) must be provided"
501
+ end
502
+
503
+ range.each do |operator, value|
504
+ operator_str = operator.to_s
505
+
506
+ unless allowed_operators.include?(operator_str)
507
+ raise Errors::ValidationError,
508
+ "Invalid range operator '#{operator_str}' in #{filter_field} filter. " \
509
+ "Allowed operators: #{allowed_operators.join(', ')}"
510
+ end
511
+
512
+ # Validate date format (YYYYMMDD)
513
+ validated_date = validate_filter_date(value, filter_field, operator_str)
514
+ validated_range[operator_str] = validated_date
515
+ end
516
+
517
+ { "range" => validated_range }
518
+ end
519
+
520
+ # Validate date format for filter ranges
521
+ # @param date_value [String, Date] Date value to validate
522
+ # @param filter_field [String] Filter field name for error messages
523
+ # @param operator [String] Range operator for error messages
524
+ # @return [String] Validated date in YYYYMMDD format
525
+ # @raise [ValidationError] If date format is invalid
526
+ def validate_filter_date(date_value, filter_field, operator)
527
+ # Convert Date objects to string
528
+ return date_value.strftime("%Y%m%d") if date_value.is_a?(Date)
529
+
530
+ unless date_value.is_a?(String)
531
+ raise Errors::ValidationError,
532
+ "Invalid date type for '#{operator}' in #{filter_field} filter. Expected String or Date, got #{date_value.class}"
533
+ end
534
+
535
+ # Check if it's already in YYYYMMDD format
536
+ if date_value.match?(/^\d{8}$/)
537
+ # Validate that it's a real date
538
+ begin
539
+ year = date_value[0..3].to_i
540
+ month = date_value[4..5].to_i
541
+ day = date_value[6..7].to_i
542
+ Date.new(year, month, day)
543
+ return date_value
544
+ rescue ArgumentError
545
+ raise Errors::ValidationError,
546
+ "Invalid date '#{date_value}' for '#{operator}' in #{filter_field} filter. Not a valid date"
547
+ end
548
+ end
549
+
550
+ # Try to parse various date formats and convert to YYYYMMDD
551
+ begin
552
+ parsed_date = case date_value
553
+ when /^\d{4}-\d{2}-\d{2}$/ # YYYY-MM-DD
554
+ Date.parse(date_value)
555
+ when %r{^\d{4}/\d{2}/\d{2}$} # YYYY/MM/DD
556
+ Date.parse(date_value)
557
+ when %r{^\d{2}/\d{2}/\d{4}$} # MM/DD/YYYY
558
+ Date.strptime(date_value, "%m/%d/%Y")
559
+ when /^\d{2}-\d{2}-\d{4}$/ # MM-DD-YYYY
560
+ Date.strptime(date_value, "%m-%d-%Y")
561
+ else
562
+ Date.parse(date_value) # Let Date.parse try to handle it
563
+ end
564
+
565
+ parsed_date.strftime("%Y%m%d")
566
+ rescue ArgumentError
567
+ raise Errors::ValidationError,
568
+ "Invalid date format '#{date_value}' for '#{operator}' in #{filter_field} filter. " \
569
+ "Expected YYYYMMDD format (e.g., '20200101') or standard date formats (YYYY-MM-DD, etc.)"
570
+ end
571
+ end
337
572
  end
338
573
  end
@@ -41,14 +41,14 @@ module Rospatent
41
41
  # @param qn [String] Natural language search query
42
42
  # @param limit [Integer] Maximum number of results to return
43
43
  # @param offset [Integer] Offset for pagination
44
- # @param pre_tag [String] HTML tag to prepend to highlighted matches
45
- # @param post_tag [String] HTML tag to append to highlighted matches
44
+ # @param pre_tag [String, Array<String>] HTML tag(s) to prepend to highlighted matches
45
+ # @param post_tag [String, Array<String>] HTML tag(s) to append to highlighted matches
46
46
  # @param sort [Symbol, String] Sort option (:relevance, :pub_date, :filing_date)
47
- # @param group_by [Symbol, String] Grouping option (:patent_family)
47
+ # @param group_by [String] Grouping option ("family:docdb", "family:dwpi")
48
48
  # @param include_facets [Boolean] Whether to include facet information
49
49
  # @param filter [Hash] Filters to apply to the search
50
50
  # @param datasets [Array<String>] Datasets to search within
51
- # @param highlight [Boolean] Whether to highlight matches
51
+ # @param highlight [Hash] Advanced highlight configuration with profiles
52
52
  #
53
53
  # @return [Rospatent::SearchResult] Search result object
54
54
  def execute(
@@ -111,31 +111,35 @@ module Rospatent
111
111
  end
112
112
 
113
113
  # Validate highlighting parameters (only if provided)
114
- if params.key?(:highlight)
115
- validated[:highlight] = !!params[:highlight]
116
- if params[:highlight] && params[:pre_tag]
117
- validated[:pre_tag] =
118
- validate_string(params[:pre_tag], "pre_tag", max_length: 50)
119
- end
120
- if params[:highlight] && params[:post_tag]
121
- validated[:post_tag] =
122
- validate_string(params[:post_tag], "post_tag", max_length: 50)
114
+ # pre_tag and post_tag must be provided together
115
+ if params[:pre_tag] || params[:post_tag]
116
+ unless params[:pre_tag] && params[:post_tag]
117
+ raise Errors::ValidationError,
118
+ "Both pre_tag and post_tag must be provided together for highlighting"
123
119
  end
120
+
121
+ validated[:pre_tag] =
122
+ validate_string_or_array(params[:pre_tag], "pre_tag", max_length: 50, max_size: 10)
123
+ validated[:post_tag] =
124
+ validate_string_or_array(params[:post_tag], "post_tag", max_length: 50, max_size: 10)
124
125
  end
125
126
 
127
+ # Validate highlight parameter (complex object for advanced highlighting)
128
+ validated[:highlight] = validate_hash(params[:highlight], "highlight") if params[:highlight]
129
+
126
130
  # Validate sort parameter (only if provided)
127
131
  validated[:sort] = validate_sort_parameter(params[:sort]) if params[:sort]
128
132
 
129
133
  # Validate group_by parameter (only if provided)
130
134
  if params[:group_by]
131
- validated[:group_by] = validate_enum(params[:group_by], [:patent_family], "group_by")
135
+ validated[:group_by] = validate_string_enum(params[:group_by], %w[family:docdb family:dwpi], "group_by")
132
136
  end
133
137
 
134
138
  # Validate boolean parameters (only if provided)
135
139
  validated[:include_facets] = !params[:include_facets].nil? if params.key?(:include_facets)
136
140
 
137
141
  # Validate filter parameter
138
- validated[:filter] = validate_hash(params[:filter], "filter") if params[:filter]
142
+ validated[:filter] = validate_filter(params[:filter], "filter") if params[:filter]
139
143
 
140
144
  # Validate datasets parameter
141
145
  if params[:datasets]
@@ -161,18 +165,20 @@ module Rospatent
161
165
  payload[:limit] = params[:limit] if params[:limit]
162
166
  payload[:offset] = params[:offset] if params[:offset]
163
167
 
164
- # Add highlighting parameters (only if explicitly provided)
165
- if params.key?(:highlight)
166
- payload[:highlight] = params[:highlight]
167
- payload[:pre_tag] = params[:pre_tag] if params[:pre_tag]
168
- payload[:post_tag] = params[:post_tag] if params[:post_tag]
168
+ # Add highlighting tags (only if both are provided)
169
+ if params[:pre_tag] && params[:post_tag]
170
+ payload[:pre_tag] = params[:pre_tag]
171
+ payload[:post_tag] = params[:post_tag]
169
172
  end
170
173
 
174
+ # Add advanced highlight parameter (independent of tags)
175
+ payload[:highlight] = params[:highlight] if params[:highlight]
176
+
171
177
  # Add sort parameter (only if explicitly provided)
172
178
  payload[:sort] = params[:sort] if params[:sort]
173
179
 
174
180
  # Add grouping parameter (only if explicitly provided)
175
- payload[:group_by] = "patent_family" if params[:group_by] == :patent_family
181
+ payload[:group_by] = params[:group_by] if params[:group_by]
176
182
 
177
183
  # Add other parameters (only if explicitly provided)
178
184
  payload[:include_facets] = params[:include_facets] if params.key?(:include_facets)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rospatent
4
- VERSION = "1.2.0"
4
+ VERSION = "1.3.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rospatent
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aleksandr Dryzhuk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-06-04 00:00:00.000000000 Z
11
+ date: 2025-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -72,42 +72,42 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '1.59'
75
+ version: '1.76'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '1.59'
82
+ version: '1.76'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rubocop-minitest
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.34'
89
+ version: '0.38'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.34'
96
+ version: '0.38'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rubocop-rake
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0.6'
103
+ version: '0.7'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0.6'
110
+ version: '0.7'
111
111
  description: A comprehensive Ruby client for interacting with the Rospatent patent
112
112
  search API. Features include automatic caching, request validation, structured logging,
113
113
  error handling, and batch operations for efficient patent data retrieval.