sealine_insurance 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2570d0c8577ccddecace6f195f828b55fed6ccd4
4
- data.tar.gz: 4edc40e5c7d78a39b566c283e3e26277a8454b72
3
+ metadata.gz: 899ba793d64bc085d6d163ccf4c9f33d708e3f6b
4
+ data.tar.gz: 954959b1f88ae37f6694bf3456b22fb3b80a1b4e
5
5
  SHA512:
6
- metadata.gz: acdbb4f62702688a3646d15be8b3543990e77d48c3eb2195b6b615e1d2f287f25083c7c4c2e97e7e676534a1719745597b6e4c1070c108a775d5a6fb6fb760b7
7
- data.tar.gz: 49102b678d43b9927e7ab66eeb014a217118ecece16ef5ad1f091fb4e281dbe2e06463776b80310d23fe78459bcb0092050b077e9e679ea6e8ad57c2761a5373
6
+ metadata.gz: 7d9eac9bc6fe8b5e90326227b94348251e1e3342e06cf1e80c054f34319608be484cc0f38a166bd581700f16bffb88095a57a01f97b6e1c6bcf9fbc40e9e59d6
7
+ data.tar.gz: 61a12fe07c3bd971d8877fae0529d01494c300e880d15aabf763db680ca5b61854b5173ae010456311b4581a55d1665e1975227419d3162005e54a1458b96e4a
data/README.md CHANGED
@@ -18,7 +18,157 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- TODO: Write usage instructions here
21
+ Перед началом работы нужно создать клиента, задав необходимые настройки:
22
+
23
+ ```ruby
24
+ client = SealineInsurance::Client.new(
25
+ token: '0123456789abcdef', # required
26
+ host: 'sealine.ru', # optional, default: dev.sealine.ru
27
+ timeout: 10, # optional
28
+ open_timeout: 15, # optional
29
+ logger: Logger.new(...), # optional
30
+ )
31
+ ```
32
+
33
+ ### 1. Запрос информации
34
+
35
+ Запрос типов продуктов:
36
+
37
+ ```ruby
38
+ result = client.product_types
39
+ result.success?
40
+ # => true
41
+ result.body
42
+ # => {
43
+ # "meta": {
44
+ # ...
45
+ # },
46
+ # "objects": [
47
+ # {
48
+ # "id": 6,
49
+ # "code": "transport",
50
+ # "title": "НС пассажирских перевозок",
51
+ # "details": "https://sealine.ru/api/v1/classifiers/product-type/6.json"
52
+ # }
53
+ # ]
54
+ # }
55
+ ```
56
+
57
+ Аналогично можно запрашивать другую информацию:
58
+
59
+ ```ruby
60
+ # списки статусов
61
+ client.calculate_status_list
62
+ client.order_status_list
63
+ client.payment_status_list
64
+
65
+ # продукты и их типы
66
+ client.product_types
67
+ client.product_type(id: 6)
68
+ client.products
69
+ client.search_products(product_type: 'transport')
70
+
71
+ # кастомные запросы информации
72
+ client.classifiers(path: 'product/vsk_trans')
73
+ client.classifiers(path: 'product/component-group')
74
+ ```
75
+
76
+ ### 2. Расчет стоимости страховки
77
+
78
+ Операция расчета асинхроная, в рамках одного расчета нужно выполнять несколько запросов к API. Эта логика инкапсулирована в класс операции.
79
+
80
+ Запуск расчета:
81
+
82
+ ```ruby
83
+ operation = client.calculate(
84
+ product_type: 'transport',
85
+ products: %w[vsk_trans],
86
+ ticket_price: Money.new(100, 'RUB'),
87
+ )
88
+ ```
89
+
90
+ Операция может быть завершена сразу, при первом запросе.
91
+
92
+ Метод `finished?` проверяет статус без дополнительного запроса к API:
93
+
94
+ ```ruby
95
+ operation.finished?
96
+ # => false
97
+ ```
98
+
99
+ Далее необходимо запрашивать актуальный статус из API до тех пор, пока `finished?` не вернет `true`.
100
+
101
+ **Метод `finished?` не делает дополнительных запросов к API**, за это отвечает метод `fetch_status!`:
102
+
103
+ ```ruby
104
+ operation.fetch_status!
105
+ operation.finished?
106
+ # => false
107
+
108
+ sleep(2)
109
+ operation.fetch_status!
110
+ operation.finished?
111
+ # => true
112
+ ```
113
+
114
+ После завершения операции можно получить ее результат:
115
+
116
+ ```ruby
117
+ operation.success?
118
+ # => true
119
+ operation.result.status
120
+ # => 'DONE'
121
+ operation.result.price
122
+ # => #<Money fractional:70 currency:RUB>
123
+ ```
124
+
125
+ Если с ошибкой:
126
+
127
+ ```ruby
128
+ operation.success?
129
+ # => false
130
+ operation.result.error_code
131
+ # => 'unauthorized'
132
+ operation.result.error_message
133
+ # => 'Недопустимый токен.'
134
+ ```
135
+
136
+ ### 3. Создание страховки
137
+
138
+ ```ruby
139
+ operation = client.create_order(
140
+ product_type: 'transport',
141
+ product: 'vsk_trans',
142
+ ticket_number: 12345678,
143
+ ticket_price: Money.new(100, 'RUB'),
144
+ departure_datetime: Time.new(2018, 8, 1, 10, 0, 0),
145
+ arrival_datetime: Time.new(2018, 8, 1, 18, 0, 0),
146
+ insured_first_name: 'Иван',
147
+ insured_last_name: 'Иванов',
148
+ insured_birthday: Date.new(1985, 1, 15),
149
+ insurer_first_name: 'Петр',
150
+ insurer_last_name: 'Петров',
151
+ )
152
+ ```
153
+
154
+ Дальнейшие действия с `operation` аналогичны расчету стоимости.
155
+
156
+ ### 4. Подтверждение страховки
157
+
158
+ ```ruby
159
+ operation = client.create_payment(order_id: 7311)
160
+ ```
161
+
162
+ Дальнейшие действия с `operation` аналогичны расчету стоимости.
163
+
164
+ ### 5. Возврат страховки
165
+
166
+ ```ruby
167
+ operation = client.cancel_order(order_id: 7311)
168
+ ```
169
+
170
+ Дальнейшие действия с `operation` аналогичны расчету стоимости.
171
+
22
172
 
