amorail 0.6.1 → 0.7.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
  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