amorail 0.6.1 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ad5de495defa31c023d17cb5733c2d51e56f4dd08b348e8259ee80986ec6c12
4
- data.tar.gz: 2682594dd0d1c3661497b4712ad5751b9b7f7e2b67aa9f472151881ad8410e29
3
+ metadata.gz: 3b18605e272eb7a0c72af69e2d017f54753ff715d474d79cc9071791a036cd6d
4
+ data.tar.gz: d77fee259b877786fd18a044aaa52aff432be6936834095b5d8ab2d857d0df11
5
5
  SHA512:
6
- metadata.gz: '089d26d7753c6b30108b65f119fa615e1ce3e7fad256ef4f4893d0737f9dcdcc4e5eabc42d49b75bb86c9dccc0ad5d8fe06779f0140d65450ddb58810b4998eb'
7
- data.tar.gz: ff5cc0582d9bc552886efeb5bcb6fd0aa53a7ca930a682d1fba846719fc3be068966ea77103f5c20590c9f5597397f3cfe6518c02da6e7cf8220d5b3247adae6
6
+ metadata.gz: ab1e1221b40f406909ed76f4b444b7f7f7805d8f04c1e386030563471172175149ff718438bb4ee97d9bee28f2a3d9b9a78733828f8ab2d7b62edca6f631f56e
7
+ data.tar.gz: 4034fbcc9fea9004e159e62ac910ce9bfbe301ea76013f7d62d05a26cae8df2084c2836a4e4c65707cc4451fc8380fc163c5afe580068bdb8c4a40f79f632ff3
@@ -0,0 +1,23 @@
1
+ name: Build
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+
9
+ jobs:
10
+ build:
11
+
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - name: Set up Ruby 2.5
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: 2.5
20
+ bundler-cache: true
21
+ bundler: 1.13.6
22
+ - name: Build and test
23
+ run: bundle exec rspec
data/.gitignore CHANGED
@@ -12,4 +12,5 @@
12
12
  *.o
13
13
  *.a
14
14
  mkmf.log