23
173
  ## License
24
174
 
@@ -2,8 +2,6 @@
2
2
 
3
3
  module SealineInsurance
4
4
  class Client
5
- attr_reader :config
6
-
7
5
  def initialize(**args)
8
6
  @config = Config.new(**args)
9
7
  end
@@ -20,10 +18,6 @@ module SealineInsurance
20
18
  classifiers(path: 'product')
21
19
  end
22
20
 
23
- def component_groups
24
- classifiers(path: 'product/component-group')
25
- end
26
-
27
21
  def calculate_status_list
28
22
  classifiers(path: 'status/calculate-product')
29
23
  end
@@ -38,7 +32,7 @@ module SealineInsurance
38
32
 
39
33
  def classifiers(path:)
40
34
  response = request.get("/classifiers/#{path}")
41
- BaseResponse.new(response)
35
+ Responses::Base.new(response)
42
36
  end
43
37
 
44
38
  def search_products(product_type:, options: [])
@@ -46,74 +40,34 @@ module SealineInsurance
46
40
  product_type: product_type,
47
41
  options: options,
48
42
  )
49
- BaseResponse.new(response)
43
+ Responses::Base.new(response)
50
44
  end
51
45
 
52
- def calculate(product_type:, products:, ticket_price:, options: [])
53
- response = request.post('/calculate-product',
54
- product_type: product_type,
55
- products: products,
56
- options: options,
57
- data: {
58
- ticket_price: ticket_price.to_i,
59
- },
60
- )
61
- CalculateResponse.new(response)
46
+ def calculate(**args)
47
+ Operations::Calculate.new(config: @config, **args).tap(&:call)
62
48
  end
63
49
 
64
- def calculate_status(request_id:)
65
- response = request.get("/calculate-product/#{request_id}")
66
- CalculateResponse.new(response)
50
+ def create_order(**args)
51
+ Operations::CreateOrder.new(config: @config, **args).tap(&:call)
67
52
  end
68
53
 
