cobot_client 4.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/workflows/ruby.yml +23 -0
- data/.github/workflows/test.yml +26 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +31 -0
- data/CHANGELOG.md +15 -3
- data/Gemfile +10 -0
- data/README.md +49 -11
- data/Rakefile +6 -2
- data/cobot_client.gemspec +18 -17
- data/lib/cobot_client/api_client.rb +31 -90
- data/lib/cobot_client/engine.rb +2 -0
- data/lib/cobot_client/errors.rb +47 -0
- data/lib/cobot_client/navigation_link.rb +11 -8
- data/lib/cobot_client/navigation_link_service.rb +11 -6
- data/lib/cobot_client/request.rb +89 -0
- data/lib/cobot_client/response.rb +45 -0
- data/lib/cobot_client/url_helper.rb +23 -20
- data/lib/cobot_client/version.rb +3 -1
- data/lib/cobot_client.rb +12 -3
- data/rbs_collection.yaml +20 -0
- data/sig/cobot_client/api_client.rbs +51 -0
- data/sig/cobot_client/errors.rbs +23 -0
- data/sig/cobot_client/navigation_link.rbs +11 -0
- data/sig/cobot_client/navigation_link_service.rbs +17 -0
- data/sig/cobot_client/request.rbs +39 -0
- data/sig/cobot_client/response.rbs +23 -0
- data/sig/cobot_client/url_helper.rbs +25 -0
- data/sig/cobot_client/version.rbs +3 -0
- data/sig/manifests.yaml +4 -0
- data/spec/cobot_client/api_client_spec.rb +183 -173
- data/spec/cobot_client/navigation_link_service_spec.rb +48 -23
- data/spec/cobot_client/url_helper_spec.rb +8 -6
- data/spec/spec_helper.rb +10 -0
- metadata +35 -67
- data/.travis.yml +0 -4
- data/lib/cobot_client/exceptions.rb +0 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3d961f35435c958c2cf33b88170ce9f5c327f3ee57c332eb534b6b60098a86f1
|
|
4
|
+
data.tar.gz: 33c485cd858e729d508bd331897872f8d24345b75d1e4f192089e7bcfafc2793
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 101809ca57f22278713eeedd3f3f3f32c21552b2ed6e15189cb866f1d37d2d104c493acda5348e90dd187d3906e89c1290e35ec549f219caabd86d0adf2a9a5b
|
|
7
|
+
data.tar.gz: 27a001426e07ab13b48724219ae62b58e72a706b9547f4863b97f282c72568dff88657fbd0e380d3c2863e477524291cd8d851cb809c04428166e82199b526bd
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Push gem
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
jobs:
|
|
8
|
+
release:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
id-token: write
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v5
|
|
15
|
+
with:
|
|
16
|
+
persist-credentials: false
|
|
17
|
+
- name: Set up Ruby
|
|
18
|
+
uses: ruby/setup-ruby@v1
|
|
19
|
+
with:
|
|
20
|
+
bundler-cache: true
|
|
21
|
+
ruby-version: ruby
|
|
22
|
+
- name: Release gem
|
|
23
|
+
uses: rubygems/release-gem@v1
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Ruby CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- main
|
|
6
|
+
pull_request:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
ruby: ["4.0", "3.4", "3.3"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v5
|
|
18
|
+
- name: Set up Ruby
|
|
19
|
+
uses: ruby/setup-ruby@v1
|
|
20
|
+
with:
|
|
21
|
+
ruby-version: ${{ matrix.ruby }}
|
|
22
|
+
bundler-cache: true
|
|
23
|
+
- name: Install RBS type declaration collection
|
|
24
|
+
run: bundle exec rbs collection install
|
|
25
|
+
- name: Run tests
|
|
26
|
+
run: bundle exec rake
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
plugins:
|
|
2
|
+
- rubocop-rake
|
|
3
|
+
- rubocop-rspec
|
|
4
|
+
|
|
5
|
+
AllCops:
|
|
6
|
+
NewCops: enable
|
|
7
|
+
TargetRubyVersion: 3.3
|
|
8
|
+
|
|
9
|
+
Layout/SpaceInsideHashLiteralBraces:
|
|
10
|
+
EnforcedStyle: no_space
|
|
11
|
+
|
|
12
|
+
RSpec/AnyInstance:
|
|
13
|
+
Enabled: false
|
|
14
|
+
|
|
15
|
+
RSpec/ExampleLength:
|
|
16
|
+
Enabled: false
|
|
17
|
+
|
|
18
|
+
RSpec/MessageSpies:
|
|
19
|
+
Enabled: false
|
|
20
|
+
|
|
21
|
+
RSpec/MultipleExpectations:
|
|
22
|
+
Enabled: false
|
|
23
|
+
|
|
24
|
+
RSpec/StubbedMock:
|
|
25
|
+
Enabled: false
|
|
26
|
+
|
|
27
|
+
RSpec/VerifiedDoubles:
|
|
28
|
+
Enabled: false
|
|
29
|
+
|
|
30
|
+
Style/Documentation:
|
|
31
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
|
+
# 6.0.0
|
|
2
|
+
|
|
3
|
+
- Drop support for EOL Rubies (< 3.3)
|
|
4
|
+
- Add support for Ruby v4
|
|
5
|
+
- Add RBS type declarations
|
|
6
|
+
- Replace discontinued `Virtus` with `ActiveModel::Attributes`
|
|
7
|
+
- Replace abandoned `rest-client` with `net/http`
|
|
8
|
+
|
|
9
|
+
# 5.0.0
|
|
10
|
+
|
|
11
|
+
Bump oauth2 dependency to ~>2.0.
|
|
12
|
+
|
|
1
13
|
# 4.0.0
|
|
2
14
|
|
|
3
|
-
|
|
15
|
+
- changed navigation link service to add missing links and return all if there are links already (before it would do nothing if there were any links already)
|
|
4
16
|
|
|
5
17
|
# 3.1.0
|
|
6
18
|
|
|
7
|
-
|
|
19
|
+
- adds user_editable attribute to navigation links
|
|
8
20
|
|
|
9
21
|
# 3.0.0
|
|
10
22
|
|
|
11
|
-
|
|
23
|
+
- bumps rest-client to ~2.0.1, which results in CobotClient::ResourceNotFound to be renamed to CobotClient::NotFound (FrauBienenstich)
|
data/Gemfile
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
source 'https://rubygems.org'
|
|
2
4
|
|
|
3
5
|
# Specify your gem's dependencies in cobot_client.gemspec
|
|
4
6
|
gemspec
|
|
7
|
+
|
|
8
|
+
gem 'rake', '~> 13.4'
|
|
9
|
+
gem 'rbs'
|
|
10
|
+
gem 'rspec', '~> 3.0'
|
|
11
|
+
gem 'rubocop'
|
|
12
|
+
gem 'rubocop-rake'
|
|
13
|
+
gem 'rubocop-rspec'
|
|
14
|
+
gem 'webmock', require: false
|
data/README.md
CHANGED
|
@@ -24,9 +24,11 @@ Or install it yourself as:
|
|
|
24
24
|
|
|
25
25
|
You can install links to your app into the navigation on Cobot. When users click the link an iframe pointing to the given `iframe_url` will be shown.
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
```ruby
|
|
28
|
+
client = CobotClient::ApiClient.new '<access token>'
|
|
29
|
+
CobotClient::NavigationLinkService.new(client, 'co-up').install_links [
|
|
30
|
+
CobotClient::NavigationLink.new(section: 'admin/manage', label: 'My App', iframe_url: 'http://example.com')]
|
|
31
|
+
```
|
|
30
32
|
|
|
31
33
|
### Setting up automatic iframe resizing
|
|
32
34
|
|
|
@@ -48,25 +50,61 @@ When you display layers in the iframe that are positioned relative to the window
|
|
|
48
50
|
|
|
49
51
|
There is a module `CobotClient::UrlHelper`. After you include it you can call `cobot_url`. Examples:
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
```ruby
|
|
54
|
+
cobot_url('co-up') # => 'https://co-up.cobot.me/'
|
|
55
|
+
cobot_url('co-up', '/api/user') # => 'https://co-up.cobot.me/api/user'
|
|
56
|
+
cobot_url('co-up', '/api/user', params: {x: 'y'}) # => 'https://co-up.cobot.me/api/user?x=y'
|
|
57
|
+
```
|
|
54
58
|
|
|
55
59
|
### Calling the API
|
|
56
60
|
|
|
57
61
|
At the moment there are only a few high-level methods. For more details see the specs.
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
```ruby
|
|
64
|
+
client = CobotClient::ApiClient.new('<access token>')
|
|
65
|
+
client.list_resources('<subdomain>')
|
|
66
|
+
```
|
|
61
67
|
|
|
62
68
|
For everything else you can use the low-level get/post/put/delete metods:
|
|
63
69
|
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
```ruby
|
|
71
|
+
client.get 'www', '/user'
|
|
72
|
+
client.post 'my-subdomain', '/users', {"email": "joe@doe.com"}
|
|
73
|
+
```
|
|
66
74
|
|
|
67
75
|
You can also pass a URL instead of subdomain/path:
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
```ruby
|
|
78
|
+
client.get 'https://www/cobot.me/user'
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Error handling
|
|
82
|
+
|
|
83
|
+
In case of Cobot returning a 4xx or 5xx status code, the `ApiClient` throws an exception that is a subclass of `CobotClient::Exception`.
|
|
84
|
+
|
|
85
|
+
The most common exceptions encountered are `CobotClient::NotFound` (404), `CobotClient::Forbidden` (403), `CobotClient::UnprocesseableEntity` (422) and `CobotClient::TooManyRequests` (429).
|
|
86
|
+
|
|
87
|
+
To access the error message contained in the response, rescue the exception and `http_body` on it:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
begin
|
|
91
|
+
client = CobotClient::ApiClient.new('<access token>')
|
|
92
|
+
client.get('www', '/user')
|
|
93
|
+
rescue CobotClient::Exception => e
|
|
94
|
+
puts JSON.parse(e.http_body)
|
|
95
|
+
end
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
To access response headers, for example the `Retry-After` header of a rate-limited request:
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
begin
|
|
102
|
+
client = CobotClient::ApiClient.new('<access token>')
|
|
103
|
+
client.get('www', '/user')
|
|
104
|
+
rescue CobotClient::TooManyRequests => e
|
|
105
|
+
puts e.response.headers[:retry_after].to_i
|
|
106
|
+
end
|
|
107
|
+
```
|
|
70
108
|
|
|
71
109
|
## Contributing
|
|
72
110
|
|
data/Rakefile
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env rake
|
|
2
|
-
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'bundler/gem_tasks'
|
|
3
5
|
require 'rspec/core/rake_task'
|
|
6
|
+
require 'rubocop/rake_task'
|
|
4
7
|
|
|
5
8
|
RSpec::Core::RakeTask.new(:spec)
|
|
9
|
+
RuboCop::RakeTask.new(:rubocop)
|
|
6
10
|
|
|
7
|
-
task default:
|
|
11
|
+
task default: %i[spec rubocop]
|
data/cobot_client.gemspec
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require File.expand_path('lib/cobot_client/version', __dir__)
|
|
3
4
|
|
|
4
5
|
Gem::Specification.new do |gem|
|
|
5
|
-
gem.authors = [
|
|
6
|
-
gem.email = [
|
|
7
|
-
gem.description =
|
|
8
|
-
gem.summary =
|
|
9
|
-
gem.homepage =
|
|
6
|
+
gem.authors = ['Alexander Lang']
|
|
7
|
+
gem.email = ['alex@cobot.me']
|
|
8
|
+
gem.description = 'Client for the Cobot API plus helpers'
|
|
9
|
+
gem.summary = 'Client for the Cobot API plus helpers'
|
|
10
|
+
gem.homepage = 'http://github.com/cobot/cobot_client'
|
|
10
11
|
gem.license = 'MIT'
|
|
11
12
|
|
|
12
|
-
gem.files = `git ls-files`.split(
|
|
13
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
|
14
|
-
gem.
|
|
15
|
-
gem.
|
|
16
|
-
gem.require_paths = ["lib"]
|
|
13
|
+
gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
|
|
14
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
|
15
|
+
gem.name = 'cobot_client'
|
|
16
|
+
gem.require_paths = ['lib']
|
|
17
17
|
gem.version = CobotClient::VERSION
|
|
18
18
|
|
|
19
|
-
gem.
|
|
20
|
-
|
|
21
|
-
gem.add_dependency '
|
|
19
|
+
gem.required_ruby_version = ['>=3.3', '<5']
|
|
20
|
+
|
|
21
|
+
gem.add_dependency 'activemodel', '>=5.2'
|
|
22
22
|
gem.add_dependency 'json', '~>2.0'
|
|
23
|
-
gem.
|
|
24
|
-
|
|
23
|
+
gem.add_dependency 'oauth2', '~>2.0'
|
|
24
|
+
|
|
25
|
+
gem.metadata['rubygems_mfa_required'] = 'true'
|
|
25
26
|
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
require 'json'
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
module CobotClient
|
|
5
4
|
class ApiClient
|
|
@@ -8,127 +7,69 @@ module CobotClient
|
|
|
8
7
|
class << self
|
|
9
8
|
attr_accessor :user_agent, :retry_time
|
|
10
9
|
end
|
|
10
|
+
|
|
11
11
|
self.retry_time = 1
|
|
12
12
|
|
|
13
13
|
def initialize(access_token)
|
|
14
14
|
@access_token = access_token
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def get_resources(subdomain)
|
|
18
|
-
get subdomain, '/resources'
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def create_booking(subdomain, resource_id, attributes)
|
|
22
|
-
post subdomain, "/resources/#{resource_id}/bookings", attributes
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def update_booking(subdomain, id, attributes)
|
|
26
|
-
put subdomain, "/bookings/#{id}", attributes
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def delete_booking(subdomain, id)
|
|
30
|
-
delete subdomain, "/bookings/#{id}"
|
|
31
|
-
end
|
|
32
|
-
|
|
33
17
|
# args: either a full URL or subdomain, path, plus a body as hash
|
|
34
|
-
def post(*
|
|
35
|
-
request
|
|
18
|
+
def post(*)
|
|
19
|
+
request(:post, *).parsed_body
|
|
36
20
|
end
|
|
37
21
|
|
|
38
22
|
# args: either a full URL or subdomain, path, plus a body as hash
|
|
39
|
-
def put(*
|
|
40
|
-
request
|
|
23
|
+
def put(*)
|
|
24
|
+
request(:put, *).parsed_body
|
|
41
25
|
end
|
|
42
26
|
|
|
43
|
-
def patch(*
|
|
44
|
-
request
|
|
27
|
+
def patch(*)
|
|
28
|
+
request(:patch, *).parsed_body
|
|
45
29
|
end
|
|
46
30
|
|
|
47
31
|
# args: either a full URL or subdomain, path, plus an optional params hash
|
|
48
|
-
def get(*
|
|
49
|
-
|
|
50
|
-
JSON.parse(
|
|
51
|
-
rewrap_errors do
|
|
52
|
-
RestClient.get(
|
|
53
|
-
build_url(url || subdomain, path, params),
|
|
54
|
-
headers).body
|
|
55
|
-
end, symbolize_names: true)
|
|
32
|
+
def get(*)
|
|
33
|
+
request(:get, *).parsed_body
|
|
56
34
|
end
|
|
57
35
|
|
|
58
36
|
# args: either a full URL or subdomain, path
|
|
59
|
-
def delete(*
|
|
60
|
-
|
|
61
|
-
rewrap_errors do
|
|
62
|
-
RestClient.delete(build_url(url || subdomain, path), headers)
|
|
63
|
-
end
|
|
37
|
+
def delete(*)
|
|
38
|
+
request(:delete, *)
|
|
64
39
|
end
|
|
65
40
|
|
|
66
41
|
private
|
|
67
42
|
|
|
68
|
-
def request(method, *
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
headers.merge(content_type_header))
|
|
75
|
-
JSON.parse response.body, symbolize_names: true unless response.code == 204
|
|
43
|
+
def request(method, *)
|
|
44
|
+
request = Request.new(method, *)
|
|
45
|
+
request.headers = headers
|
|
46
|
+
|
|
47
|
+
retry_errors do
|
|
48
|
+
request.submit
|
|
76
49
|
end
|
|
77
50
|
end
|
|
78
51
|
|
|
79
|
-
def rewrap_errors
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
52
|
+
def rewrap_errors
|
|
53
|
+
yield.tap do |response|
|
|
54
|
+
raise response.to_error if response.client_error? || response.server_error?
|
|
55
|
+
end
|
|
56
|
+
rescue Net::ProtocolError, SocketError, Timeout::Error => e
|
|
57
|
+
raise ConnectionError, "#{e.class}: #{e.message}"
|
|
83
58
|
end
|
|
84
59
|
|
|
85
|
-
def retry_errors
|
|
60
|
+
def retry_errors(&)
|
|
86
61
|
retries = 0
|
|
87
62
|
begin
|
|
88
|
-
|
|
89
|
-
rescue
|
|
90
|
-
|
|
91
|
-
sleep self.class.retry_time
|
|
92
|
-
retries += 1
|
|
93
|
-
retry
|
|
94
|
-
else
|
|
95
|
-
raise e
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def parse_args(*args)
|
|
101
|
-
if args.size == 3 || (args.size == 2 && args[0].match(%r{https?://}))
|
|
102
|
-
params = args.pop
|
|
103
|
-
else
|
|
104
|
-
params = {}
|
|
105
|
-
end
|
|
106
|
-
if args.size == 1
|
|
107
|
-
url = args[0]
|
|
108
|
-
path = nil
|
|
109
|
-
subdomain = nil
|
|
110
|
-
else
|
|
111
|
-
subdomain = args[0]
|
|
112
|
-
path = args[1]
|
|
113
|
-
url = nil
|
|
114
|
-
end
|
|
115
|
-
[url, subdomain, path, params]
|
|
116
|
-
end
|
|
63
|
+
rewrap_errors(&)
|
|
64
|
+
rescue ConnectionError, BadGateway, InternalServerError => e
|
|
65
|
+
raise e unless retries < 3
|
|
117
66
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
else
|
|
122
|
-
uri = URI.parse(subdomain_or_url)
|
|
123
|
-
uri.query = URI.encode_www_form(params) if params && params.any?
|
|
124
|
-
uri.to_s
|
|
67
|
+
sleep self.class.retry_time
|
|
68
|
+
retries += 1
|
|
69
|
+
retry
|
|
125
70
|
end
|
|
126
71
|
end
|
|
127
72
|
|
|
128
|
-
def content_type_header
|
|
129
|
-
{'Content-Type' => 'application/json'}
|
|
130
|
-
end
|
|
131
|
-
|
|
132
73
|
def headers
|
|
133
74
|
{
|
|
134
75
|
'Authorization' => "Bearer #{@access_token}",
|
data/lib/cobot_client/engine.rb
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CobotClient
|
|
4
|
+
class Error < StandardError; end
|
|
5
|
+
|
|
6
|
+
class ConnectionError < Error; end
|
|
7
|
+
|
|
8
|
+
class ResponseError < Error
|
|
9
|
+
HTTP_CODE = nil
|
|
10
|
+
|
|
11
|
+
attr_reader :response
|
|
12
|
+
|
|
13
|
+
def self.build(msg = nil, response:)
|
|
14
|
+
RESPONSE_CODE_TO_ERROR_CLASS
|
|
15
|
+
.fetch(response.code, self)
|
|
16
|
+
.new(msg, response: response)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(msg = nil, response: nil)
|
|
20
|
+
@response = response
|
|
21
|
+
|
|
22
|
+
super(
|
|
23
|
+
[
|
|
24
|
+
"HTTP #{http_code}",
|
|
25
|
+
msg
|
|
26
|
+
].compact.join(' - ')
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def http_body
|
|
31
|
+
@response&.body
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def http_code
|
|
35
|
+
@response&.code || self.class.const_get(:HTTP_CODE)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
RESPONSE_CODE_TO_ERROR_CLASS = ::Net::HTTPResponse::CODE_TO_OBJ.to_h do |code, net_http_class|
|
|
40
|
+
class_name = net_http_class.name.delete_prefix('Net::HTTP')
|
|
41
|
+
|
|
42
|
+
class_object = Class.new(ResponseError)
|
|
43
|
+
class_object.const_get(:HTTP_CODE, code)
|
|
44
|
+
|
|
45
|
+
[code.to_i, const_set(class_name, class_object)]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
module CobotClient
|
|
4
|
+
class NavigationLink
|
|
5
|
+
include ActiveModel::Model
|
|
6
|
+
include ActiveModel::Attributes
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
attribute :section, :string
|
|
9
|
+
attribute :label, :string
|
|
10
|
+
attribute :iframe_url, :string
|
|
11
|
+
attribute :user_url, :string
|
|
12
|
+
attribute :user_editable, :boolean, default: true
|
|
13
|
+
end
|
|
11
14
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'oauth2'
|
|
2
4
|
|
|
3
5
|
module CobotClient
|
|
@@ -5,9 +7,9 @@ module CobotClient
|
|
|
5
7
|
class NavigationLinkService
|
|
6
8
|
# api_client - an CobotClient::ApiClient
|
|
7
9
|
# access_token - an access token string (owner must be admin of the space to be used)
|
|
8
|
-
def initialize(api_client,
|
|
10
|
+
def initialize(api_client, space_subdomain)
|
|
9
11
|
@api_client = api_client
|
|
10
|
-
@subdomain =
|
|
12
|
+
@subdomain = space_subdomain
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
# Checks if links are already installed and if not installs them.
|
|
@@ -16,7 +18,7 @@ module CobotClient
|
|
|
16
18
|
#
|
|
17
19
|
# Returns the links as `[CobotClient::NavigationLink]`
|
|
18
20
|
def install_links(new_links)
|
|
19
|
-
existing_links =
|
|
21
|
+
existing_links = fetch_links
|
|
20
22
|
missing_links = new_links.reject do |new_link|
|
|
21
23
|
existing_links.find do |existing_link|
|
|
22
24
|
existing_link.section == new_link.section && existing_link.iframe_url == new_link.iframe_url
|
|
@@ -30,18 +32,21 @@ module CobotClient
|
|
|
30
32
|
|
|
31
33
|
private
|
|
32
34
|
|
|
33
|
-
def
|
|
35
|
+
def fetch_links
|
|
34
36
|
@api_client.get(@subdomain, '/navigation_links').map do |attributes|
|
|
35
37
|
NavigationLink.new attributes
|
|
36
38
|
end
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
def create_link(link)
|
|
40
|
-
response = @api_client.post(
|
|
42
|
+
response = @api_client.post(
|
|
43
|
+
@subdomain,
|
|
44
|
+
'/navigation_links',
|
|
41
45
|
section: link.section,
|
|
42
46
|
label: link.label,
|
|
43
47
|
iframe_url: link.iframe_url,
|
|
44
|
-
user_editable: link.user_editable
|
|
48
|
+
user_editable: link.user_editable
|
|
49
|
+
)
|
|
45
50
|
|
|
46
51
|
NavigationLink.new response
|
|
47
52
|
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CobotClient
|
|
4
|
+
class Request
|
|
5
|
+
include UrlHelper
|
|
6
|
+
|
|
7
|
+
CONTENT_TYPE_HEADER = {'Content-Type' => 'application/json'}.freeze
|
|
8
|
+
VERBS = %i[get post put patch delete].freeze
|
|
9
|
+
|
|
10
|
+
attr_reader :body, :headers, :uri, :verb
|
|
11
|
+
|
|
12
|
+
def initialize(verb, *)
|
|
13
|
+
raise ArgumentError, "Unsupported verb: #{verb.inspect}" unless VERBS.include?(verb)
|
|
14
|
+
|
|
15
|
+
@verb = verb
|
|
16
|
+
@headers = CONTENT_TYPE_HEADER
|
|
17
|
+
|
|
18
|
+
url, subdomain, path, params = parse_args(*)
|
|
19
|
+
@uri, @body = case @verb
|
|
20
|
+
when :get, :delete
|
|
21
|
+
[build_uri(url || subdomain, path, **params), nil]
|
|
22
|
+
else
|
|
23
|
+
[build_uri(url || subdomain, path), params.to_json]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def headers=(headers)
|
|
28
|
+
raise ArgumentError, "Expected Hash, got: #{headers.inspect}" unless headers.is_a?(Hash)
|
|
29
|
+
|
|
30
|
+
@headers = headers.merge(CONTENT_TYPE_HEADER)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def submit
|
|
34
|
+
Response.new(
|
|
35
|
+
http.request(net_http_request)
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def build_uri(subdomain_or_url, path, **params)
|
|
42
|
+
if path
|
|
43
|
+
cobot_uri(subdomain_or_url, "/api#{path}", params: params)
|
|
44
|
+
else
|
|
45
|
+
uri = URI.parse(subdomain_or_url)
|
|
46
|
+
uri.query = URI.encode_www_form(params) unless params.empty?
|
|
47
|
+
uri
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def http
|
|
52
|
+
@http ||= Net::HTTP.new(uri.host, uri.port).tap do |http|
|
|
53
|
+
http.use_ssl = (uri.scheme == 'https')
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Do not memoize this because `headers` can change
|
|
58
|
+
def net_http_request
|
|
59
|
+
request_class.new(uri).tap do |request|
|
|
60
|
+
request.body = body if body
|
|
61
|
+
request.initialize_http_header(headers)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def request_class
|
|
66
|
+
case @verb
|
|
67
|
+
when :get then Net::HTTP::Get
|
|
68
|
+
when :post then Net::HTTP::Post
|
|
69
|
+
when :put then Net::HTTP::Put
|
|
70
|
+
when :patch then Net::HTTP::Patch
|
|
71
|
+
when :delete then Net::HTTP::Delete
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def parse_args(*args)
|
|
76
|
+
params = if args.size == 3 || (args.size == 2 && args[0].match(%r{https?://}))
|
|
77
|
+
args.pop
|
|
78
|
+
else
|
|
79
|
+
{}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
if args.size == 1
|
|
83
|
+
[args[0], nil, nil, params]
|
|
84
|
+
else
|
|
85
|
+
[nil, args[0], args[1], params]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|