15
- *.gem
15
+ *.gem
16
+ .idea
data/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Change log
2
+
3
+ ## master (unreleased)
4
+
5
+ ## 0.7.0 (2021-07-16)
6
+
7
+ ### Features
8
+
9
+ - Introduce [#53](https://github.com/teachbase/amorail/issues/48) Implement OAuth authentication ([@lHydra][])
10
+
11
+ ### Changes
12
+
13
+ - increased ruby version to >= 2.5.0
14
+ - updates dependencies versions
15
+
16
+
17
+ [@palkan]: https://github.com/palkan
18
+ [@AlexanderShvaykin]: https://github.com/AlexanderShvaykin
19
+ [@lHydra]: https://github.com/lHydra
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Gem Version](https://badge.fury.io/rb/amorail.svg)](https://rubygems.org/gems/amorail) [![Build Status](https://travis-ci.org/teachbase/amorail.svg?branch=master)](https://travis-ci.org/teachbase/amorail)
1
+ [![Gem Version](https://badge.fury.io/rb/amorail.svg)](https://rubygems.org/gems/amorail) ![Build](https://github.com/teachbase/amorail/workflows/Build/badge.svg)
2
2
 
3
3
  # Amorail
4
4
 
@@ -25,12 +25,48 @@ Or install it yourself as:
25
25
  With Amorail you can manipulate the following AmoCRM entities: Companies, Contacts, Leads and Tasks.
26
26
  We're triying to build simple AR-like interface.
27
27
 
28
+ ### Store configuration
29
+
30
+ In order to configure a token store you should set up a store adapter in a following way: `Amorail.token_store = :redis, { redis_url: 'redis://127.0.0.1:6379/0' }` (options can be omitted). Currently supported stores are `:redis` and `:memory`. Memory adapter is used **by default**.
31
+
32
+ Here is a default configuration for Redis:
33
+
34
+ ```ruby
35
+ Amorail.token_store = :redis, {
36
+ redis_host: "127.0.0.1",
37
+ redis_port: "6379",
38
+ redis_db_name: "0"
39
+ }
40
+ ```
41
+
42
+ You can also provide a Redis URL instead:
43
+
44
+ ```ruby
45
+ Amorail.token_store = :redis, { redis_url: "redis://localhost:6397" }
46
+ ```
47
+
48
+ **NOTE**: if `REDIS_URL` environment variable is set it is used automatically.
49
+
50
+ ### Add custom store
51
+
52
+ To add custom store you need declare a class that implements the interface `AbstractStoreAdapter`.
53
+ For example `class FileStoreAdapter < Amorail::StoreAdapters::AbstractStoreAdapter`
54
+
55
+ The class must contain constructor `initialize(**options)` and **4 required methods**:
56
+
57
+ 1. `fetch_access` — method that should return Hash with token data (**required keys:** `token`, `refresh_token` and `expiration`) or empty Hash (`{}`) if no value was received
58
+ 2. `persist_access` — method that stores data in storage
59
+ 3. `update_access` — method that updates existed token data in storage
60
+ 4. `access_expired?` — method that returns `true` if token was expired otherwise `false`
61
+
28
62
  ### Auth configuration
29
63
 
30
64
  Amorail uses [anyway_config](https://github.com/palkan/anyway_config) for configuration, so you
31
65
  can provide configuration parameters through env vars, seperate config file (`config/amorail.yml`) or `secrets.yml`.
32
66
 
33
- Required params: **usermail**, **api_key** and **api_endpoint**.
67
+ Required params: **client_id**, **client_secret** **code**, **redirect_uri** and **api_endpoint**.
68
+
69
+ An authorization **code** is required for the initial obtaining of a pair of access and refresh tokens. You can see it in the interface or through a Redirect URI if the authorization was run from the modal window for permissions. The lifespan of the code is 20 minutes. [More details](https://www.amocrm.com/developers/content/oauth/oauth/)
34
70
 
35
71
  Example:
36
72
 
@@ -39,9 +75,14 @@ Example:
39
75
  development:
40
76
  ...
41
77
  amorail:
42
- usermail: 'amorail@test.com'
43
- api_key: '75742b166417fe32ae132282ce178cf6'
44
- api_endpoint: 'https://test.amocrm.ru'
78
+ client_id: c0df457d-eacc-47cc-behb-3d8f962g4lbf
79
+ client_secret: a36b564b64398d3e53004c12e4997eb340e32b18ee185389ddb409292ebc5ebae297a3eab96be4a9d38ecbf274d90bbb54a7e8f282f40d1b29e5c9b2e2e357a6
80
+ code: a911ff963f58ea6c846901056114d37a14d2efa4d05ffb6ef0a8d60d32e5d6dae785bd317cbc9b0bd04261cb0cf9905af0cc32b5567c1eb84433328d08888f5c613608b822c1928272769ffd284b
81
+ redirect_uri: https://example.ru
82
+ api_endpoint: https://test.amocrm.ru
83
+ redis_host: 127.0.0.1
84
+ redis_port: 6379
85
+ redis_db_name: 0
45
86
  ```
46
87
 
47
88
  ### Running from console
@@ -50,7 +91,7 @@ You can try amorail in action from console ([PRY](https://github.com/pry/pry) is
50
91
 
51
92
  ```shell
52
93
  # amorail gem directory
53
- AMORAIL_USERMAIL=my_mail@test.com AMORAIL_API_KEY=my_key AMORAIL_API_ENDPOINT=my@amo.com bundle exec rake console
94
+ AMORAIL_CLIENT_ID=integration_id AMORAIL_CLIENT_SECRET=secret_key AMORAIL_CODE=my_code AMORAIL_REDIRECT_URI=https://example.com AMORAIL_API_ENDPOINT=https://test.amocrm.ru bundle exec rake console
54
95
  pry> Amorail.properties
55
96
  # ... prints properties (custom_fields) data
56
97
  pry> Amorail::Contact.find_by_query("test_contact")
@@ -223,12 +264,12 @@ It is possible to use Amorail with multiple AmoCRM accounts. To do so use `Amora
223
264
  which receive client params or client instance and a block to execute within custom context:
224
265
 
225
266
  ```ruby
226
- Amorail.with_client(usermail: "custom@mail.com", api_endpoint: "https://my.acmocrm.ru", api_key: "my_secret_key") do
267
+ Amorail.with_client(client_id: "my_id", client_secret: "my_secret", code: "my_code", api_endpoint: "https://my.acmocrm.ru", redirect_uri: "https://example.com") do
227
268
  # Client specific code here
228
269
  end
229
270
 
230
271
  # or using client instance
231
- my_client = Amorail::Client.new(usermail: "custom@mail.com", api_endpoint: "https://my.acmocrm.ru", api_key: "my_secret_key")
272
+ my_client = Amorail::Client.new(client_id: "my_id", client_secret: "my_secret", code: "my_code", api_endpoint: "https://my.acmocrm.ru", redirect_uri: "https://example.com")
232
273
 
233
274
  Amorail.with_client(client) do
234
275
  ...
data/RELEASING.md ADDED
@@ -0,0 +1,43 @@
1
+ # How to release a gem
2
+
3
+ This document describes a process of releasing a new version of a gem.
4
+
5
+ 1. Bump version.
6
+
7
+ ```sh
8
+ git commit -m "Bump 1.<x>.<y>"
9
+ ```
10
+
11
+ We're (kinda) using semantic versioning:
12
+
13
+ - Bugfixes should be released as fast as possible as patch versions.
14
+ - New features could be combined and released as minor or patch version upgrades (depending on the _size of the feature_—it's up to maintainers to decide).
15
+ - Breaking API changes should be avoided in minor and patch releases.
16
+ - Breaking dependencies changes (e.g., dropping older Ruby support) could be released in minor versions.
17
+
18
+ How to bump a version:
19
+
20
+ - Change the version number in `lib/amorail/version.rb` file.
21
+ - Update the changelog (add new heading with the version name and date).
22
+ - Update the installation documentation if necessary (e.g., during minor and major updates).
23
+
24
+ 2. Push code to GitHub and make sure CI passes.
25
+
26
+ ```sh
27
+ git push
28
+ ```
29
+
30
+ 3. Release a gem.
31
+
32
+ ```sh
33
+ gem release -t
34
+ git push --tags
35
+ ```
36
+
37
+ We use [gem-release](https://github.com/svenfuchs/gem-release) for publishing gems with a single command:
38
+
39
+ ```sh
40
+ gem release -t
41
+ ```
42
+
43
+ Don't forget to push tags and write release notes on GitHub (if necessary).
data/amorail.gemspec CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.description = %q{Ruby API client for AmoCRM. You can integrate your system with it.}
13
13
  spec.homepage = ""
14
14
  spec.license = "MIT"
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
15
16
 
16
17
  spec.files = `git ls-files -z`.split("\x0")
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
@@ -28,6 +29,7 @@ Gem::Specification.new do |spec|
28
29
  spec.add_dependency "anyway_config", ">= 1.0"
29
30
  spec.add_dependency "faraday"
30
31
  spec.add_dependency "faraday_middleware"
31
- spec.add_dependency 'activemodel'
32
- spec.add_dependency 'json'
32
+ spec.add_dependency "activemodel"
33
+ spec.add_dependency "json"
34
+ spec.add_dependency "redis"
33
35
  end
data/lib/amorail.rb CHANGED
@@ -6,34 +6,38 @@ require 'amorail/client'
6
6
  require 'amorail/exceptions'
7
7
  require 'amorail/entity'
8
8
  require 'amorail/property'
9
+ require 'amorail/access_token'
10
+ require 'amorail/store_adapters'
9
11
 
10
12
  Gem.find_files('amorail/entities/*.rb').each { |path| require path }
11
13
 
12
14
  # AmoCRM API integration.
13
15
  # https://www.amocrm.com/
14
16
  module Amorail
15
- def self.config
17
+ extend self
18
+
19
+ def config
16
20
  @config ||= Config.new
17
21
  end
18
22
 
19
- def self.properties
23
+ def properties
20
24
  client.properties
21
25
  end
22
26
 
23
- def self.configure
27
+ def configure
24
28
  yield(config) if block_given?
25
29
  end
26
30
 
27
- def self.client
31
+ def client
28
32
  ClientRegistry.client || (@client ||= Client.new)
29
33
  end
30
34
 
31
- def self.reset
35
+ def reset
32
36
  @config = nil
33
37
  @client = nil
34
38
  end
35
39
 
36
- def self.with_client(client)
40
+ def with_client(client)
37
41
  client = Client.new(client) unless client.is_a?(Client)
38
42
  ClientRegistry.client = client
39
43
  yield
@@ -41,6 +45,21 @@ module Amorail
41
45
  ClientRegistry.client = nil
42
46
  end
43
47
 
48
+ def token_store=(args)
49
+ adapter, options = Array(args)
50
+ @token_store = StoreAdapters.build_by_name(adapter, options)
51
+ rescue NameError => e
52
+ raise e.class, "Token store adapter for :#{adapter} haven't been found", e.backtrace
53
+ end
54
+
55
+ def token_store
56
+ unless instance_variable_defined?(:@token_store)
57
+ self.token_store = :memory
58
+ end
59
+
60
+ @token_store
61
+ end
62
+
44
63
  class ClientRegistry # :nodoc:
45
64
  extend ActiveSupport::PerThreadRegistry
46
65
 
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amorail
4
+ class AccessToken
5
+ attr_reader :token, :secret, :expiration, :refresh_token, :store
6
+
7
+ def initialize(secret, token, refresh_token, expiration, store)
8
+ @secret = secret
9
+ @token = token
10
+ @refresh_token = refresh_token
11
+ @expiration = expiration
12
+ @store = store
13
+ end
14
+
15
+ def expired?
16
+ store.access_expired?(secret)
17
+ end
18
+
19
+ class << self
20
+ def create(secret, token, refresh_token, expiration, store)
21
+ new(secret, token, refresh_token, expiration, store).tap do |access_token|
22
+ store.persist_access(access_token.secret, access_token.token, access_token.refresh_token, access_token.expiration)
23
+ end
24
+ end
25
+
26
+ def find(secret, store)
27
+ token_attrs = store.fetch_access(secret)
28
+ build_with_token_attrs(store, secret, token_attrs)
29
+ end
30
+
31
+ def refresh(secret, token, refresh_token, expiration, store)
32
+ new(secret, token, refresh_token, expiration, store).tap do |access_token|
33
+ store.update_access(access_token.secret, access_token.token, access_token.refresh_token, access_token.expiration)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def build_with_token_attrs(store, secret, token_attrs)
40
+ new(secret, token_attrs[:token], token_attrs[:refresh_token], token_attrs[:expiration], store)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -10,14 +10,31 @@ module Amorail
10
10
  class Client
11
11
  SUCCESS_STATUS_CODES = [200, 204].freeze
12
12
 
13
- attr_reader :usermail, :api_key, :api_endpoint
13
+ attr_accessor :store
14
+ attr_reader :api_endpoint,
15
+ :access_token,
16
+ :refresh_token,
17
+ :access,
18
+ :client_id,
19
+ :client_secret,
20
+ :code,
21
+ :redirect_uri
14
22
 
15
23
  def initialize(api_endpoint: Amorail.config.api_endpoint,
16
- api_key: Amorail.config.api_key,
17
- usermail: Amorail.config.usermail)
24
+ client_id: Amorail.config.client_id,
25
+ client_secret: Amorail.config.client_secret,
26
+ code: Amorail.config.code,
27
+ redirect_uri: Amorail.config.redirect_uri)
28
+ @store = Amorail.token_store
18
29
  @api_endpoint = api_endpoint
19
- @api_key = api_key
20
- @usermail = usermail
30
+ @client_id = client_id
31
+ @client_secret = client_secret
32
+ @code = code
33
+ @redirect_uri = redirect_uri
34
+ @access = AccessToken.find(@client_secret, store)
35
+ @access_token = @access.token
36
+ @refresh_token = @access.refresh_token
37
+
21
38
  @connect = Faraday.new(url: api_endpoint) do |faraday|
22
39
  faraday.response :json, content_type: /\bjson$/
23
40
  faraday.use :instrumentation
@@ -34,33 +51,53 @@ module Amorail
34
51
  end
35
52
 
36
53
  def authorize
37
- self.cookies = nil
38
- response = post(
39
- Amorail.config.auth_url,
40
- 'USER_LOGIN' => usermail,
41
- 'USER_HASH' => api_key
42
- )
43
- cookie_handler(response)
54
+ response = post(Amorail.config.auth_url, auth_params)
55
+ create_access_token(response)
56
+ response
57
+ end
58
+
59
+ def refresh_token!
60
+ response = post(Amorail.config.auth_url, refresh_params)
61
+ update_access_token(response)
44
62
  response
45
63
  end
46
64
 
65
+ def auth_params
66
+ {
67
+ client_id: client_id,
68
+ client_secret: client_secret,
69
+ grant_type: 'authorization_code',
70
+ code: @code,
71
+ redirect_uri: redirect_uri
72
+ }
73
+ end
74
+
75
+ def refresh_params
76
+ {
77
+ client_id: client_id,
78
+ client_secret: client_secret,
79
+ grant_type: 'refresh_token',
80
+ refresh_token: refresh_token,
81
+ redirect_uri: redirect_uri
82
+ }
83
+ end
84
+
47
85
  def safe_request(method, url, params = {})
48
- public_send(method, url, params)
49
- rescue ::Amorail::AmoUnauthorizedError
50
- authorize
86
+ authorize if access_token.blank?
87
+ refresh_token! if access.expired?
51
88
  public_send(method, url, params)
52
89
  end
53
90
 
54
91
  def get(url, params = {})
55
92
  response = connect.get(url, params) do |request|
56
- request.headers['Cookie'] = cookies if cookies.present?
93
+ request.headers['Authorization'] = "Bearer #{access_token}" if access_token.present?
57
94
  end
58
95
  handle_response(response)
59
96
  end
60
97
 
61
98
  def post(url, params = {})
62
99
  response = connect.post(url) do |request|
63
- request.headers['Cookie'] = cookies if cookies.present?
100
+ request.headers['Authorization'] = "Bearer #{access_token}" if access_token.present?
64
101
  request.headers['Content-Type'] = 'application/json'
65
102
  request.body = params.to_json
66
103
  end
@@ -69,12 +106,6 @@ module Amorail
69
106
 
70
107
  private
71
108
 
72
- attr_accessor :cookies
73
-
74
- def cookie_handler(response)
75
- self.cookies = response.headers['set-cookie'].split('; ')[0]
76
- end
77
-
78
109
  def handle_response(response) # rubocop:disable all
79
110
  return response if SUCCESS_STATUS_CODES.include?(response.status)
80
111
 
@@ -99,5 +130,33 @@ module Amorail
99
130
  fail ::Amorail::AmoUnknownError, response.body
100
131
  end
101
132
  end
133
+
134
+ def create_access_token(response)
135
+ _access = AccessToken.create(
136
+ client_secret,
137
+ response.body['access_token'],
138
+ response.body['refresh_token'],
139
+ expiration(response.body['expires_in']),
140
+ store
141
+ )
142
+ @access_token = _access.token
143
+ @refresh_token = _access.refresh_token
144
+ end
145
+
146
+ def update_access_token(response)
147
+ _access = AccessToken.refresh(
148
+ client_secret,
149
+ response.body['access_token'],
150
+ response.body['refresh_token'],
151
+ expiration(response.body['expires_in']),
152
+ store
153
+ )
154
+ @access_token = _access.token
155
+ @refresh_token = _access.refresh_token
156
+ end
157
+
158
+ def expiration(expires_in)
159
+ Time.now.to_i + expires_in.to_i
160
+ end
102
161
  end
103
162
  end
@@ -4,16 +4,20 @@ require 'anyway'
4
4
 
5
5
  module Amorail
6
6
  # Amorail config contains:
7
- # - usermail ("user@gmail.com")
8
- # - api_key ("13601bbac84727df")
9
7
  # - api_endpoint ("http://you_company.amocrm.com")
10
8
  # - api_path (default: "/private/api/v2/json/")
11
- # - auth_url (default: "/private/api/auth.php?type=json")
9
+ # - auth_url (default: "/oauth2/access_token")
12
10
  class Config < Anyway::Config
13
- attr_config :usermail,
14
- :api_key,
15
- :api_endpoint,
16
- api_path: "/private/api/v2/json/",
17
- auth_url: "/private/api/auth.php?type=json"
11
+ attr_config :api_endpoint,
12
+ :client_id,
13
+ :client_secret,
14
+ :code,
15
+ :redirect_uri,
16
+ :redis_url,
17
+ api_path: '/private/api/v2/json/',
18
+ auth_url: '/oauth2/access_token',
19
+ redis_host: '127.0.0.1',
20
+ redis_port: '6379',
21
+ redis_db_name: '0'
18
22
  end
19
23
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'amorail/store_adapters/abstract_store_adapter'
4
+ require 'amorail/store_adapters/redis_store_adapter'
5
+ require 'amorail/store_adapters/memory_store_adapter'
6
+
7
+ module Amorail
8
+ module StoreAdapters
9
+ def self.build_by_name(adapter, options = nil)
10
+ camelized_adapter = adapter.to_s.split('_').map(&:capitalize).join
11
+ adapter_class_name = "#{camelized_adapter}StoreAdapter"
12
+ StoreAdapters.const_get(adapter_class_name).new(**(options || {}))
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amorail
4
+ module StoreAdapters
5
+ class AbstractStoreAdapter
6
+ def fetch_access(_secret)
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def persist_access(_secret, _token, _refresh_token, _expiration)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def update_refresh(_secret, _token, _refresh_token, _expiration)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def access_expired?(_key)
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amorail
4
+ module StoreAdapters
5
+ class MemoryStoreAdapter < AbstractStoreAdapter
6
+ attr_reader :storage
7
+
8
+ def initialize(**options)
9
+ raise ArgumentError, 'Memory store doesn\'t support any options' if options.any?
10
+ @storage = Hash.new { |hh, kk| hh[kk] = {} }
11
+ end
12
+
13
+ def fetch_access(secret)
14
+ value_if_not_expired(secret)
15
+ end
16
+
17
+ def persist_access(secret, token, refresh_token, expiration)
18
+ access_token = { token: token, refresh_token: refresh_token, expiration: expiration }
19
+ storage.store(secret, access_token)
20
+ end
21
+
22
+ def update_access(secret, token, refresh_token, expiration)
23
+ update_access_fields(
24
+ secret,
25
+ token: token,
26
+ refresh_token: refresh_token,
27
+ expiration: expiration
28
+ )
29
+ end
30
+
31
+ def access_expired?(key)
32
+ storage[key][:expiration] && Time.now.to_i >= storage[key][:expiration]
33
+ end
34
+
35
+ private
36
+
37
+ def value_if_not_expired(key)
38
+ if !access_expired?(key)
39
+ storage[key]
40
+ else
41
+ {}
42
+ end
43
+ end
44
+
45
+ def update_access_fields(key, fields)
46
+ storage[key].merge!(fields)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amorail
4
+ module StoreAdapters
5
+ class RedisStoreAdapter < AbstractStoreAdapter
6
+ attr_reader :storage
7
+
8
+ def initialize(**options)
9
+ begin
10
+ require 'redis'
11
+ @storage = configure_redis_client(**options)
12
+ rescue LoadError => e
13
+ msg = 'Could not load the \'redis\' gem, please add it to your gemfile or ' \
14
+ 'configure a different adapter (e.g. Amorail.store_adapter = :memory)'
15
+ raise e.class, msg, e.backtrace
16
+ end
17
+ end
18
+
19
+ def fetch_access(secret)
20
+ token = storage.get(access_key(secret))
21
+ refresh_token = storage.get(refresh_key(secret))
22
+ token.nil? ? {} : { token: token, refresh_token: refresh_token }
23
+ end
24
+
25
+ def persist_access(secret, token, refresh_token, expiration)
26
+ update_data(secret, token, refresh_token, expiration)
27
+ end
28
+
29
+ def update_access(secret, token, refresh_token, expiration)
30
+ update_data(secret, token, refresh_token, expiration)
31
+ end
32
+
33
+ def access_expired?(secret)
34
+ access_key = access_key(secret)
35
+ refresh_key = refresh_key(secret)
36
+ storage.get(refresh_key) && storage.get(access_key).nil?
37
+ end
38
+
39
+ private
40
+
41
+ def update_data(secret, token, refresh_token, expiration)
42
+ access_key = access_key(secret)
43
+ refresh_key = refresh_key(secret)
44
+ storage.set(access_key, token)
45
+ storage.set(refresh_key, refresh_token)
46
+ storage.expireat(access_key, expiration)
47
+ end
48
+
49
+ def configure_redis_client(redis_url: nil, redis_host: nil, redis_port: nil, redis_db_name: nil)
50
+ if redis_url && (redis_host || redis_port || redis_db_name)
51
+ raise ArgumentError, 'redis_url cannot be passed along with redis_host, redis_port or redis_db_name options'
52
+ end
53
+
54
+ redis_url ||= build_redis_url(
55
+ redis_host: redis_host,
56
+ redis_port: redis_port,
57
+ redis_db_name: redis_db_name
58
+ )
59
+
60
+ Redis.new(url: redis_url)
61
+ end
62
+
63
+ def build_redis_url(redis_host: nil, redis_port: nil, redis_db_name: nil)
64
+ redis_db_name ||= Amorail.config.redis_db_name
65
+ return URI.join(Amorail.config.redis_url, redis_db_name).to_s if Amorail.config.redis_url
66
+
67
+ redis_host ||= Amorail.config.redis_host
68
+ redis_port ||= Amorail.config.redis_port
69
+
70
+ redis_base_url = ENV['REDIS_URL'] || "redis://#{redis_host}:#{redis_port}"
71
+ URI.join(redis_base_url, redis_db_name).to_s
72
+ end
73
+
74
+ def access_key(secret)
75
+ "access_#{secret}"
76
+ end
77
+
78
+ def refresh_key(secret)
79
+ "refresh_#{secret}"
80
+ end
81
+ end
82
+ end
83
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Amorail version
4
4
  module Amorail
5
- VERSION = '0.6.1'
5
+ VERSION = '0.7.0'
6
6
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Amorail::AccessToken do
6
+ let(:store) { Amorail.token_store }
7
+
8
+ before do
9
+ Amorail::AccessToken.create(
10
+ 'secret',
11
+ 'token',
12
+ 'refresh_token',
13
+ Time.now.to_i + 86_000,
14
+ store
15
+ )
16
+
17
+ Amorail::AccessToken.create(
18
+ 'secret_expired',
19
+ 'token',
20
+ 'refresh_token',
21
+ Time.now.to_i - 92_000,
22
+ store
23
+ )
24
+ end
25
+
26
+ describe '#find' do
27
+ context 'when token is not expired' do
28
+ subject { Amorail::AccessToken.find('secret', store).token }
29
+
30
+ it 'should return token' do
31
+ expect(subject).to eq('token')
32
+ end
33
+ end
34
+
35
+ context 'when token is expired' do
36
+ subject { Amorail::AccessToken.find('secret_expired', store).token }
37
+
38
+ it 'should return nil' do
39
+ expect(subject).to be_nil
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '#refresh' do
45
+ it 'updates token data' do
46
+ upd_expiration = Time.now.to_i + 96_000
47
+ access_token = Amorail::AccessToken.refresh('secret',
48
+ 'upd_token',
49
+ 'upd_refresh',
50
+ upd_expiration,
51
+ Amorail.token_store)
52
+ aggregate_failures do
53
+ expect(access_token.token).to eq('upd_token')
54
+ expect(access_token.refresh_token).to eq('upd_refresh')
55
+ expect(access_token.expiration).to eq(upd_expiration)
56
+ end
57
+ end
58
+ end
59
+ end
data/spec/client_spec.rb CHANGED
@@ -9,8 +9,10 @@ describe Amorail::Client do
9
9
 
10
10
  context "default client" do
11
11
  it "should create client", :aggregate_failures do
12
- expect(subject.usermail).to eq "amorail@test.com"
13
- expect(subject.api_key).to eq "75742b166417fe32ae132282ce178cf6"
12
+ expect(subject.client_id).to eq "some_id"
13
+ expect(subject.client_secret).to eq "some_secret"
14
+ expect(subject.code).to eq "some_code"
15
+ expect(subject.redirect_uri).to eq "https://example.ru/redirect/uri"
14
16
  expect(subject.api_endpoint).to eq "https://test.amocrm.ru"
15
17
  end
16
18
 
@@ -26,40 +28,48 @@ describe Amorail::Client do
26
28
  end
27
29
 
28
30
  describe "#with_client" do
29
- before { mock_custom_api("https://custom.amo.com", "custom@amo.com", "123") }
31
+ before { mock_custom_api("https://custom.amo.com", "custom_client_id", "custom_client_secret", "custom_code", "https://custom-site.ru/redirecto/uri") }
30
32
 
31
33
  let(:new_client) do
32
34
  described_class.new(
33
35
  api_endpoint: "https://custom.amo.com",
34
- usermail: "custom@amo.com",
35
- api_key: "123"
36
+ client_secret: "custom_client_secret",
37
+ client_id: "custom_client_id",
38
+ code: "custom_code",
39
+ redirect_uri: "https://custom-site.ru/redirecto/uri"
36
40
  )
37
41
  end
38
42
 
39
43
  it "use custom client as instance", :aggregate_failures do
40
- expect(Amorail.client.usermail).to eq "amorail@test.com"
44
+ expect(Amorail.client.client_id).to eq "some_id"
41
45
  Amorail.with_client(new_client) do
42
- expect(Amorail.client.usermail).to eq "custom@amo.com"
46
+ expect(Amorail.client.client_secret).to eq "custom_client_secret"
47
+ expect(Amorail.client.client_id).to eq "custom_client_id"
43
48
  expect(Amorail.client.api_endpoint).to eq "https://custom.amo.com"
44
- expect(Amorail.client.api_key).to eq "123"
49
+ expect(Amorail.client.code).to eq "custom_code"
50
+ expect(Amorail.client.redirect_uri).to eq "https://custom-site.ru/redirecto/uri"
45
51
  end
46
52
 
47
- expect(Amorail.client.usermail).to eq "amorail@test.com"
53
+ expect(Amorail.client.client_id).to eq "some_id"
48
54
  end
49
55
 
50
56
  it "use custom client as options", :aggregate_failures do
51
- expect(Amorail.client.usermail).to eq "amorail@test.com"
57
+ expect(Amorail.client.client_id).to eq "some_id"
52
58
  Amorail.with_client(
53
59
  api_endpoint: "https://custom.amo.com",
54
- usermail: "custom@amo.com",
55
- api_key: "123"
60
+ client_secret: "custom_client_secret",
61
+ client_id: "custom_client_id",
62
+ code: "custom_code",
63
+ redirect_uri: "https://custom-site.ru/redirecto/uri"
56
64
  ) do
57
- expect(Amorail.client.usermail).to eq "custom@amo.com"
65
+ expect(Amorail.client.client_secret).to eq "custom_client_secret"
66
+ expect(Amorail.client.client_id).to eq "custom_client_id"
58
67
  expect(Amorail.client.api_endpoint).to eq "https://custom.amo.com"
59
- expect(Amorail.client.api_key).to eq "123"
68
+ expect(Amorail.client.code).to eq "custom_code"
69
+ expect(Amorail.client.redirect_uri).to eq "https://custom-site.ru/redirecto/uri"
60
70
  end
61
71
 
62
- expect(Amorail.client.usermail).to eq "amorail@test.com"
72
+ expect(Amorail.client.client_id).to eq "some_id"
63
73
  end
64
74
 
65
75
  it "loads custom properties", :aggregate_failures do
@@ -83,10 +93,10 @@ describe Amorail::Client do
83
93
  # only after the second thread enters block
84
94
  threads << Thread.new do
85
95
  q1.pop
86
- Amorail.with_client(usermail: 'test1@amo.com') do
96
+ Amorail.with_client(client_id: 'some_id_1') do
87
97
  q2 << 1
88
98
  q1.pop
89
- results << Amorail.client.usermail
99
+ results << Amorail.client.client_id
90
100
  q2 << 1
91
101
  end
92
102
  q3 << 1
@@ -96,10 +106,10 @@ describe Amorail::Client do
96
106
  # after the first block
97
107
  threads << Thread.new do
98
108
  q2.pop
99
- Amorail.with_client(usermail: 'test2@amo.com') do
109
+ Amorail.with_client(client_id: 'some_id_2') do
100
110
  q1 << 1
101
111
  q2.pop
102
- results << Amorail.client.usermail
112
+ results << Amorail.client.client_id
103
113
  end
104
114
  q3 << 1
105
115
  end
@@ -107,19 +117,19 @@ describe Amorail::Client do
107
117
  # This thread enters block third and commits
108
118
  # after all other threads left blocks
109
119
  threads << Thread.new do
110
- Amorail.with_client(usermail: 'test3@amo.com') do
120
+ Amorail.with_client(client_id: 'some_id_3') do
111
121
  q3.pop
112
122
  q3.pop
113
- results << Amorail.client.usermail
123
+ results << Amorail.client.client_id
114
124
  end
115
125
  end
116
126
 
117
127
  q1 << 1
118
128
  threads.each(&:join)
119
129
 
120
- expect(results[0]).to eq 'test1@amo.com'
121
- expect(results[1]).to eq 'test2@amo.com'
122
- expect(results[2]).to eq 'test3@amo.com'
130
+ expect(results[0]).to eq 'some_id_1'
131
+ expect(results[1]).to eq 'some_id_2'
132
+ expect(results[2]).to eq 'some_id_3'
123
133
  end
124
134
  end
125
135
  end
@@ -1,3 +1,5 @@
1
- usermail: 'amorail@test.com'
2
- api_key: '75742b166417fe32ae132282ce178cf6'
3
- api_endpoint: 'https://test.amocrm.ru'
1
+ client_id: 'some_id'
2
+ client_secret: 'some_secret'
3
+ code: 'some_code'
4
+ redirect_uri: 'https://example.ru/redirect/uri'
5
+ api_endpoint: 'https://test.amocrm.ru'
@@ -0,0 +1,6 @@
1
+ {
2
+ "token_type": "Bearer",
3
+ "expires_in": 86400,
4
+ "access_token": "eyJ0eXAiOiJKf2QihCJhbGciOiJSUzI1NiIsImp0aSI6IjMxMTY3MGY3ZDVjY2IwODRjYTUyOTc5OGVjMzM0ZGU2ZDIzMmYzZDUyMjE0NTFhMTYzZDFlMTM2ZGY3NjA4MGVmMjViZTMwODQwNzFhY2Y0In0.eyJhdWQiOiIwNDA4M2FjZC1iY2ZmLTRiOWItOTJkYS05OTFiOTc0MDBlNzMiLCJqdGkiOiIzMTE2NzBmN2Q1Y2NiMDg0Y2E1Mjk3OThlYzMzNGRlNmQyMzJmM2Q1MjIxNDUxYTE2M2QxZTEzNmRmNzYwODBlZjI1YmUzMDg0MDcxYWNmNCIsImlhdCI6MTYxMzM4NzQ2MiwibmJmIDoxNjEzMzg3NDYyLCJleHAiOjE2MTM0NzM4NyIsInN1YiI6IjYyNjU1OTEiLCJhY2NvdW50X2lkIjoyOTAxMTIzMSwic2NvcGVzIjpbInB1c2hfbm90aWZpY2F0aW9ucyIsImNybSIsIm5vdGlmaWNhdGlvbnMiXX0.HRnJJXSK5KrlkcxHVBQBvE8TS34Rwru4Ba_lBlgzEtfU6FjaOCuBA0VLKJ24lLHz2w9B3lgMn9-OG9ayXAkDqrNPZByOimJ5cdwQExlNws-gvO9Hj27QiWK5JMTvbDE4EjVGvKuH7uXRsFTcsmuP6MA-5hjZ5h2DmNr5gzfZVS2Onh_9vrX75yh5FjFohfoO8izohtQYzZDVNYzek13EBdEK2BiNgLCoGhyGeMRtmTpwdSueD72qFGL80RMkQAGV8mElkZiXeUuGckTvEdIUodlgH6fk_KYG8OF3jozbEpzFKKfgPMUjE3vyCLVPWCEolQZjZPhSd6KbatK-f0oPWA",
5
+ "refresh_token": "def50200f4151ec4424843abc377eec488c6e8dceeda93b3f0b33d8f02207911533d98b408b97e2b2688acd1d956a44368f51263c7a55253efb543b256b90a2a1dc0ac71bf546374f23ab3d2b07f3ced5ad31e500e16b4d99deb6c735a5001c43c70156feac0a9c94dd24fdc6238ee59603997614ccb28773e4bce14d01e69fa7c04f869088fb1c9c27fea2f68f70E46d615a3982bdcd352Dcd05e3ae70e1d1ba707dcb354e7b611eba6790039a6c4bf2909210a85bdc34a254e9a10f8637aff405938ddcdc8bf88fb4ed8c8f15d7fa1e0e787c624b26c270b43e5877e1b6c87292bd95bb90305f50886b595d5a83c5b9768a8a61c63cdc7b082312ecc32619577253856058e8761c6f1e338e58824d8bed2a227bbe43dd12a69bf347657aa4f41f5d7609b6c5e088a0a0d2b2af9877bf8642c7d44fc6caa3c7dfa342d8b38e37d8a68dd26e814f0bfcdee707bf44ce7ef9e4d57ff5ac6aa52f2259388e5a3b7e5c6689366f43bf9197b2bf0e00470a3031581031b82ade8d8c0f8c01eabb61c312f47a2cfdc2943b10bc1f3d0b7791e1b1487832d3db75908611af90659f721cd3f831dcc76d8da12044611910722c9dc8bc09a5aff82923a2551a5bd5c5c57e4273993e655da7881e3"
6
+ }
@@ -5,34 +5,69 @@ module AmoWebMock
5
5
  def mock_api
6
6
  authorize_stub(
7
7
  Amorail.config.api_endpoint,
8
- Amorail.config.usermail,
9
- Amorail.config.api_key)
8
+ Amorail.config.client_id,
9
+ Amorail.config.client_secret,
10
+ 'authorization_code',
11
+ Amorail.config.code,
12
+ Amorail.config.redirect_uri
13
+ )
14
+
15
+ resresh_token_stub(
16
+ Amorail.config.api_endpoint,
17
+ Amorail.config.client_id,
18
+ Amorail.config.client_secret,
19
+ 'refresh_token',
20
+ 'refresh_token',
21
+ Amorail.config.redirect_uri
22
+ )
10
23
 
11
24
  account_info_stub(Amorail.config.api_endpoint)
12
25
  end
13
26
 
14
- def mock_custom_api(endpoint, usermail, api_key, properties = 'response_2.json')
27
+ def mock_custom_api(endpoint, client_id, client_secret, code, redirect_uri, properties = 'response_2.json')
15
28
  authorize_stub(
16
29
  endpoint,
17
- usermail,
18
- api_key
30
+ client_id,
31
+ client_secret,
32
+ 'authorization_code',
33
+ code,
34
+ redirect_uri
35
+ )
36
+
37
+ resresh_token_stub(
38
+ endpoint,
39
+ client_id,
40
+ client_secret,
41
+ 'refresh_token',
42
+ 'refresh_token',
43
+ redirect_uri
19
44
  )
20
45
 
21
46
  account_info_stub(endpoint, properties)
22
47
  end
23
48
 
24
- def authorize_stub(endpoint, usermail, api_key)
25
- cookie = 'PHPSESSID=58vorte6dd4t7h6mtuig9l0p50; path=/; domain=amocrm.ru'
26
- stub_request(:post, "#{endpoint}/private/api/auth.php?type=json")
49
+ def authorize_stub(endpoint, client_id, client_secret, grant_type, code, redirect_uri)
50
+ stub_request(:post, "#{endpoint}/oauth2/access_token")
27
51
  .with(
28
- body: "{\"USER_LOGIN\":\"#{usermail}\",\"USER_HASH\":\"#{api_key}\"}"
52
+ body: "{\"client_id\":\"#{client_id}\",\"client_secret\":\"#{client_secret}\",\"grant_type\":\"#{grant_type}\",\"code\":\"#{code}\",\"redirect_uri\":\"#{redirect_uri}\"}"
29
53
  )
30
54
  .to_return(
31
55
  status: 200,
32
- body: "",
33
- headers: {
34
- 'Set-Cookie' => cookie
35
- })
56
+ body: File.read('./spec/fixtures/authorize.json'),
57
+ headers: {}
58
+ )
59
+ end
60
+
61
+ def resresh_token_stub(endpoint, client_id, client_secret, grant_type, refresh_token, redirect_uri)
62
+ stub_request(:post, "#{endpoint}/oauth2/access_token")
63
+ .with(
64
+ body: "{\"client_id\":\"#{client_id}\",\"client_secret\":\"#{client_secret}\",\"grant_type\":\"#{grant_type}\",\"refresh_token\":\"#{refresh_token}\",\"redirect_uri\":\"#{redirect_uri}\"}"
65
+ )
66
+ .to_return(
67
+ status: 200,
68
+ body: File.read('./spec/fixtures/authorize.json'),
69
+ headers: {}
70
+ )
36
71
  end
37
72
 
38
73
  def account_info_stub(endpoint, properties = 'response_1.json')
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Amorail::StoreAdapters::MemoryStoreAdapter do
6
+ let!(:expiration) { Time.now.to_i + 86_000 }
7
+ let(:store) { Amorail::StoreAdapters.build_by_name(:memory) }
8
+
9
+ describe '#initialize' do
10
+ it 'raises error on unknow option' do
11
+ expect { Amorail::StoreAdapters::MemoryStoreAdapter.new(something: 'something') }.to raise_error(ArgumentError)
12
+ end
13
+ end
14
+
15
+ describe '#persist_access' do
16
+ subject { store.persist_access('secret', 'token', 'refresh_token', expiration) }
17
+
18
+ it 'save record to memory' do
19
+ expect { subject }.to change { store.fetch_access('secret') }.from(
20
+ {}
21
+ ).to(
22
+ { token: 'token', refresh_token: 'refresh_token', expiration: expiration }
23
+ )
24
+ end
25
+ end
26
+
27
+ describe '#fetch_access' do
28
+ context 'when token not expired' do
29
+ it 'returns valid data' do
30
+ store.persist_access('secret', 'token', 'refresh_token', expiration)
31
+ expect(store.fetch_access('secret')).to eq({ token: 'token', refresh_token: 'refresh_token', expiration: expiration })
32
+ end
33
+ end
34
+
35
+ context 'when token is expired' do
36
+ it 'returns blank hash' do
37
+ store.persist_access('secret', 'token', 'refresh_token', Time.now.to_i - 10_000)
38
+ expect(store.fetch_access('secret')).to eq({})
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#update_access' do
44
+ let!(:upd_expiration) { Time.now.to_i + 92_000 }
45
+ subject { store.update_access('secret', 'upd_token', 'upd_refresh', upd_expiration) }
46
+
47
+ it 'refresh token data' do
48
+ store.persist_access('secret', 'token', 'refresh_token', expiration)
49
+ expect { subject }.to change { store.fetch_access('secret') }.from(
50
+ { token: 'token', refresh_token: 'refresh_token', expiration: expiration }
51
+ ).to(
52
+ { token: 'upd_token', refresh_token: 'upd_refresh', expiration: upd_expiration }
53
+ )
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Amorail::StoreAdapters::RedisStoreAdapter do
6
+ before :each do
7
+ Amorail.config.reload
8
+ end
9
+
10
+ let(:store) { Amorail::StoreAdapters.build_by_name(:redis) }
11
+
12
+ describe '#initialize' do
13
+ it 'raises error on mixed redis options' do
14
+ expect { Amorail::StoreAdapters::RedisStoreAdapter.new(redis_url: 'redis://127.0.0.1:6379/0',
15
+ redis_port: '8082') }.to raise_error(ArgumentError)
16
+ end
17
+
18
+ it 'raises error on unknown options' do
19
+ expect { Amorail::StoreAdapters::RedisStoreAdapter.new(redis_url: 'redis://127.0.0.1:6379/0',
20
+ something: 'smth') }.to raise_error(ArgumentError)
21
+ end
22
+ end
23
+
24
+ describe 'configuration' do
25
+ let(:adapter) { Amorail::StoreAdapters::RedisStoreAdapter.new }
26
+
27
+ it 'set default url' do
28
+ expect(adapter.storage.id).to eq('redis://127.0.0.1:6379/0')
29
+ end
30
+
31
+ it 'set url from env variable' do
32
+ ENV['REDIS_URL'] = 'redis://localhost:2020/'
33
+ adapter = Amorail::StoreAdapters::RedisStoreAdapter.new
34
+ expect(adapter.storage.id).to eq('redis://localhost:2020/0')
35
+
36
+ ENV.delete('REDIS_URL')
37
+ adapter = Amorail::StoreAdapters::RedisStoreAdapter.new
38
+ expect(adapter.storage.id).to eq('redis://127.0.0.1:6379/0')
39
+ end
40
+
41
+ it 'configuration via host post and db' do
42
+ adapter = Amorail::StoreAdapters::RedisStoreAdapter.new(
43
+ redis_host: '127.0.0.2',
44
+ redis_port: '6372',
45
+ redis_db_name: '2'
46
+ )
47
+ expect(adapter.storage.id).to eq('redis://127.0.0.2:6372/2')
48
+ end
49
+
50
+ it 'configuration via host port and db in module' do
51
+ Amorail.config.redis_host = '127.0.0.2'
52
+ Amorail.config.redis_port = '6372'
53
+ Amorail.config.redis_db_name = '2'
54
+ expect(adapter.storage.id).to eq('redis://127.0.0.2:6372/2')
55
+ end
56
+
57
+ it 'configuration via redis url' do
58
+ adapter = Amorail::StoreAdapters::RedisStoreAdapter.new(redis_url: 'redis://127.0.0.2:6322')
59
+ expect(adapter.storage.id).to eq('redis://127.0.0.2:6322/0')
60
+ end
61
+
62
+ it 'configuration via redis url in module' do
63
+ Amorail.config.redis_url = 'redis://127.0.0.2:6322'
64
+ expect(adapter.storage.id).to eq('redis://127.0.0.2:6322/0')
65
+ end
66
+ end
67
+ end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amorail
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - alekseenkoss
8
8
  - palkan
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-11-25 00:00:00.000000000 Z
12
+ date: 2021-07-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -179,6 +179,20 @@ dependencies:
179
179
  - - ">="
180
180
  - !ruby/object:Gem::Version
181
181
  version: '0'
182
+ - !ruby/object:Gem::Dependency
183
+ name: redis
184
+ requirement: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ type: :runtime
190
+ prerelease: false
191
+ version_requirements: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ version: '0'
182
196
  description: Ruby API client for AmoCRM. You can integrate your system with it.
183
197
  email:
184
198
  - alekseenkoss@gmail.com
@@ -187,15 +201,18 @@ executables: []
187
201
  extensions: []
188
202
  extra_rdoc_files: []
189
203
  files:
204
+ - ".github/workflows/rspec.yml"
190
205
  - ".gitignore"
191
206
  - ".rubocop.yml"
192
- - ".travis.yml"
207
+ - CHANGELOG.md
193
208
  - Gemfile
194
209
  - LICENSE.txt
195
210
  - README.md
211
+ - RELEASING.md
196
212
  - Rakefile
197
213
  - amorail.gemspec
198
214
  - lib/amorail.rb
215
+ - lib/amorail/access_token.rb
199
216
  - lib/amorail/client.rb
200
217
  - lib/amorail/config.rb
201
218
  - lib/amorail/entities/company.rb
@@ -214,8 +231,13 @@ files:
214
231
  - lib/amorail/exceptions.rb
215
232
  - lib/amorail/property.rb
216
233
  - lib/amorail/railtie.rb
234
+ - lib/amorail/store_adapters.rb
235
+ - lib/amorail/store_adapters/abstract_store_adapter.rb
236
+ - lib/amorail/store_adapters/memory_store_adapter.rb
237
+ - lib/amorail/store_adapters/redis_store_adapter.rb
217
238
  - lib/amorail/version.rb
218
239
  - lib/tasks/amorail.rake
240
+ - spec/access_token_spec.rb
219
241
  - spec/client_spec.rb
220
242
  - spec/company_spec.rb
221
243
  - spec/contact_link_spec.rb
@@ -224,6 +246,7 @@ files:
224
246
  - spec/fixtures/accounts/response_1.json
225
247
  - spec/fixtures/accounts/response_2.json
226
248
  - spec/fixtures/amorail_test.yml
249
+ - spec/fixtures/authorize.json
227
250
  - spec/fixtures/contacts/create.json
228
251
  - spec/fixtures/contacts/find_many.json
229
252
  - spec/fixtures/contacts/find_one.json
@@ -244,6 +267,8 @@ files:
244
267
  - spec/note_spec.rb
245
268
  - spec/property_spec.rb
246
269
  - spec/spec_helper.rb
270
+ - spec/store_adapters/memory_store_adapter_spec.rb
271
+ - spec/store_adapters/redis_store_adapter_spec.rb
247
272
  - spec/support/elementable_example.rb
248
273
  - spec/support/entity_class_example.rb
249
274
  - spec/support/leadable_example.rb
@@ -255,7 +280,7 @@ homepage: ''
255
280
  licenses:
256
281
  - MIT
257
282
  metadata: {}
258
- post_install_message:
283
+ post_install_message:
259
284
  rdoc_options: []
260
285
  require_paths:
261
286
  - lib
@@ -263,18 +288,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
263
288
  requirements:
264
289
  - - ">="
265
290
  - !ruby/object:Gem::Version
266
- version: '0'
291
+ version: 2.5.0
267
292
  required_rubygems_version: !ruby/object:Gem::Requirement
268
293
  requirements:
269
294
  - - ">="
270
295
  - !ruby/object:Gem::Version
271
296
  version: '0'
272
297
  requirements: []
273
- rubygems_version: 3.0.6
274
- signing_key:
298
+ rubygems_version: 3.0.8
299
+ signing_key:
275
300
  specification_version: 4
276
301
  summary: Ruby API client for AmoCRM
277
302
  test_files:
303
+ - spec/access_token_spec.rb
278
304
  - spec/client_spec.rb
279
305
  - spec/company_spec.rb
280
306
  - spec/contact_link_spec.rb
@@ -283,6 +309,7 @@ test_files:
283
309
  - spec/fixtures/accounts/response_1.json
284
310
  - spec/fixtures/accounts/response_2.json
285
311
  - spec/fixtures/amorail_test.yml
312
+ - spec/fixtures/authorize.json
286
313
  - spec/fixtures/contacts/create.json
287
314
  - spec/fixtures/contacts/find_many.json
288
315
  - spec/fixtures/contacts/find_one.json
@@ -303,6 +330,8 @@ test_files:
303
330
  - spec/note_spec.rb
304
331
  - spec/property_spec.rb
305
332
  - spec/spec_helper.rb
333
+ - spec/store_adapters/memory_store_adapter_spec.rb
334
+ - spec/store_adapters/redis_store_adapter_spec.rb
306
335
  - spec/support/elementable_example.rb
307
336
  - spec/support/entity_class_example.rb
308
337
  - spec/support/leadable_example.rb
data/.travis.yml DELETED
@@ -1,9 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.3.3
5
-
6
- before_install: gem install bundler -v 1.13.6
7
-
8
- notifications:
9
- email: false