69
- # rubocop:disable Metrics/ParameterLists
70
- def create_order(
71
- product_type:,
72
- product:,
73
- options: [],
74
- ticket_number:,
75
- ticket_price:,
76
- departure_datetime:,
77
- arrival_datetime:,
78
- insured_first_name:,
79
- insured_last_name:,
80
- insured_birthday:,
81
- insurer_first_name:,
82
- insurer_last_name:
83
- )
84
- response = request.post('/order',
85
- product_type: product_type,
86
- product: product,
87
- options: options,
88
- data: {
89
- ticket_number: ticket_number.to_i,
90
- ticket_price: ticket_price.to_i,
91
- departure: departure_datetime.strftime('%Y-%m-%dT%H:%M'),
92
- arrival: arrival_datetime.strftime('%Y-%m-%dT%H:%M'),
93
- insured: {
94
- first_name: insured_first_name,
95
- last_name: insured_last_name,
96
- birthday: insured_birthday.strftime('%Y-%m-%d'),
97
- },
98
- insurer: {
99
- first_name: insurer_first_name,
100
- last_name: insurer_last_name,
101
- },
102
- },
103
- )
104
- OrderResponse.new(response)
54
+ def get_order(order_id:)
55
+ raw_response = request.get("/order/#{order_id}")
56
+ Responses::Order.new(raw_response)
105
57
  end
106
- # rubocop:enable Metrics/ParameterLists
107
58
 
108
- def get_order(order_id:)
109
- response = request.get("/order/#{order_id}")
110
- OrderResponse.new(response)
59
+ def create_payment(**args)
60
+ Operations::CreatePayment.new(config: @config, **args).tap(&:call)
61
+ end
62
+
63
+ def cancel_order(**args)
64
+ Operations::CancelOrder.new(config: @config, **args).tap(&:call)
111
65
  end
112
66
 
113
67
  private
114
68
 
115
69
  def request
116
- @request ||= Request.new(config: config)
70
+ @request ||= Request.new(config: @config)
117
71
  end
118
72
  end
