dadata-rb 3.0.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 +7 -0
- data/.rubocop.yml +134 -0
- data/CHANGELOG.md +129 -0
- data/CONTRIBUTING.md +153 -0
- data/LICENSE.txt +21 -0
- data/README.md +603 -0
- data/Rakefile +16 -0
- data/dadata.gemspec +46 -0
- data/lib/dadata/api_exceptions.rb +35 -0
- data/lib/dadata/client/base.rb +272 -0
- data/lib/dadata/client/clean.rb +52 -0
- data/lib/dadata/client/profile.rb +51 -0
- data/lib/dadata/client/suggest.rb +86 -0
- data/lib/dadata/sensitive_data.rb +58 -0
- data/lib/dadata/version.rb +5 -0
- data/lib/dadata-rb.rb +6 -0
- data/lib/dadata.rb +421 -0
- data/lib/generators/dadata/USAGE +73 -0
- data/lib/generators/dadata/initializer_generator.rb +86 -0
- data/lib/generators/dadata/templates/dadata.rb.tt +24 -0
- data/lib/generators/dadata/templates/dadata_credentials.rb.tt +48 -0
- metadata +132 -0
data/lib/dadata.rb
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'dadata/version'
|
|
4
|
+
require_relative 'dadata/api_exceptions'
|
|
5
|
+
require_relative 'dadata/sensitive_data'
|
|
6
|
+
require_relative 'dadata/client/base'
|
|
7
|
+
require_relative 'dadata/client/clean'
|
|
8
|
+
require_relative 'dadata/client/suggest'
|
|
9
|
+
require_relative 'dadata/client/profile'
|
|
10
|
+
require 'logger'
|
|
11
|
+
|
|
12
|
+
# Ruby wrapper for DaData API
|
|
13
|
+
module Dadata
|
|
14
|
+
SUGGESTIONS_COUNT = 10
|
|
15
|
+
TIMEOUT_SEC = 3
|
|
16
|
+
MAX_SUGGESTIONS = 20
|
|
17
|
+
|
|
18
|
+
# SecureLogger extends Ruby's Logger class to provide automatic sanitization
|
|
19
|
+
# of sensitive data in log messages. It is used internally by the gem to ensure
|
|
20
|
+
# that sensitive information like API keys and secrets are not exposed in logs.
|
|
21
|
+
#
|
|
22
|
+
# @example
|
|
23
|
+
# logger = SecureLogger.new($stdout)
|
|
24
|
+
# logger.info('API-Key: secret123') # Logs: "API-Key: [FILTERED]"
|
|
25
|
+
class SecureLogger < Logger
|
|
26
|
+
include SensitiveData
|
|
27
|
+
|
|
28
|
+
# Creates a new SecureLogger instance with a custom formatter that sanitizes
|
|
29
|
+
# sensitive data in log messages.
|
|
30
|
+
#
|
|
31
|
+
# @param logdev [IO, String, nil] The log device to write to
|
|
32
|
+
def initialize(logdev = nil)
|
|
33
|
+
super
|
|
34
|
+
@formatter = proc do |severity, datetime, progname, msg|
|
|
35
|
+
msg = sanitize_message(msg.to_s)
|
|
36
|
+
"#{severity[0]}, [#{datetime}] #{severity} -- #{progname}: #{msg}\n"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Configuration class for the DaData API client
|
|
42
|
+
#
|
|
43
|
+
# @example
|
|
44
|
+
# Dadata.configure do |config|
|
|
45
|
+
# config.api_key = 'your_api_key'
|
|
46
|
+
# config.secret_key = 'your_secret_key'
|
|
47
|
+
# config.timeout_sec = 5
|
|
48
|
+
# end
|
|
49
|
+
class Configuration
|
|
50
|
+
include SensitiveData
|
|
51
|
+
|
|
52
|
+
attr_accessor :api_key, :secret_key, :timeout_sec,
|
|
53
|
+
:log_level, :log_request_bodies
|
|
54
|
+
attr_reader :suggestions_count, :logger
|
|
55
|
+
|
|
56
|
+
# Initialize a new Configuration instance with default values
|
|
57
|
+
#
|
|
58
|
+
# @return [Configuration]
|
|
59
|
+
def initialize
|
|
60
|
+
@suggestions_count = SUGGESTIONS_COUNT
|
|
61
|
+
@timeout_sec = TIMEOUT_SEC
|
|
62
|
+
@log_level = :info
|
|
63
|
+
# Request payloads carry PII (passports, names, phones); never logged unless
|
|
64
|
+
# a developer explicitly opts in for debugging.
|
|
65
|
+
@log_request_bodies = false
|
|
66
|
+
setup_logger
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Validates the configuration settings
|
|
70
|
+
#
|
|
71
|
+
# @raise [ConfigurationError] if any settings are invalid
|
|
72
|
+
# @return [void]
|
|
73
|
+
def validate!
|
|
74
|
+
if timeout_sec && timeout_sec <= 0
|
|
75
|
+
raise ConfigurationError, 'Timeout must be positive'
|
|
76
|
+
end
|
|
77
|
+
if suggestions_count && (suggestions_count < 1 || suggestions_count > MAX_SUGGESTIONS)
|
|
78
|
+
raise ConfigurationError, "Suggestions count must be between 1 and #{MAX_SUGGESTIONS}"
|
|
79
|
+
end
|
|
80
|
+
if api_key.nil? || api_key.strip.empty?
|
|
81
|
+
raise ConfigurationError, "API key can't be blank"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Sets the suggestions count, enforcing the maximum limit
|
|
86
|
+
#
|
|
87
|
+
# @param value [Integer] The number of suggestions to return
|
|
88
|
+
# @raise [ConfigurationError] if value is less than 1
|
|
89
|
+
# @return [Integer] The actual suggestions count (may be capped at MAX_SUGGESTIONS)
|
|
90
|
+
def suggestions_count=(value)
|
|
91
|
+
return unless value
|
|
92
|
+
if value < 1
|
|
93
|
+
raise ConfigurationError, "Suggestions count must be between 1 and #{MAX_SUGGESTIONS}"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
@suggestions_count = [value, MAX_SUGGESTIONS].min
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Sets the logger, wrapping it in SecureLogger if necessary
|
|
100
|
+
#
|
|
101
|
+
# @param logger [Logger] The logger to use
|
|
102
|
+
# @return [SecureLogger] The wrapped logger
|
|
103
|
+
def logger=(logger)
|
|
104
|
+
@logger = if logger.is_a?(SecureLogger)
|
|
105
|
+
logger
|
|
106
|
+
else
|
|
107
|
+
SecureLogger.new(logger.instance_variable_get(:@logdev))
|
|
108
|
+
end
|
|
109
|
+
@logger.level = logger.level if logger.respond_to?(:level)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
# Sets up the default logger
|
|
115
|
+
#
|
|
116
|
+
# @return [void]
|
|
117
|
+
def setup_logger
|
|
118
|
+
self.logger = Logger.new($stdout)
|
|
119
|
+
@logger.level = log_level || :info
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
class << self
|
|
124
|
+
attr_accessor :configuration
|
|
125
|
+
|
|
126
|
+
def configure
|
|
127
|
+
self.configuration ||= Configuration.new
|
|
128
|
+
yield(configuration)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def api_key
|
|
132
|
+
configuration&.api_key
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def secret_key
|
|
136
|
+
configuration&.secret_key
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def suggestions_count
|
|
140
|
+
configuration&.suggestions_count || SUGGESTIONS_COUNT
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def timeout_sec
|
|
144
|
+
configuration&.timeout_sec || TIMEOUT_SEC
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Глобальный клиент к API Dadata
|
|
149
|
+
class Client
|
|
150
|
+
def initialize(token = Dadata.api_key, secret = Dadata.secret_key)
|
|
151
|
+
@cleaner = CleanClient.new(token, secret)
|
|
152
|
+
@suggestions = SuggestClient.new(token, secret)
|
|
153
|
+
@profile = ProfileClient.new(token, secret)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Стандартизация. Приводит в порядок и обогащает дополнительной информацией.
|
|
157
|
+
#
|
|
158
|
+
# @param [String] name Тип применяемой стандартизации:
|
|
159
|
+
# +address+ — адрес,
|
|
160
|
+
# +phone+ — телефонный номер,
|
|
161
|
+
# +passport+ — серия и номер паспорта,
|
|
162
|
+
# +name+ — ФИО,
|
|
163
|
+
# +email+ — электронная почта,
|
|
164
|
+
# +birthdate+ — дата,
|
|
165
|
+
# +vehicle+ — марка автомобиля,
|
|
166
|
+
# +simple_party_name+ — наименование юрлица
|
|
167
|
+
# @param [String] source Строка, подлежащая обработке
|
|
168
|
+
# @return [Object]
|
|
169
|
+
#
|
|
170
|
+
# @see https://dadata.ru/api/clean/
|
|
171
|
+
# @note 15 коп./запись. Максимальная частота запросов — 20 в секунду с одного IP-адреса.
|
|
172
|
+
# @note Максимальная частота создания новых соединений — 60 в минуту с одного IP-адреса.
|
|
173
|
+
# @note Дадата не поддерживает вызов этого метода из браузерного JavaScript.
|
|
174
|
+
# Иначе злоумышленник мог бы похитить секретный ключ и использовать API за ваш счет.
|
|
175
|
+
def clean(name, source)
|
|
176
|
+
with_logging("cleaning #{name}") { @cleaner.clean(name, source) }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Стандартизация составных записей
|
|
180
|
+
#
|
|
181
|
+
# @param [Array<String>] structure Структура записи, содержит поля:
|
|
182
|
+
# +AS_IS+ — оставить как есть (не стандартизировать),
|
|
183
|
+
# +SIMPLE_PARTY_NAME+ — разобрать наименование,
|
|
184
|
+
# +NAME+ — разобрать как ФИО,
|
|
185
|
+
# +BIRTHDATE+ — разобрать как дату,
|
|
186
|
+
# +ADDRESS+ — разобрать как адрес,
|
|
187
|
+
# +PHONE+ — разобрать как телефон,
|
|
188
|
+
# +PASSPORT+ — номер и серия паспорта,
|
|
189
|
+
# +EMAIL+ — адрес электронной почты,
|
|
190
|
+
# +VEHICLE+ — марка и модель автомобиля
|
|
191
|
+
# @param [Array<String>] record Запись, подлежащая обработке; порядок полей должен соответствовать +structure+
|
|
192
|
+
# @return [Object]
|
|
193
|
+
#
|
|
194
|
+
# @see https://dadata.ru/api/clean/record/
|
|
195
|
+
# @note Максимальное количество полей в одной записи:
|
|
196
|
+
# 1 ФИО,
|
|
197
|
+
# 3 адреса,
|
|
198
|
+
# 3 телефона,
|
|
199
|
+
# 3 email,
|
|
200
|
+
# 1 дата рождения,
|
|
201
|
+
# 1 паспорт,
|
|
202
|
+
# 1 автомобиль.
|
|
203
|
+
# @note 15 коп./запись. Максимальная частота запросов — 20 в секунду с одного IP-адреса.
|
|
204
|
+
# @note Максимальная частота создания новых соединений — 60 в минуту с одного IP-адреса.
|
|
205
|
+
# @note Дадата не поддерживает вызов этого метода из браузерного JavaScript.
|
|
206
|
+
# Иначе злоумышленник мог бы похитить секретный ключ и использовать API за ваш счет.
|
|
207
|
+
def clean_record(structure, record)
|
|
208
|
+
with_logging('cleaning record') { @cleaner.clean_record(structure, record) }
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Обратное геокодирование (адрес по координатам)
|
|
212
|
+
#
|
|
213
|
+
# @param [String<address|postal_unit>] name Тип поиска
|
|
214
|
+
# @param [Numeric] lat Географическая широта
|
|
215
|
+
# @param [Numeric] lon Географическая долгота
|
|
216
|
+
# @param [Integer] radius_meters Радиус поиска в метрах, опционально, default 100, max 1000
|
|
217
|
+
# @param [Integer] **kwargs(:count) Количество результатов, опционально, default 10, max 20
|
|
218
|
+
# @param [String<ru|en>] **kwargs(:language) На каком языке вернуть результат, опционально, default "ru"
|
|
219
|
+
# @return [Object]
|
|
220
|
+
#
|
|
221
|
+
# @see https://dadata.ru/api/geolocate/
|
|
222
|
+
# @see https://dadata.ru/api/suggest/postal_unit/
|
|
223
|
+
# @note Метод бесплатный до 10000 запросов в день, или в соответствии с тарифным планом.
|
|
224
|
+
# @note Максимальная частота запросов — 30 в секунду с одного IP-адреса.
|
|
225
|
+
# @note Максимальная частота создания новых соединений — 60 в минуту с одного IP-адреса.
|
|
226
|
+
def geolocate(name, lat, lon, radius_meters = 100, **)
|
|
227
|
+
with_logging('geolocating') { @suggestions.geolocate(name, lat, lon, radius_meters, **) }
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Город по IP-адресу
|
|
231
|
+
#
|
|
232
|
+
# @param [String] ip IP-адрес
|
|
233
|
+
# @param [String<ru|en>] **kwargs(:language) На каком языке вернуть результат, опционально, default "ru"
|
|
234
|
+
# @return [Object]
|
|
235
|
+
#
|
|
236
|
+
# @see https://dadata.ru/api/iplocate/
|
|
237
|
+
# @note Метод бесплатный до 10000 запросов в день, или в соответствии с тарифным планом.
|
|
238
|
+
# @note Максимальная частота запросов — 30 в секунду с одного IP-адреса.
|
|
239
|
+
# @note Максимальная частота создания новых соединений — 60 в минуту с одного IP-адреса.
|
|
240
|
+
def iplocate(ip, **)
|
|
241
|
+
with_logging('iplocating') { @suggestions.iplocate(ip, **) }
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Подсказки
|
|
245
|
+
#
|
|
246
|
+
# @param [String] name Тип применяемой подсказки:
|
|
247
|
+
# +address+ — адрес,
|
|
248
|
+
# +postal_unit+ — почтовое отделение,
|
|
249
|
+
# +party+ — организация,
|
|
250
|
+
# +bank+ — банк,
|
|
251
|
+
# +fio+ — ФИО,
|
|
252
|
+
# +fms_unit+ — отделение ФМС,
|
|
253
|
+
# +email+ — адрес электронной почты,
|
|
254
|
+
# +car_brand+ — марка автомобиля,
|
|
255
|
+
# +fns_unit+ — отделение ФНС,
|
|
256
|
+
# +fts_unit+ — отделение ФТС,
|
|
257
|
+
# +region_court+ — отделение регионального суда,
|
|
258
|
+
# +country+ — страны,
|
|
259
|
+
# +metro+ — станция метро,
|
|
260
|
+
# +mktu+ — классификатор МКТУ,
|
|
261
|
+
# +currency+ — справочник валют,
|
|
262
|
+
# +okved2+ — классификатор ОКВЭД 2,
|
|
263
|
+
# +okpd2+ — классификатор ОКПД 2
|
|
264
|
+
# @param [String] query Текст запроса
|
|
265
|
+
# @param [Integer] count Количество результатов, опционально, default 10, max 20
|
|
266
|
+
# @param [Array<Object>] **kwargs(:filters) фильтрация результата (не для всех `name`), опционально
|
|
267
|
+
# @param [String<ru|en>] **kwargs(:language) На каком языке вернуть результат, опционально, default "ru"
|
|
268
|
+
# @return [Object]
|
|
269
|
+
#
|
|
270
|
+
# @see https://dadata.ru/api/find-address
|
|
271
|
+
# @see https://dadata.ru/api/suggest/postal_unit
|
|
272
|
+
# @see https://dadata.ru/api/suggest/party
|
|
273
|
+
# @see https://dadata.ru/api/suggest/bank
|
|
274
|
+
# @see https://dadata.ru/api/suggest/name
|
|
275
|
+
# @see https://dadata.ru/api/suggest/fms_unit
|
|
276
|
+
# @see https://dadata.ru/api/suggest/email
|
|
277
|
+
# @see https://dadata.ru/api/suggest/car_brand
|
|
278
|
+
# @see https://dadata.ru/api/suggest/fns_unit
|
|
279
|
+
# @see https://dadata.ru/api/suggest/fts_unit
|
|
280
|
+
# @see https://dadata.ru/api/suggest/region_court
|
|
281
|
+
# @see https://dadata.ru/api/suggest/country
|
|
282
|
+
# @see https://dadata.ru/api/suggest/metro
|
|
283
|
+
# @see https://dadata.ru/api/suggest/mktu
|
|
284
|
+
# @see https://dadata.ru/api/suggest/currency
|
|
285
|
+
# @see https://dadata.ru/api/suggest/okved2
|
|
286
|
+
# @see https://dadata.ru/api/suggest/okpd2
|
|
287
|
+
# @note Длина запроса (параметр query) — не более 300 символов.
|
|
288
|
+
# @note Метод бесплатный до 10000 запросов в день, или в соответствии с тарифным планом.
|
|
289
|
+
# @note Максимальная частота запросов — 30 в секунду с одного IP-адреса.
|
|
290
|
+
# @note Максимальная частота создания новых соединений — 60 в минуту с одного IP-адреса.
|
|
291
|
+
def suggest(name, query, count = Dadata.suggestions_count, **)
|
|
292
|
+
with_logging('suggesting') { @suggestions.suggest(name, query, [count, MAX_SUGGESTIONS].min, **) }
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Поиск по коду
|
|
296
|
+
#
|
|
297
|
+
# @param [String] name Тип стандартизации:
|
|
298
|
+
# +address+ — адрес,
|
|
299
|
+
# +postal_unit+ — почтовое отделение,
|
|
300
|
+
# +party+ — организация,
|
|
301
|
+
# +bank+ — банк,
|
|
302
|
+
# +fms_unit+ — отделение ФМС,
|
|
303
|
+
# +car_brand+ — марка автомобиля,
|
|
304
|
+
# +fns_unit+ — отделение ФНС,
|
|
305
|
+
# +fts_unit+ — отделение ФТС,
|
|
306
|
+
# +region_court+ — отделение регионального суда,
|
|
307
|
+
# +delivery+ — идентификатор города в СДЭК, Boxberry и DPD,
|
|
308
|
+
# +country+ — справочник стран,
|
|
309
|
+
# +mktu+ — классификатор МКТУ,
|
|
310
|
+
# +currency+ — справочник валют,
|
|
311
|
+
# +okved2+ — классификатор ОКВЭД 2,
|
|
312
|
+
# +okpd2+ — классификатор ОКПД 2,
|
|
313
|
+
# +oktmo+ — классификатор ОКТМО
|
|
314
|
+
# @param [String] query Текст запроса, обязательно
|
|
315
|
+
# @param [Integer] count Количество результатов, опционально, default 10
|
|
316
|
+
# @param [String] **kwargs(:kpp) поиск по филиалам для `party`, опционально
|
|
317
|
+
# @param [String<MAIN|BRANCH>] **kwargs(:branch_type) тип филиала для `party`, опционально
|
|
318
|
+
# @param [String<LEGAL|INDIVIDUAL>] **kwargs(:type) юрлицо или ИП для `party`, опционально
|
|
319
|
+
# @param [Array<String>] **kwargs(:status) status для `party`, опционально
|
|
320
|
+
# @param [String<ru|en>] **kwargs(:language) На каком языке вернуть результат (не для всех `name`), опционально, default ru
|
|
321
|
+
# @return [Object]
|
|
322
|
+
#
|
|
323
|
+
# @see https://dadata.ru/api/find-address
|
|
324
|
+
# @see https://dadata.ru/api/suggest/postal_unit
|
|
325
|
+
# @see https://dadata.ru/api/find-party
|
|
326
|
+
# @see https://dadata.ru/api/find-bank
|
|
327
|
+
# @see https://dadata.ru/api/suggest/fms_unit
|
|
328
|
+
# @see https://dadata.ru/api/suggest/car_brand
|
|
329
|
+
# @see https://dadata.ru/api/suggest/fns_unit
|
|
330
|
+
# @see https://dadata.ru/api/suggest/fts_unit
|
|
331
|
+
# @see https://dadata.ru/api/suggest/region_court
|
|
332
|
+
# @see https://dadata.ru/api/delivery
|
|
333
|
+
# @see https://dadata.ru/api/suggest/country
|
|
334
|
+
# @see https://dadata.ru/api/suggest/mktu
|
|
335
|
+
# @see https://dadata.ru/api/suggest/currency
|
|
336
|
+
# @see https://dadata.ru/api/suggest/okved2
|
|
337
|
+
# @see https://dadata.ru/api/suggest/okpd2
|
|
338
|
+
# @see https://dadata.ru/api/suggest/oktmo
|
|
339
|
+
# @note Длина запроса (параметр query) — не более 300 символов.
|
|
340
|
+
# @note Метод бесплатный до 10000 запросов в день, или в соответствии с тарифным планом.
|
|
341
|
+
# @note Максимальная частота запросов — 30 в секунду с одного IP-адреса.
|
|
342
|
+
# @note Максимальная частота создания новых соединений — 60 в минуту с одного IP-адреса.
|
|
343
|
+
def find_by_id(name, query, count = Dadata.suggestions_count, **)
|
|
344
|
+
with_logging('finding by id') { @suggestions.find_by_id(name, query, [count, MAX_SUGGESTIONS].min, **) }
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# Компания по email
|
|
348
|
+
#
|
|
349
|
+
# @param [String] query, Текст запроса
|
|
350
|
+
# @return [Array<Object>]
|
|
351
|
+
#
|
|
352
|
+
# @see https://dadata.ru/api/find-company/by-email/
|
|
353
|
+
# @note Длина запроса (параметр query) — не более 300 символов.
|
|
354
|
+
# @note 5 руб./запрос. Количество запросов — в соответствии с тарифным планом.
|
|
355
|
+
# @note Максимальная частота запросов — 30 в секунду с одного IP-адреса.
|
|
356
|
+
# @note Максимальная частота создания новых соединений — 60 в минуту с одного IP-адреса.
|
|
357
|
+
def find_by_email(query)
|
|
358
|
+
with_logging('finding by email') { @suggestions.find_by_email(query) }
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Поиск аффилированных компаний
|
|
362
|
+
#
|
|
363
|
+
# @param [String] query, Текст запроса
|
|
364
|
+
# @param [Integer] count Количество результатов, опционально, default 10
|
|
365
|
+
# @param [Array<String>] **kwargs(:scope), Где искать, опционально, default ["FOUNDERS", "MANAGERS"]
|
|
366
|
+
# @return [Object]
|
|
367
|
+
#
|
|
368
|
+
# @see https://dadata.ru/api/find-affiliated/
|
|
369
|
+
# @note Доступно только на тарифе «Максимальный»
|
|
370
|
+
# @note Длина запроса (параметр query) — не более 300 символов.
|
|
371
|
+
# @note Количество запросов — в соответствии с тарифным планом.
|
|
372
|
+
# @note Максимальная частота запросов — 30 в секунду с одного IP-адреса.
|
|
373
|
+
# @note Максимальная частота создания новых соединений — 60 в минуту с одного IP-адреса.
|
|
374
|
+
def find_affiliated(query, count = Dadata.suggestions_count, **)
|
|
375
|
+
with_logging('finding affiliated') { @suggestions.find_affiliated(query, [count, MAX_SUGGESTIONS].min, **) }
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Баланс пользователя
|
|
379
|
+
#
|
|
380
|
+
# @see https://dadata.ru/api/balance
|
|
381
|
+
def balance
|
|
382
|
+
with_logging('getting balance') { @profile.balance }
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Статистика использования
|
|
386
|
+
#
|
|
387
|
+
# @see https://dadata.ru/api/stat
|
|
388
|
+
def daily_stats(date = nil)
|
|
389
|
+
with_logging('getting daily stats') { @profile.daily_stats(date) }
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# Версии справочников
|
|
393
|
+
#
|
|
394
|
+
# @see https://dadata.ru/api/version
|
|
395
|
+
def versions
|
|
396
|
+
with_logging('getting versions') { @profile.versions }
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def close
|
|
400
|
+
with_logging('closing client') do
|
|
401
|
+
@cleaner.close
|
|
402
|
+
@suggestions.close
|
|
403
|
+
@profile.close
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
private
|
|
408
|
+
|
|
409
|
+
# Runs the block, logging any error with the operation label before
|
|
410
|
+
# re-raising. The label completes the sentence "Error <label>: <message>".
|
|
411
|
+
#
|
|
412
|
+
# @param label [String] Operation description for the log line
|
|
413
|
+
# @return [Object] The block's return value
|
|
414
|
+
def with_logging(label)
|
|
415
|
+
yield
|
|
416
|
+
rescue StandardError => e
|
|
417
|
+
Dadata.configuration&.logger&.error("Error #{label}: #{e.message}")
|
|
418
|
+
raise
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Description:
|
|
2
|
+
Creates a DaData API initializer for your Rails application.
|
|
3
|
+
By default, it will store API keys in Rails credentials and create an initializer
|
|
4
|
+
that reads from them.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
rails generate dadata:initializer
|
|
8
|
+
|
|
9
|
+
This will create:
|
|
10
|
+
config/initializers/dadata.rb
|
|
11
|
+
And update:
|
|
12
|
+
config/credentials.yml.enc
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
--api-key=KEY # Your DaData API key
|
|
16
|
+
# Default: DADATA_API_KEY
|
|
17
|
+
|
|
18
|
+
--secret-key=KEY # Your DaData secret key
|
|
19
|
+
# Default: DADATA_SECRET_KEY
|
|
20
|
+
|
|
21
|
+
--[no-]use-credentials # Whether to store API keys in Rails credentials
|
|
22
|
+
# Default: true
|
|
23
|
+
|
|
24
|
+
--timeout=SECONDS # API request timeout in seconds
|
|
25
|
+
# Default: 3
|
|
26
|
+
|
|
27
|
+
--suggestions-count=N # Default number of suggestions to return
|
|
28
|
+
# Default: 10
|
|
29
|
+
|
|
30
|
+
Example with options:
|
|
31
|
+
rails generate dadata:initializer \
|
|
32
|
+
--api-key=your_api_key \
|
|
33
|
+
--secret-key=your_secret_key \
|
|
34
|
+
--no-use-credentials \
|
|
35
|
+
--timeout=5 \
|
|
36
|
+
--suggestions-count=20
|
|
37
|
+
|
|
38
|
+
Описание:
|
|
39
|
+
Создает инициализатор DaData API для вашего Rails-приложения.
|
|
40
|
+
По умолчанию, API-ключи будут храниться в Rails credentials,
|
|
41
|
+
а инициализатор будет настроен на их использование.
|
|
42
|
+
|
|
43
|
+
Пример:
|
|
44
|
+
rails generate dadata:initializer
|
|
45
|
+
|
|
46
|
+
Будут созданы файлы:
|
|
47
|
+
config/initializers/dadata.rb
|
|
48
|
+
И обновлены:
|
|
49
|
+
config/credentials.yml.enc
|
|
50
|
+
|
|
51
|
+
Опции:
|
|
52
|
+
--api-key=КЛЮЧ # Ваш API-ключ DaData
|
|
53
|
+
# По умолчанию: DADATA_API_KEY
|
|
54
|
+
|
|
55
|
+
--secret-key=КЛЮЧ # Ваш секретный ключ DaData
|
|
56
|
+
# По умолчанию: DADATA_SECRET_KEY
|
|
57
|
+
|
|
58
|
+
--[no-]use-credentials # Использовать ли Rails credentials для хранения ключей
|
|
59
|
+
# По умолчанию: true
|
|
60
|
+
|
|
61
|
+
--timeout=СЕКУНДЫ # Таймаут запросов в секундах
|
|
62
|
+
# По умолчанию: 3
|
|
63
|
+
|
|
64
|
+
--suggestions-count=N # Количество подсказок по умолчанию
|
|
65
|
+
# По умолчанию: 10
|
|
66
|
+
|
|
67
|
+
Пример с опциями:
|
|
68
|
+
rails generate dadata:initializer \
|
|
69
|
+
--api-key=ваш_api_ключ \
|
|
70
|
+
--secret-key=ваш_секретный_ключ \
|
|
71
|
+
--no-use-credentials \
|
|
72
|
+
--timeout=5 \
|
|
73
|
+
--suggestions-count=20
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators'
|
|
4
|
+
|
|
5
|
+
module Dadata
|
|
6
|
+
module Generators
|
|
7
|
+
# Rails generator for DaData API configuration
|
|
8
|
+
class InitializerGenerator < Rails::Generators::Base
|
|
9
|
+
source_root File.expand_path('templates', __dir__)
|
|
10
|
+
|
|
11
|
+
class_option :api_key,
|
|
12
|
+
type: :string,
|
|
13
|
+
default: 'DADATA_API_KEY',
|
|
14
|
+
desc: 'Your DaData API key'
|
|
15
|
+
|
|
16
|
+
class_option :secret_key,
|
|
17
|
+
type: :string,
|
|
18
|
+
default: 'DADATA_SECRET_KEY',
|
|
19
|
+
desc: 'Your DaData secret key'
|
|
20
|
+
|
|
21
|
+
class_option :use_credentials,
|
|
22
|
+
type: :boolean,
|
|
23
|
+
default: true,
|
|
24
|
+
desc: 'Store API keys in Rails credentials'
|
|
25
|
+
|
|
26
|
+
class_option :timeout,
|
|
27
|
+
type: :numeric,
|
|
28
|
+
default: 3,
|
|
29
|
+
desc: 'API request timeout in seconds'
|
|
30
|
+
|
|
31
|
+
class_option :suggestions_count,
|
|
32
|
+
type: :numeric,
|
|
33
|
+
default: 10,
|
|
34
|
+
desc: 'Default number of suggestions to return'
|
|
35
|
+
|
|
36
|
+
def create_initializer
|
|
37
|
+
@api_key = options[:api_key]
|
|
38
|
+
@secret_key = options[:secret_key]
|
|
39
|
+
@timeout = options[:timeout]
|
|
40
|
+
@suggestions_count = options[:suggestions_count]
|
|
41
|
+
|
|
42
|
+
if options[:use_credentials]
|
|
43
|
+
create_credentials
|
|
44
|
+
template 'dadata_credentials.rb', 'config/initializers/dadata.rb'
|
|
45
|
+
else
|
|
46
|
+
template 'dadata.rb', 'config/initializers/dadata.rb'
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def create_credentials
|
|
53
|
+
return unless options[:use_credentials]
|
|
54
|
+
|
|
55
|
+
# Skip if both keys are already in credentials
|
|
56
|
+
credentials = Rails.application.credentials.dadata
|
|
57
|
+
return if credentials&.api_key.present? && credentials&.secret_key.present?
|
|
58
|
+
|
|
59
|
+
# Create or update credentials
|
|
60
|
+
content = <<~YAML
|
|
61
|
+
dadata:
|
|
62
|
+
api_key: #{@api_key}
|
|
63
|
+
secret_key: #{@secret_key}
|
|
64
|
+
YAML
|
|
65
|
+
|
|
66
|
+
# Create credentials directory if it doesn't exist
|
|
67
|
+
FileUtils.mkdir_p(File.dirname(Rails.application.credentials.content_path))
|
|
68
|
+
|
|
69
|
+
if File.exist?(Rails.application.credentials.content_path)
|
|
70
|
+
# If credentials exist but don't have dadata config, append it
|
|
71
|
+
current_content = File.read(Rails.application.credentials.content_path)
|
|
72
|
+
if current_content.match?(/^dadata:/m)
|
|
73
|
+
say_status :skip, 'credentials already contain DaData configuration', :yellow
|
|
74
|
+
else
|
|
75
|
+
File.write(Rails.application.credentials.content_path, "#{current_content}\n#{content}")
|
|
76
|
+
say_status :update, 'config/credentials.yml.enc', :green
|
|
77
|
+
end
|
|
78
|
+
else
|
|
79
|
+
# Create new credentials file
|
|
80
|
+
File.write(Rails.application.credentials.content_path, content)
|
|
81
|
+
say_status :create, 'config/credentials.yml.enc', :green
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Конфигурация клиента DaData API
|
|
4
|
+
#
|
|
5
|
+
# Этот файл создан генератором dadata:initializer и содержит
|
|
6
|
+
# настройки для работы с API DaData.
|
|
7
|
+
#
|
|
8
|
+
# @see https://dadata.ru/api
|
|
9
|
+
# @see https://hub.mos.ru/ad/dadata
|
|
10
|
+
Dadata.configure do |config|
|
|
11
|
+
# Ваш API-ключ DaData
|
|
12
|
+
# Можно получить в личном кабинете: https://dadata.ru/profile/#info
|
|
13
|
+
config.api_key = '<%= @api_key %>'
|
|
14
|
+
|
|
15
|
+
# Ваш секретный ключ DaData
|
|
16
|
+
# Требуется для работы с некоторыми методами API
|
|
17
|
+
config.secret_key = '<%= @secret_key %>'
|
|
18
|
+
|
|
19
|
+
# Таймаут запросов в секундах
|
|
20
|
+
config.timeout_sec = <%= @timeout %>
|
|
21
|
+
|
|
22
|
+
# Количество подсказок в методах подсказок (suggest)
|
|
23
|
+
config.suggestions_count = <%= @suggestions_count %>
|
|
24
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Конфигурация клиента DaData API
|
|
5
|
+
#
|
|
6
|
+
# Этот инициализатор использует Rails credentials для хранения
|
|
7
|
+
# конфиденциальной информации. API-ключи хранятся в файле
|
|
8
|
+
# config/credentials.yml.enc в разделе 'dadata'.
|
|
9
|
+
#
|
|
10
|
+
# Для редактирования credentials используйте команду:
|
|
11
|
+
# rails credentials:edit
|
|
12
|
+
#
|
|
13
|
+
# Ожидаемый формат:
|
|
14
|
+
# dadata:
|
|
15
|
+
# api_key: ваш_api_ключ
|
|
16
|
+
# secret_key: ваш_секретный_ключ
|
|
17
|
+
#
|
|
18
|
+
# @example Редактирование credentials
|
|
19
|
+
# rails credentials:edit
|
|
20
|
+
#
|
|
21
|
+
# @see https://dadata.ru/api
|
|
22
|
+
# @see https://hub.mos.ru/ad/dadata
|
|
23
|
+
# @see https://guides.rubyonrails.org/security.html#custom-credentials
|
|
24
|
+
|
|
25
|
+
Dadata.configure do |config|
|
|
26
|
+
credentials = Rails.application.credentials[:dadata]
|
|
27
|
+
|
|
28
|
+
if credentials.nil?
|
|
29
|
+
raise KeyError, 'Секция dadata не найдена в credentials. Запустите rails credentials:edit для её добавления.'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Получаем API-ключ из credentials
|
|
33
|
+
config.api_key = credentials.api_key
|
|
34
|
+
|
|
35
|
+
# Получаем секретный ключ из credentials (опционально)
|
|
36
|
+
config.secret_key = credentials.secret_key if credentials.respond_to?(:secret_key)
|
|
37
|
+
|
|
38
|
+
# Таймаут запросов в секундах
|
|
39
|
+
config.timeout_sec = <%= @timeout %>
|
|
40
|
+
|
|
41
|
+
# Количество подсказок в методах подсказок (suggest)
|
|
42
|
+
config.suggestions_count = <%= @suggestions_count %>
|
|
43
|
+
|
|
44
|
+
# Проверка наличия API-ключа
|
|
45
|
+
if config.api_key.nil?
|
|
46
|
+
raise KeyError, 'API-ключ DaData не найден в credentials. Запустите rails credentials:edit для его добавления.'
|
|
47
|
+
end
|
|
48
|
+
end
|