119
73
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Часть методов в API асинхронные. Класс предоставляет абстракцию для них.
4
+ # Работа с API выглядит примерно так:
5
+ # 1. Запрос на выполнение действия.
6
+ # 2. Запрос актуального статуса до тех пор, пока операция не будет завершена.
7
+ module SealineInsurance
8
+ module Operations
9
+ class Base
10
+ # Промежуточный ответ сервера
11
+ attr_reader :response
12
+
13
+ def initialize(config:)
14
+ @config = config
15
+ end
16
+
17
+ # Запуск операции
18
+ def call
19
+ raise NotImplementedError
20
+ end
21
+
22
+ # Получение актуального статуса выполнения и результата
23
+ def fetch_status!
24
+ raise NotImplementedError
25
+ end
26
+
27
+ # Завершена ли операция (с успехом или ошибкой)
28
+ def finished?
29
+ response.error? || finished_status_list.include?(response.status)
30
+ end
31
+
32
+ # Завершена ли операция с успехом
33
+ def success?
34
+ response.success? && success_status_list.include?(response.status)
35
+ end
36
+
37
+ # Окончательный результат операции (успешный или нет)
38
+ def result
39
+ response if finished?
40
+ end
41
+
42
+ private
43
+
44
+ # Массив статусов, соответствующий завершенной операции
45
+ def finished_status_list
46
+ raise NotImplementedError
47
+ end
48
+
49
+ # Массив статусов, соответствующий успешно завершенной операции
50
+ def success_status_list
51
+ raise NotImplementedError
52
+ end
53
+
54
+ def request
55
+ @request ||= Request.new(config: @config)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SealineInsurance
4
+ module Operations
5
+ class Calculate < Base
6
+ def initialize(config:, product_type:, products:, ticket_price:, options: [])
7
+ super(config: config)
8
+ @product_type = product_type
9
+ @products = products
10
+ @ticket_price = ticket_price
11
+ @options = options
12
+ end
13
+
14
+ def call
15
+ raw_response = request.post('/calculate-product',
16
+ product_type: @product_type,
17
+ products: @products,
18
+ options: @options,
19
+ data: {
20
+ ticket_price: @ticket_price.to_i,
21
+ },
22
+ )
23
+ @response = Responses::Calculate.new(raw_response)
24
+ @request_id = @response.request_id
25
+ end
26
+
27
+ def fetch_status!
28
+ return unless @request_id
29
+
30
+ raw_response = request.get("/calculate-product/#{@request_id}")
31
+ @response = Responses::Calculate.new(raw_response)
32
+ end
33
+
34
+ private
35
+
36
+ def finished_status_list
37
+ @finished_status_list ||= [
38
+ 'DONE', # Выполнено
39
+ 'DONE_WITH_ERRORS', # Выполнено с ошибками
40
+ 'ERROR', # Ошибка
41
+ ]
42
+ end
43
+
44
+ def success_status_list
45
+ @success_status_list ||= ['DONE']
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SealineInsurance
4
+ module Operations
5
+ class CancelOrder < Base
6
+ def initialize(config:, order_id:)
7
+ super(config: config)
8
+ @order_id = order_id
9
+ end
10
+
11
+ def call
12
+ raw_response = request.delete("/order/#{@order_id}")
13
+ @response = Responses::Order.new(raw_response)
14
+ end
15
+
16
+ def fetch_status!
17
+ raw_response = request.get("/order/#{@order_id}")
18
+ @response = Responses::Order.new(raw_response)
19
+ end
20
+
21
+ private
22
+
23
+ def finished_status_list
24
+ @finished_status_list ||= [
25
+ 'REPEAT_CANCELLATION', # Требуется повтор отмены
26
+ 'NOT_CANCELLED', # Не отменен
27
+ 'CANCELLED', # Отменен
28
+ ]
29
+ end
30
+
31
+ def success_status_list
32
+ @success_status_list ||= ['CANCELLED']
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SealineInsurance
4
+ module Operations
5
+ class CreateOrder < Base
6
+ # rubocop:disable Metrics/ParameterLists
7
+ def initialize(
8
+ config:,
9
+ product_type:,
10
+ product:,
11
+ options: [],
12
+ ticket_number:,
13
+ ticket_price:,
14
+ departure_datetime:,
15
+ arrival_datetime:,
16
+ insured_first_name:,
17
+ insured_last_name:,
18
+ insured_birthday:,
19
+ insurer_first_name:,
20
+ insurer_last_name:
21
+ )
22
+ super(config: config)
23
+ @product_type = product_type
24
+ @product = product
25
+ @options = options
26
+ @ticket_number = ticket_number
27
+ @ticket_price = ticket_price
28
+ @departure_datetime = departure_datetime
29
+ @arrival_datetime = arrival_datetime
30
+ @insured_first_name = insured_first_name
31
+ @insured_last_name = insured_last_name
32
+ @insured_birthday = insured_birthday
33
+ @insurer_first_name = insurer_first_name
34
+ @insurer_last_name = insurer_last_name
35
+ end
36
+ # rubocop:enable Metrics/ParameterLists
37
+
38
+ def call
39
+ raw_response = request.post('/order',
40
+ product_type: @product_type,
41
+ product: @product,
42
+ options: @options,
43
+ data: {
44
+ ticket_number: @ticket_number.to_i,
45
+ ticket_price: @ticket_price.to_i,
46
+ departure: @departure_datetime.strftime('%Y-%m-%dT%H:%M'),
47
+ arrival: @arrival_datetime.strftime('%Y-%m-%dT%H:%M'),
48
+ insured: {
49
+ first_name: @insured_first_name,
50
+ last_name: @insured_last_name,
51
+ birthday: @insured_birthday.strftime('%Y-%m-%d'),
52
+ },
53
+ insurer: {
54
+ first_name: @insurer_first_name,
55
+ last_name: @insurer_last_name,
56
+ },
57
+ },
58
+ )
59
+ @response = Responses::Order.new(raw_response)
60
+ @order_id = @response.order_id
61
+ end
62
+
63
+ def fetch_status!
64
+ raw_response = request.get("/order/#{@order_id}")
65
+ @response = Responses::Order.new(raw_response)
66
+ end
67
+
68
+ private
69
+
70
+ def finished_status_list
71
+ @finished_status_list ||= [
72
+ 'ERROR', # Ошибка
73
+ 'NEED_PAYMENT', # Требуется оплата
74
+ ]
75
+ end
76
+
77
+ def success_status_list
78
+ @success_status_list ||= ['NEED_PAYMENT']
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SealineInsurance
4
+ module Operations
5
+ class CreatePayment < Base
6
+ def initialize(config:, order_id:)
7
+ super(config: config)
8
+ @order_id = order_id
9
+ end
10
+
11
+ def call
12
+ raw_response = request.post('/payment', order: @order_id)
13
+ @response = Responses::Payment.new(raw_response)
14
+ end
15
+
16
+ def fetch_status!
17
+ raw_response = request.get("/order/#{@order_id}")
18
+ @response = Responses::Order.new(raw_response)
19
+ end
20
+
21
+ private
22
+
23
+ def finished_status_list
24
+ @finished_status_list ||= [
25
+ 'DONE', # Выполнено
26
+ 'ERROR', # Ошибка
27
+ ]
28
+ end
29
+
30
+ def success_status_list
31
+ @success_status_list ||= ['DONE']
32
+ end
33
+ end
34
+ end
35
+ end
@@ -16,6 +16,10 @@ module SealineInsurance
16
16
  send_request(:post, url, data)
17
17
  end
18
18
 
19
+ def delete(url)
20
+ send_request(:delete, url)
21
+ end
22
+
19
23
  private
20
24
 
21
25
  def send_request(method, url, body = nil)
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SealineInsurance
4
+ module Responses
5
+ class Base
6
+ attr_reader :body
7
+
8
+ def initialize(http_response)
9
+ @body = parse_response_body(http_response.body)
10
+ if @body.nil?
11
+ # ошибка парсинга JSON
12
+ @success = false
13
+ @error_code = 'invalid_response'
14
+ @error_message = 'Invalid JSON'
15
+ @body = {}
16
+ elsif http_response.status == 401
17
+ # ошибка авторизации
18
+ @success = false
19
+ @error_code = 'unauthorized'
20
+ @error_message = body['detail']
21
+ elsif http_response.status == 400
22
+ # невалидные входные данные
23
+ @success = false
24
+ @error_code = 'invalid_params'
25
+ @error_message = fetch_validation_error(body)
26
+ elsif http_response.status == 409
27
+ # еще один вариант ошибки - 409 Conflict
28
+ @success = false
29
+ @error_code = 'conflict'
30
+ @error_message = body['error']
31
+ else
32
+ @success = true
33
+ end
34
+ end
35
+
36
+ def success?
37
+ @success
38
+ end
39
+
40
+ def error?
41
+ !success?
42
+ end
43
+
44
+ def error_code
45
+ @error_code ||= body['error_code']
46
+ end
47
+
48
+ def error_message
49
+ @error_message ||= body['error_message']
50
+ end
51
+
52
+ private
53
+
54
+ def parse_response_body(body)
55
+ JSON.parse(body)
56
+ rescue JSON::ParserError
57
+ nil
58
+ end
59
+
60
+ # Ошибки валидации возвращаются в виде хэша различной вложенности.
61
+ # Ключи хеша - поля, которые не прошли валидацию. Значение - текст ошибки.
62
+ # Метод рекурсивный, он находит первую ошибку и возвращает ее в виде строки.
63
+ # Пример возвращаемого значения:
64
+ # "data.arrival: Не может быть меньше даты trip.departure.date"
65
+ def fetch_validation_error(data, key_path = [])
66
+ if data.is_a?(Hash) && !data.empty?
67
+ key = data.keys.first
68
+ fetch_validation_error(data[key], key_path + [key])
69
+ elsif data.is_a?(Array) && !data.empty? && data.all? { |item| item.is_a?(String) }
70
+ fetch_validation_error(data.join(', '), key_path)
71
+ elsif data.is_a?(String)
72
+ "#{key_path.join('.')}: #{data}"
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SealineInsurance
4
+ module Responses
5
+ class Calculate < Base
6
+ STATUSES = [
7
+ 'IN_PROGRESS', # Обрабатывается
8
+ 'DONE', # Выполнено
9
+ 'DONE_WITH_ERRORS', # Выполнено с ошибками
10
+ 'ERROR', # Ошибка
11
+ ].freeze
12
+
13
+ def request_id
14
+ body['id']
15
+ end
16
+
17
+ def status
18
+ body['status']
19
+ end
20
+
21
+ def price
22
+ result = body['results']&.detect { |item| item['status'] == 'DONE' }
23
+ if result && result['price']
24
+ Money.new(result['price'], DEFAULT_CURRENCY)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SealineInsurance
4
+ module Responses
5
+ class Order < Base
6
+ STATUSES = [
7
+ 'IN_PROGRESS', # Обрабатывается
8
+ 'ERROR', # Ошибка
9
+ 'UPDATE_IN_PROGRESS', # Ожидает обновления
10
+ 'NOT_UPDATED', # Не обновлен
11
+ 'NEED_PAYMENT', # Требуется оплата
12
+ 'PAYMENT_IN_PROGRESS', # Производится подтверждение оплаты
13
+ 'WAITING_FOR_DOCUMENTS', # Загрузка электронного документа
14
+ 'DONE', # Создан
15
+ 'CANCEL_IN_PROGRESS', # Ожидает отмены
16
+ 'WAITING_CANCELLATION_APPROVAL', # Ожидает подтверждения отмены
17
+ 'REPEAT_CANCELLATION', # Требуется повтор отмены
18
+ 'NOT_CANCELLED', # Не отменен
19
+ 'CANCELLED', # Отменен
20
+ ].freeze
21
+
22
+ def order_id
23
+ body['id']
24
+ end
25
+
26
+ def status
27
+ body['status']
28
+ end
29
+
30
+ def price
31
+ Money.new(body['price'], DEFAULT_CURRENCY) if body['price']
32
+ end
33
+
34
+ def documents
35
+ body['documents'] || []
36
+ end
37
+
38
+ def external_numbers
39
+ body['external_numbers'] || []
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SealineInsurance
4
+ module Responses
5
+ class Payment < Base
6
+ STATUSES = [
7
+ 'IN_PROGRESS', # Обрабатывается
8
+ 'DONE', # Выполнено
9
+ 'ERROR', # Ошибка
10
+ ].freeze
11
+
12
+ def payment_id
13
+ body['id']
14
+ end
15
+
16
+ def order_id
17
+ body['order']
18
+ end
19
+
20
+ def status
21
+ body['status']
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SealineInsurance
4
- VERSION = '0.1.1'
4
+ VERSION = '1.0.0'
5
5
  end
@@ -9,9 +9,15 @@ require 'sealine_insurance/errors'
9
9
  require 'sealine_insurance/config'
10
10
  require 'sealine_insurance/client'
11
11
  require 'sealine_insurance/request'
12
- require 'sealine_insurance/base_response'
13
- require 'sealine_insurance/calculate_response'
14
- require 'sealine_insurance/order_response'
12
+ require 'sealine_insurance/operations/base'
13
+ require 'sealine_insurance/operations/calculate'
14
+ require 'sealine_insurance/operations/create_order'
15
+ require 'sealine_insurance/operations/create_payment'
16
+ require 'sealine_insurance/operations/cancel_order'
17
+ require 'sealine_insurance/responses/base'
18
+ require 'sealine_insurance/responses/calculate'
19
+ require 'sealine_insurance/responses/order'
20
+ require 'sealine_insurance/responses/payment'
15
21
 
16
22
  module SealineInsurance
17
23
  DEFAULT_CURRENCY = 'RUB'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sealine_insurance
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Khrebtov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-07-30 00:00:00.000000000 Z
11
+ date: 2018-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -140,13 +140,19 @@ files:
140
140
  - bin/console
141
141
  - bin/setup
142
142
  - lib/sealine_insurance.rb
143
- - lib/sealine_insurance/base_response.rb
144
- - lib/sealine_insurance/calculate_response.rb
145
143
  - lib/sealine_insurance/client.rb
146
144
  - lib/sealine_insurance/config.rb
147
145
  - lib/sealine_insurance/errors.rb
148
- - lib/sealine_insurance/order_response.rb
146
+ - lib/sealine_insurance/operations/base.rb
147
+ - lib/sealine_insurance/operations/calculate.rb
148
+ - lib/sealine_insurance/operations/cancel_order.rb
149
+ - lib/sealine_insurance/operations/create_order.rb
150
+ - lib/sealine_insurance/operations/create_payment.rb
149
151
  - lib/sealine_insurance/request.rb
152
+ - lib/sealine_insurance/responses/base.rb
153
+ - lib/sealine_insurance/responses/calculate.rb
154
+ - lib/sealine_insurance/responses/order.rb
155
+ - lib/sealine_insurance/responses/payment.rb
150
156
  - lib/sealine_insurance/version.rb
151
157
  - sealine_insurance.gemspec
152
158
  homepage: https://github.com/busfor/sealine_insurance
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SealineInsurance
4
- class BaseResponse
5
- attr_reader :raw_body
6
- attr_reader :error_code
7
- attr_reader :error_message
8
-
9
- def initialize(http_response)
10
- @raw_body = parse_response_body(http_response.body)
11
- if @error_code
12
- # ошибка парсинга JSON
13
- @success = false
14
- elsif http_response.status == 401
15
- # ошибка авторизации
16
- @success = false
17
- @error_code = 'unauthorized'
18
- @error_message = raw_body['detail']
19
- elsif http_response.status == 400
20
- # невалидные входные данные
21
- @success = false
22
- @error_code = 'invalid_params'
23
- @error_message = fetch_validation_error(raw_body)
24
- else
25
- # определение ошибки по статусу в JSON
26
- # если !error_status? - считаем ответ успешным
27
- @success = !error_status?
28
- @error_code = raw_body['error_code']
29
- @error_message = raw_body['error_message']
30
- end
31
- end
32
-
33
- def success?
34
- @success
35
- end
36
-
37
- def error?
38
- !success?
39
- end
40
-
41
- private
42
-
43
- # Дочерние классы переопределяют этот метод.
44
- # В общем случае статуса в raw_body может не быть, поэтому по дефолту false.
45
- def error_status?
46
- false
47
- end
48
-
49
- def parse_response_body(body)
50
- JSON.parse(body)
51
- rescue JSON::ParserError
52
- @error_code = 'invalid_response'
53
- @error_message = 'Invalid JSON'
54
- {}
55
- end
56
-
57
- # Ошибки валидации возвращаются в виде хэша различной вложенности.
58
- # Ключи хеша - поля, которые не прошли валидацию. Значение - текст ошибки.
59
- # Метод рекурсивный, он находит первую ошибку и возвращает ее в виде строки.
60
- # Пример возвращаемого значения:
61
- # "data.arrival: Не может быть меньше даты trip.departure.date"
62
- def fetch_validation_error(data, key_path = [])
63
- if data.is_a?(Hash) && !data.empty?
64
- key = data.keys.first
65
- fetch_validation_error(data[key], key_path + [key])
66
- elsif data.is_a?(Array) && !data.empty? && data.all? { |item| item.is_a?(String) }
67
- fetch_validation_error(data.join(', '), key_path)
68
- elsif data.is_a?(String)
69
- "#{key_path.join('.')}: #{data}"
70
- end
71
- end
72
- end
73
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SealineInsurance
4
- class CalculateResponse < BaseResponse
5
- STATUSES = [
6
- 'IN_PROGRESS', # Обрабатывается
7
- 'DONE', # Выполнено
8
- 'DONE_WITH_ERRORS', # Выполнено с ошибками
9
- 'ERROR', # Ошибка
10
- ].freeze
11
-
12
- def request_id
13
- raw_body['id']
14
- end
15
-
16
- def status
17
- raw_body['status']
18
- end
19
-
20
- def calculated?
21
- status == 'DONE'
22
- end
23
-
24
- def price
25
- result = raw_body['results']&.detect { |item| item['status'] == 'DONE' }
26
- if result && result['price']
27
- Money.new(result['price'], DEFAULT_CURRENCY)
28
- end
29
- end
30
-
31
- private
32
-
33
- def error_status?
34
- %w[DONE_WITH_ERRORS ERROR].include?(status)
35
- end
36
- end
37
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SealineInsurance
4
- class OrderResponse < BaseResponse
5
- STATUSES = [
6
- 'IN_PROGRESS', # Обрабатывается
7
- 'ERROR', # Ошибка
8
- 'UPDATE_IN_PROGRESS', # Ожидает обновления
9
- 'NOT_UPDATED', # Не обновлен
10
- 'NEED_PAYMENT', # Требуется оплата
11
- 'PAYMENT_IN_PROGRESS', # Производится подтверждение оплаты
12
- 'WAITING_FOR_DOCUMENTS', # Загрузка электронного документа
13
- 'DONE', # Создан
14
- 'CANCEL_IN_PROGRESS', # Ожидает отмены
15
- 'WAITING_CANCELLATION_APPROVAL', # Ожидает подтверждения отмены
16
- 'REPEAT_CANCELLATION', # Требуется повтор отмены
17
- 'NOT_CANCELLED', # Не отменен
18
- 'CANCELLED', # Отменен
19
- ].freeze
20
-
21
- def order_id
22
- raw_body['id']
23
- end
24
-
25
- def status
26
- raw_body['status']
27
- end
28
-
29
- def created?
30
- status == 'NEED_PAYMENT'
31
- end
32
-
33
- private
34
-
35
- def error_status?
36
- status == 'ERROR'
37
- end
38
- end
39
- end