zaikio-hub 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 578ae236058ff3d1e4191cf07f794d361f2234097fee9130f060d6056d52760c
4
+ data.tar.gz: 1e4b12ea5289e4735323eeedf06467fffd80753696373929985bd74046ff4c35
5
+ SHA512:
6
+ metadata.gz: cd4cc812ec6b153ee9af72774c52f9d64eb38f6d2eb976fe92547c140f1913ece9e65aea1b6f8c7e02c649a54cdf756639ccb71c3e5e2df42dff56a96d2eeabf
7
+ data.tar.gz: 171bf01b4ad7bc0a6e1c9b3acb9972d89f2287c795d3e9a401b06786bc3bf8fbb87b07f5c6b95c7410d39300d799de3e88466d5d6b8674f160b85459d66379a5
data/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.3.0] - 2020-02-12
11
+
12
+ ### Changed
13
+
14
+ - BREAKING: Rename `Zaikio::Directory` to `Zaikio::Hub`
15
+
16
+ ### Added
17
+
18
+ - `Zaikio::Hub::Client` as a wrapper that does not need block syntax for authorization
19
+
20
+ ## [0.2.0] - 2020-01-21
21
+
22
+ ### Added
23
+
24
+ - `Zaikio::Hub::TestAccount`
25
+
26
+ ## [0.1.1] - 2020-09-02
27
+
28
+ ### Fixed
29
+ - Thread-Safety on current access token
30
+
31
+ ## [0.1.0] - 2020-08-24
32
+
33
+ ### Added
34
+ - Added subscriptions (migration required)
35
+
36
+ [Unreleased]: https://github.com/zaikio/zaikio-hub-ruby/compare/v0.3.0...HEAD
37
+ [0.3.0]: https://github.com/zaikio/zaikio-hub-ruby/compare/v0.2.0...v.0.3.0
38
+ [0.2.0]: https://github.com/zaikio/zaikio-hub-ruby/compare/v0.1.1...v.0.2.0
39
+ [0.1.1]: https://github.com/zaikio/zaikio-hub-ruby/compare/5c6cb4dbcac316733560ddb2b1e13b53e55eb66e...v0.1.1
40
+ [0.1.0]: https://github.com/zaikio/zaikio-hub-ruby/compare/d149fb4c0abe6005f123def3952d2dd2ef6404bb...29889d8a6a496542a81e05688da2a46cf4c44188
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Zaikio GmbH
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,182 @@
1
+ # Zaikio::Hub
2
+
3
+ Ruby API Client for Zaikio's Hub.
4
+
5
+ ## Installation
6
+
7
+ ### 1. Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'zaikio-hub'
11
+ ```
12
+
13
+ And then execute:
14
+ ```bash
15
+ $ bundle
16
+ ```
17
+
18
+ Or install it yourself as:
19
+ ```bash
20
+ $ gem install zaikio-hub
21
+ ```
22
+
23
+ ### 2. Configure the gem:
24
+
25
+ ```rb
26
+ # config/initializers/zaikio_hub.rb
27
+
28
+ Zaikio::Hub.configure do |config|
29
+ config.environment = :production # sandbox or production
30
+ end
31
+ ```
32
+
33
+
34
+ ## Usage
35
+
36
+ The Hub Client has an ORM like design. However, we distinguish between the contexts of the organization and the person.
37
+
38
+ For the requests to work, a valid JSON Web token with the correct OAuth Scopes must always be provided. Please refer to [zakio-oauth_client](https://github.com/zaikio/zaikio-oauth_client).
39
+
40
+ If you want to know which actions are available and which scopes are required, please refer to the [Directory API Reference](https://docs.zaikio.com/api/directory/directory.html).
41
+
42
+ ### As an organization
43
+
44
+ ```rb
45
+ token = "..." # Your valid JWT for an organization
46
+ Zaikio::Hub.with_token(token) do
47
+ organization = Zaikio::Hub::CurrentOrganization.new
48
+
49
+ # Fetch Data
50
+ organization.memberships.find('abc')
51
+ organization.members.all
52
+ organization.machines.all
53
+ organization.software.all
54
+ organization.sites.all
55
+ organization.sites.find('123')
56
+ organization.business_relationships.all
57
+ organization.fetch
58
+ organization.name
59
+
60
+ # Create new resources
61
+ organization.business_relationships.create(target_id: "abcd-efgh", kind: "printer", reference: "a-123")
62
+ organization.machines.create(name: "Speedmaster 2020", manufacturer: "heidelberg", kind: "sheetfed_digital_press", serial_number: "HDB1337", site_id: "d6308910-f5ae-58c0-aba7-d099947845c6")
63
+ organization.sites.create(name: "Mainz", address_attributes: {
64
+ addressee: "Crispy Mountain GmbH",
65
+ text: "Emmerich-Josef-Straße 1A, 55116 Mainz"
66
+ })
67
+
68
+ # Delete resources
69
+ organization.machines.first.destroy
70
+ end
71
+ ```
72
+
73
+ ### As a person
74
+
75
+ ```rb
76
+ token = "..." # Your valid JWT for a person
77
+ Zaikio::Hub.with_token(token) do
78
+ person = Zaikio::Hub::CurrentPerson.new
79
+
80
+ # Fetch Data
81
+ person.organizations
82
+ person.organization_memberships
83
+ person.fetch
84
+ person.full_name
85
+
86
+ organization = person.admin_organizations.find(&:connected?)
87
+ organization.machines.all
88
+ organization.sites.all
89
+
90
+ # Update details
91
+ person.update(first_name: "Lisa", name: "Simpson", pronoun: "She/Her")
92
+ end
93
+ ```
94
+
95
+ ### Use client object
96
+
97
+ ```rb
98
+ # Organization JWT
99
+ client = Zaikio::Hub::Client.from_token(org_token)
100
+
101
+ client.organization.name
102
+ client.sites.first.name
103
+ machine = client.machines.create(
104
+ name: "Machine Name",
105
+ kind: "sheetfed_digital_press",
106
+ manufacturer: "My Manufacturer"
107
+ )
108
+
109
+ # Basic Auth
110
+ client = Zaikio::Hub::Client.from_credentials(client_id, client_secret)
111
+ client.organization # raises Error
112
+ connection = client.connections.first
113
+ client.test_accounts.create(
114
+ name: "My Test Org",
115
+ country_code: "DE",
116
+ kinds: ["printer"],
117
+ connection_attributes: ["procurement_consumer"]
118
+ )
119
+ ```
120
+
121
+ ### Other Use Cases
122
+
123
+ ```rb
124
+ Zaikio::Hub.with_basic_auth(client_id, client_secret) do
125
+ connections = Zaikio::Hub::Connection.all
126
+ subscription = Zaikio::Hub::Subscription.find("Organization-b1475f65-236c-58b8-96e1-e1778b43beb7")
127
+ subscription.plan # => "advanced"
128
+ subscription.activate!
129
+ subscription.increment_usage_by!(:orders_created, 12)
130
+ end
131
+
132
+ Zaikio::Hub.with_basic_auth(client_id, client_secret) do
133
+ Zaikio::Hub::TestAccount.create(
134
+ name: "My Test Org",
135
+ country_code: "DE",
136
+ kinds: ["printer"],
137
+ connection_attributes: ["procurement_consumer"]
138
+ )
139
+ end
140
+
141
+ roles = Zaikio::Hub::Role.all
142
+ revoked_access_tokens = Zaikio::Hub::RevokedAccessToken.all
143
+
144
+ Zaikio::Hub.with_token(token) do
145
+ Zaikio::Hub::RevokedAccessToken.create
146
+ end
147
+ ```
148
+
149
+ ### Error Handling
150
+
151
+ If an unexpected error occurs with an API call (i.e. an error that has no status code `2xx`, `404` or `422`) then a `Zaikio::ConnectionError` is thrown automatically (for `404` there will be a `Zaikio::ResourceNotFound`).
152
+
153
+ This can be easily caught using the `with_fallback` method. We recommend to always work with fallbacks.
154
+
155
+ ```rb
156
+ Zaikio::Hub.with_token(token) do
157
+ person = Zaikio::Hub::CurrentPerson
158
+ .find_with_fallback(Zaikio::Hub::CurrentPerson.new(full_name: "Hello World"))
159
+
160
+ person.organizations # Automatically uses empty array as fallback
161
+ end
162
+
163
+ Zaikio::Hub.with_token(token) do
164
+ organization = Zaikio::Hub::CurrentOrganization.new
165
+
166
+ organization.machines.with_fallback.all
167
+ organization.machines
168
+ .with_fallback(Zaikio::Hub::Machine.new(name: 'My Machine'))
169
+ .find('machine-id')
170
+
171
+
172
+ organization.machines
173
+ .with_fallback(Zaikio::Hub::Machine.new(name: 'My Machine'))
174
+ .find('machine-does-not-exist') # => raises Zaikio::ResourceNotFound
175
+
176
+ begin
177
+ organization.machines.create(name: "Machine Name")
178
+ rescue Zaikio::ConnectionError
179
+ # Do something
180
+ end
181
+ end
182
+ ```
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+ require 'rubocop/rake_task'
9
+
10
+ RDoc::Task.new(:rdoc) do |rdoc|
11
+ rdoc.rdoc_dir = 'rdoc'
12
+ rdoc.title = 'Zaikio::Hub'
13
+ rdoc.options << '--line-numbers'
14
+ rdoc.rdoc_files.include('README.md')
15
+ rdoc.rdoc_files.include('lib/**/*.rb')
16
+ end
17
+
18
+ require 'bundler/gem_tasks'
19
+
20
+ require 'rake/testtask'
21
+
22
+ Rake::TestTask.new(:test) do |t|
23
+ t.libs << 'test'
24
+ t.pattern = 'test/**/*_test.rb'
25
+ t.verbose = false
26
+ end
27
+
28
+ task default: :test
29
+
30
+ namespace :test do
31
+ desc 'Runs RuboCop on specified directories'
32
+ RuboCop::RakeTask.new(:rubocop) do |task|
33
+ task.fail_on_error = false
34
+ end
35
+ end
36
+
37
+ Rake::Task[:test].enhance ['test:rubocop']
@@ -0,0 +1,18 @@
1
+ module Zaikio
2
+ class Error < StandardError; end
3
+
4
+ class ConnectionError < Zaikio::Error; end
5
+
6
+ class ResourceNotFound < Zaikio::Error; end
7
+ end
8
+
9
+ module Spyke
10
+ instance_eval do
11
+ # avoid warning: already initialized constant
12
+ remove_const("ConnectionError")
13
+ remove_const("ResourceNotFound")
14
+ end
15
+
16
+ ConnectionError = Class.new Zaikio::ConnectionError
17
+ ResourceNotFound = Class.new Zaikio::ResourceNotFound
18
+ end
data/lib/zaikio/hub.rb ADDED
@@ -0,0 +1,92 @@
1
+ require "faraday"
2
+ require "spyke"
3
+ require "zaikio/hub/configuration"
4
+ require "zaikio/hub/json_parser"
5
+ require "zaikio/hub/authorization_middleware"
6
+ require "zaikio/hub/basic_auth_middleware"
7
+
8
+ # Models
9
+ require "zaikio/error"
10
+ require "zaikio/hub/client"
11
+ require "zaikio/hub/base"
12
+ require "zaikio/hub/asset"
13
+ require "zaikio/hub/organization_membership"
14
+ require "zaikio/hub/business_relationship"
15
+ require "zaikio/hub/organization"
16
+ require "zaikio/hub/person"
17
+ require "zaikio/hub/machine"
18
+ require "zaikio/hub/software"
19
+ require "zaikio/hub/specialist"
20
+ require "zaikio/hub/site"
21
+ require "zaikio/hub/membership"
22
+ require "zaikio/hub/current_person"
23
+ require "zaikio/hub/current_organization"
24
+ require "zaikio/hub/role"
25
+ require "zaikio/hub/revoked_access_token"
26
+ require "zaikio/hub/connection"
27
+ require "zaikio/hub/subscription"
28
+ require "zaikio/hub/test_account"
29
+
30
+ module Zaikio
31
+ module Hub
32
+ class << self
33
+ attr_accessor :configuration
34
+
35
+ class_attribute :connection
36
+
37
+ def configure
38
+ self.connection = nil
39
+ self.configuration ||= Configuration.new
40
+ yield(configuration)
41
+
42
+ Base.connection = create_connection
43
+ end
44
+
45
+ def with_token(token)
46
+ AuthorizationMiddleware.token = token
47
+ yield
48
+ ensure
49
+ AuthorizationMiddleware.reset_token
50
+ end
51
+
52
+ def with_basic_auth(login, password)
53
+ BasicAuthMiddleware.credentials = [login, password]
54
+ yield
55
+ ensure
56
+ BasicAuthMiddleware.reset_credentials
57
+ end
58
+
59
+ def current_token_data
60
+ return unless AuthorizationMiddleware.token
61
+
62
+ payload = JWT.decode(AuthorizationMiddleware.token, nil, false).first
63
+
64
+ create_token_data(payload)
65
+ end
66
+
67
+ def create_connection
68
+ self.connection = Faraday.new(url: "#{configuration.host}/api/v1",
69
+ ssl: { verify: configuration.environment != :test }) do |c|
70
+ c.request :json
71
+ c.response :logger, configuration&.logger
72
+ c.use JSONParser
73
+ c.use AuthorizationMiddleware
74
+ c.use BasicAuthMiddleware
75
+ c.adapter Faraday.default_adapter
76
+ end
77
+ end
78
+
79
+ def create_token_data(payload)
80
+ subjects = payload["sub"].split(">")
81
+
82
+ OpenStruct.new(
83
+ audience: payload["aud"].first,
84
+ on_behalf_of_id: subjects.first.split("/").last,
85
+ subject_id: subjects.last.split("/").last,
86
+ subject_type: subjects.last.split("/").first,
87
+ scopes: payload["scope"]
88
+ )
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,55 @@
1
+ module Zaikio
2
+ module Hub
3
+ module Asset
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # Callbacks
8
+ after_create :make_organization_owner
9
+ end
10
+
11
+ def make_organization_owner
12
+ if Zaikio::Hub.current_token_data.subject_type == "Organization"
13
+ self.class.request(:post,
14
+ "#{collection_name}/#{id}/#{singular_name}_ownership")
15
+ else
16
+ org_path = "person/organizations/#{organization_id}"
17
+ self.class.request(:post,
18
+ "#{org_path}/#{collection_name}/#{id}/#{singular_name}_ownership")
19
+ end
20
+ end
21
+
22
+ def destroy
23
+ if Zaikio::Hub.current_token_data.subject_type == "Organization"
24
+ self.class.request(:delete,
25
+ "#{collection_name}/#{id}/#{singular_name}_ownership")
26
+ else
27
+ org_path = "person/organizations/#{owner_id || organization_id}"
28
+ self.class.request(:delete,
29
+ "#{org_path}/#{collection_name}/#{id}/#{singular_name}_ownership")
30
+ end
31
+ end
32
+
33
+ def specification
34
+ prefix = if Zaikio::Hub.current_token_data.subject_type == "Person"
35
+ "person/organizations/#{owner_id || organization_id}/"
36
+ else
37
+ ""
38
+ end
39
+ self.class.request(:get,
40
+ "#{prefix}#{collection_name}/#{id}/#{singular_name}_specification")
41
+ &.body&.dig("data")
42
+ end
43
+
44
+ private
45
+
46
+ def collection_name
47
+ self.class.name.demodulize.underscore.pluralize
48
+ end
49
+
50
+ def singular_name
51
+ self.class.name.demodulize.underscore
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,32 @@
1
+ require "faraday"
2
+ require "jwt"
3
+ require "concurrent"
4
+
5
+ module Zaikio
6
+ module Hub
7
+ class AuthorizationMiddleware < Faraday::Middleware
8
+ def self.token
9
+ @token ||= Concurrent::ThreadLocalVar.new { nil }
10
+ @token.value
11
+ end
12
+
13
+ def self.token=(value)
14
+ @token ||= Concurrent::ThreadLocalVar.new { nil }
15
+ @token.value = value
16
+ end
17
+
18
+ def self.reset_token
19
+ self.token = nil
20
+ end
21
+
22
+ def call(request_env)
23
+ if self.class.token
24
+ request_env[:request_headers]["Authorization"] = "Bearer #{self.class.token}"
25
+ end
26
+
27
+ @app.call(request_env).on_complete do |response_env|
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,7 @@
1
+ module Zaikio
2
+ module Hub
3
+ class Base < Spyke::Base
4
+ self.callback_methods = { create: :post, update: :patch }.freeze
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,35 @@
1
+ require "faraday"
2
+ require "base64"
3
+
4
+ module Zaikio
5
+ module Hub
6
+ class BasicAuthMiddleware < Faraday::Middleware
7
+ class_attribute :credentials
8
+
9
+ def self.credentials
10
+ @credentials ||= Concurrent::ThreadLocalVar.new { nil }
11
+ @credentials.value
12
+ end
13
+
14
+ def self.credentials=(value)
15
+ @credentials ||= Concurrent::ThreadLocalVar.new { nil }
16
+ @credentials.value = value
17
+ end
18
+
19
+ def self.reset_credentials
20
+ self.credentials = nil
21
+ end
22
+
23
+ def call(request_env)
24
+ if self.class.credentials
25
+ value = Base64.encode64(self.class.credentials.join(":"))
26
+ value.delete!("\n")
27
+ request_env[:request_headers]["Authorization"] = "Basic #{value}"
28
+ end
29
+
30
+ @app.call(request_env).on_complete do |response_env|
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ module Zaikio
2
+ module Hub
3
+ class BusinessRelationship < Base
4
+ uri "organization/business_relationships(/:id)"
5
+
6
+ # Attributes
7
+ attributes :kind, :updated_at, :created_at, :reference,
8
+ :target_id, :target_name
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,120 @@
1
+ module Zaikio
2
+ module Hub
3
+ class RequestWrapper
4
+ def initialize(result, client)
5
+ @result = result
6
+ @client = client
7
+ end
8
+
9
+ def respond_to_missing?(method_name, include_private = false)
10
+ @result.respond_to?(method_name, include_private) || super
11
+ end
12
+
13
+ def method_missing(method_name, *args, &block)
14
+ result = @client.with_auth { @result.public_send(method_name, *args, &block) }
15
+
16
+ if result.is_a?(Spyke::Base) || result.is_a?(Spyke::Relation)
17
+ RequestWrapper.new(result, @client)
18
+ else
19
+ result
20
+ end
21
+ end
22
+ end
23
+
24
+ class Client
25
+ def self.from_token(token)
26
+ new(token: token)
27
+ end
28
+
29
+ def self.from_credentials(client_id, client_secret)
30
+ new(client_id: client_id, client_secret: client_secret)
31
+ end
32
+
33
+ def initialize(token: nil, client_id: nil, client_secret: nil)
34
+ @token = token
35
+ @client_id = client_id
36
+ @client_secret = client_secret
37
+ end
38
+
39
+ def jwt
40
+ return if @token.nil?
41
+
42
+ @jwt ||= begin
43
+ payload = JWT.decode(@token, nil, false).first
44
+
45
+ Zaikio::Hub.create_token_data(payload)
46
+ end
47
+ end
48
+
49
+ def organization?
50
+ jwt&.subject_type == "Organization"
51
+ end
52
+
53
+ def person?
54
+ jwt&.subject_type == "Person"
55
+ end
56
+
57
+ def credentials?
58
+ @token.nil?
59
+ end
60
+
61
+ def subject_class
62
+ if organization?
63
+ Zaikio::Hub::CurrentOrganization
64
+ elsif person?
65
+ Zaikio::Hub::CurrentPerson
66
+ end
67
+ end
68
+
69
+ def with_auth(&block)
70
+ if @token.nil?
71
+ Zaikio::Hub.with_basic_auth(@client_id, @client_secret, &block)
72
+ else
73
+ Zaikio::Hub.with_token(@token, &block)
74
+ end
75
+ end
76
+
77
+ def organization
78
+ raise "Current organization is not available for person" unless organization?
79
+
80
+ with_auth { Zaikio::Hub::CurrentOrganization.find }
81
+ end
82
+
83
+ def person
84
+ raise "Current person is not available for organization" unless person?
85
+
86
+ with_auth { Zaikio::Hub::CurrentPerson.find }
87
+ end
88
+
89
+ def connections
90
+ raise "Only available with credentials" unless credentials?
91
+
92
+ RequestWrapper.new(Zaikio::Hub::Connection.all, self)
93
+ end
94
+
95
+ def subscriptions
96
+ raise "Only available with credentials" unless credentials?
97
+
98
+ RequestWrapper.new(Zaikio::Hub::Subscription.all, self)
99
+ end
100
+
101
+ def test_accounts
102
+ raise "Only available with credentials" unless credentials?
103
+
104
+ RequestWrapper.new(Zaikio::Hub::TestAccount.all, self)
105
+ end
106
+
107
+ def respond_to_missing?(method_name, include_private = false)
108
+ return super if credentials?
109
+
110
+ subject_class.new.respond_to?(method_name, include_private) || super
111
+ end
112
+
113
+ def method_missing(method_name, *args, &block)
114
+ result = with_auth { subject_class.new.public_send(method_name, *args, &block) }
115
+
116
+ RequestWrapper.new(result, self)
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,40 @@
1
+ require "logger"
2
+
3
+ module Zaikio
4
+ module Hub
5
+ class Configuration
6
+ HOSTS = {
7
+ development: "https://hub.zaikio.test",
8
+ test: "https://hub.zaikio.test",
9
+ staging: "https://hub.staging.zaikio.com",
10
+ sandbox: "https://hub.sandbox.zaikio.com",
11
+ production: "https://hub.zaikio.com"
12
+ }.freeze
13
+
14
+ attr_accessor :host
15
+ attr_reader :environment
16
+ attr_writer :logger
17
+
18
+ def initialize
19
+ @environment = :sandbox
20
+ end
21
+
22
+ def logger
23
+ @logger ||= Logger.new($stdout)
24
+ end
25
+
26
+ def environment=(env)
27
+ @environment = env.to_sym
28
+ @host = host_for(environment)
29
+ end
30
+
31
+ private
32
+
33
+ def host_for(environment)
34
+ HOSTS.fetch(environment) do
35
+ raise StandardError.new, "Invalid Zaikio::Hub environment '#{environment}'"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ module Zaikio
2
+ module Hub
3
+ class Connection < Base
4
+ uri "connections(/:id)"
5
+
6
+ include_root_in_json :connection
7
+
8
+ # Attributes
9
+ attributes :updated_at, :created_at, :connectable_type, :connectable_id,
10
+ :app_name, :granted_oauth_scopes
11
+
12
+ def initialize(attributes = {})
13
+ if attributes["connectable_id"]
14
+ super(attributes.merge(
15
+ "id" => "#{attributes['connectable_type']}-#{attributes['connectable_id']}"
16
+ ))
17
+ else
18
+ super
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,50 @@
1
+ module Zaikio
2
+ module Hub
3
+ class CurrentOrganization < Base
4
+ self.primary_key = nil
5
+
6
+ uri "organization"
7
+
8
+ def self.find
9
+ all.find_one
10
+ end
11
+
12
+ def self.find_with_fallback(fallback)
13
+ all.with_fallback(fallback).find_one
14
+ end
15
+
16
+ include_root_in_json :organization
17
+
18
+ # Attributes
19
+ attributes :name, :slug, :logo_url, :connected, :subscription,
20
+ :created_at, :updated_at, :country_code, :kinds,
21
+ :sections, :currency, :brand_color, :test_account_owner_id
22
+
23
+ # Associations
24
+ has_many :memberships, class_name: "Zaikio::Hub::Membership",
25
+ uri: "organization/memberships(/:id)"
26
+ has_many :business_relationships, class_name: "Zaikio::Hub::BusinessRelationship",
27
+ uri: "organization/business_relationships(/:id)"
28
+ has_many :software, class_name: "Zaikio::Hub::Software",
29
+ uri: "software(/:id)"
30
+ has_many :machines, class_name: "Zaikio::Hub::Machine",
31
+ uri: "machines(/:id)"
32
+ has_many :specialists, class_name: "Zaikio::Hub::Specialist",
33
+ uri: "specialists(/:id)"
34
+ has_many :sites, class_name: "Zaikio::Hub::Site",
35
+ uri: "sites(/:id)"
36
+
37
+ def fetch
38
+ self.attributes = get
39
+ end
40
+
41
+ def reload
42
+ self.attributes = self.class.find.attributes
43
+ end
44
+
45
+ def members
46
+ memberships.with_fallback.map(&:person)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,42 @@
1
+ module Zaikio
2
+ module Hub
3
+ class CurrentPerson < Base
4
+ self.primary_key = nil
5
+ include_root_in_json :person
6
+ uri "person"
7
+
8
+ # Attributes
9
+ attributes :updated_at, :created_at, :first_name, :name, :full_name, :email,
10
+ :pronoun, :locale, :country_code, :currency, :unit_system, :connected,
11
+ :test_account_owner_id, :time_zone, :email_confirmed,
12
+ :two_factor_authentication_enabled, :avatar_url, :subscription,
13
+ :accessible_apps
14
+
15
+ def self.find
16
+ all.find_one
17
+ end
18
+
19
+ def self.find_with_fallback(fallback)
20
+ all.with_fallback(fallback).find_one
21
+ end
22
+
23
+ # Associations
24
+ has_many :organization_memberships, class_name: "Zaikio::Hub::OrganizationMembership",
25
+ uri: nil
26
+
27
+ def fetch
28
+ self.attributes = get
29
+ end
30
+
31
+ def organizations
32
+ organization_memberships.with_fallback.map(&:organization)
33
+ end
34
+
35
+ def admin_organizations
36
+ organization_memberships.map do |m|
37
+ m.organization if m.roles.include?("admin") || m.roles.include?("owner")
38
+ end.compact
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,36 @@
1
+ require "multi_json"
2
+
3
+ module Zaikio
4
+ module Hub
5
+ class JSONParser < Faraday::Response::Middleware
6
+ def on_complete(env)
7
+ connection_error(env) unless /^(2\d\d)|422|404$/.match?(env.status.to_s)
8
+
9
+ raise Spyke::ResourceNotFound if env.status.to_s == "404"
10
+
11
+ env.body = parse_body(env.body)
12
+ end
13
+
14
+ def connection_error(env)
15
+ Zaikio::Hub.configuration.logger
16
+ .error("Zaikio::Hub Status Code #{env.status}, #{env.body}")
17
+ raise Spyke::ConnectionError, "Status Code #{env.status}, #{env.body}"
18
+ end
19
+
20
+ def parse_body(body)
21
+ json = MultiJson.load(body, symbolize_keys: true)
22
+ {
23
+ data: json,
24
+ metadata: {},
25
+ errors: json.is_a?(Hash) ? json[:errors] : {}
26
+ }
27
+ rescue MultiJson::ParseError
28
+ {
29
+ data: {},
30
+ metadata: {},
31
+ errors: {}
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,14 @@
1
+ module Zaikio
2
+ module Hub
3
+ class Machine < Base
4
+ include Asset
5
+
6
+ uri "machines(/:id)"
7
+ include_root_in_json :machine
8
+
9
+ # Attributes
10
+ attributes :name, :updated_at, :created_at, :site_id, :kind,
11
+ :manufacturer_id, :serial_number, :manufacturer, :owner_id
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Zaikio
2
+ module Hub
3
+ class Membership < Base
4
+ uri "organization/memberships(/:id)"
5
+ include_root_in_json :membership
6
+
7
+ # Attributes
8
+ attributes :updated_at, :created_at, :roles
9
+
10
+ # Associations
11
+ has_one :person, uri: nil, class_name: "Zaikio::Hub::Person"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,26 @@
1
+ module Zaikio
2
+ module Hub
3
+ class Organization < Base
4
+ uri "person/organizations"
5
+
6
+ include_root_in_json :organization
7
+
8
+ # Attributes
9
+ attributes :name, :slug, :logo_url, :connected, :subscription,
10
+ :created_at, :updated_at, :country_code, :kinds,
11
+ :sections, :currency, :brand_color, :test_account_owner_id
12
+
13
+ # Associations
14
+ has_many :memberships, class_name: "Zaikio::Hub::Membership",
15
+ uri: "person/organizations/:organization_id/memberships"
16
+ has_many :software, class_name: "Zaikio::Hub::Software",
17
+ uri: "person/organizations/:organization_id/software"
18
+ has_many :machines, class_name: "Zaikio::Hub::Machine",
19
+ uri: "person/organizations/:organization_id/machines"
20
+ has_many :specialists, class_name: "Zaikio::Hub::Specialist",
21
+ uri: "person/organizations/:organization_id/specialists"
22
+ has_many :sites, class_name: "Zaikio::Hub::Site",
23
+ uri: "person/organizations/:organization_id/sites"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ module Zaikio
2
+ module Hub
3
+ class OrganizationMembership < Base
4
+ uri "person/organization_memberships"
5
+
6
+ include_root_in_json :organization_membership
7
+
8
+ # Attributes
9
+ attributes :updated_at, :created_at, :roles
10
+
11
+ # Associations
12
+ has_one :organization, class_name: "Zaikio::Hub::Organization"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Zaikio
2
+ module Hub
3
+ class Person < Base
4
+ # Attributes
5
+ attributes :updated_at, :created_at, :first_name, :name, :full_name, :email,
6
+ :pronoun, :locale, :country_code, :currency, :unit_system, :connected,
7
+ :test_account_owner_id, :time_zone, :email_confirmed,
8
+ :two_factor_authentication_enabled, :avatar_url, :subscription
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Zaikio
2
+ module Hub
3
+ class RevokedAccessToken < Base
4
+ uri "revoked_access_tokens"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module Zaikio
2
+ module Hub
3
+ class Role < Base
4
+ uri "roles"
5
+
6
+ include_root_in_json :role
7
+
8
+ # Attributes
9
+ attributes :name
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Zaikio
2
+ module Hub
3
+ class Site < Base
4
+ uri "sites(/:id)"
5
+ include_root_in_json :site
6
+
7
+ # Attributes
8
+ attributes :name, :headquarter, :created_at, :updated_at, :address,
9
+ :address_attributes
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ module Zaikio
2
+ module Hub
3
+ class Software < Base
4
+ include Asset
5
+
6
+ uri "software(/:id)"
7
+ include_root_in_json :software
8
+
9
+ # Attributes
10
+ attributes :name, :updated_at, :created_at, :site_id, :kind, :owner_id,
11
+ :vendor_id, :vendor
12
+
13
+ private
14
+
15
+ def collection_name
16
+ "software"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ module Zaikio
2
+ module Hub
3
+ class Specialist < Base
4
+ include Asset
5
+
6
+ uri "specialists(/:id)"
7
+ include_root_in_json :specialist
8
+
9
+ # Attributes
10
+ attributes :name, :updated_at, :created_at, :site_id, :kind, :owner_id
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ module Zaikio
2
+ module Hub
3
+ class Subscription < Base
4
+ uri "subscriptions(/:id)"
5
+
6
+ include_root_in_json :subscription
7
+
8
+ # Attributes
9
+ attributes :updated_at, :created_at, :subscriber_type, :subscriber_id,
10
+ :status, :app_name, :activated_at, :last_billed_at,
11
+ :last_paid_at, :trial_ended_at, :plan, :preceding_plan,
12
+ :changed_plan_at, :usages_in_current_billing_period
13
+
14
+ def initialize(attributes = {})
15
+ if attributes["subscriber_id"]
16
+ super(attributes.merge(
17
+ "id" => "#{attributes['subscriber_type']}-#{attributes['subscriber_id']}"
18
+ ))
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ def activate!
25
+ update(status: "active")
26
+ end
27
+
28
+ def increment_usage_by!(usage, by = 1)
29
+ update(increment_usages_in_current_billing_period: { usage => by })
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ module Zaikio
2
+ module Hub
3
+ class TestAccount < Base
4
+ uri "test_accounts(/:id)"
5
+
6
+ include_root_in_json :test_account
7
+
8
+ # Attributes
9
+ attributes :name, :slug, :logo_url, :connected, :subscription,
10
+ :created_at, :updated_at, :country_code, :kinds,
11
+ :sections, :currency, :brand_color, :test_account_owner_id,
12
+ :private_access_token, :site_attributes, :member_attributes,
13
+ :subscription_attributes, :connection_attributes
14
+
15
+ # Associations
16
+ has_many :memberships, class_name: "Zaikio::Hub::Membership",
17
+ uri: nil
18
+ has_many :sites, class_name: "Zaikio::Hub::Site",
19
+ uri: nil
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ module Zaikio
2
+ module Hub
3
+ VERSION = "0.3.0".freeze
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zaikio-hub
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - crispymtn
8
+ - Jalyna Schröder
9
+ - Martin Spickermann
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2021-02-12 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: concurrent-ruby
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: jwt
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: 2.2.1
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: 2.2.1
43
+ - !ruby/object:Gem::Dependency
44
+ name: multi_json
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 1.14.1
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: 1.16.0
53
+ type: :runtime
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 1.14.1
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: 1.16.0
63
+ - !ruby/object:Gem::Dependency
64
+ name: oj
65
+ requirement: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 3.10.5
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: 3.12.0
73
+ type: :runtime
74
+ prerelease: false
75
+ version_requirements: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 3.10.5
80
+ - - "<"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.12.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: spyke
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 5.3.4
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 5.3.4
97
+ description: Ruby API Client for Zaikio's Hub
98
+ email:
99
+ - op@crispymtn.com
100
+ - js@crispymtn.com
101
+ - spickermann@gmail.com
102
+ executables: []
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - CHANGELOG.md
107
+ - MIT-LICENSE
108
+ - README.md
109
+ - Rakefile
110
+ - lib/zaikio/error.rb
111
+ - lib/zaikio/hub.rb
112
+ - lib/zaikio/hub/asset.rb
113
+ - lib/zaikio/hub/authorization_middleware.rb
114
+ - lib/zaikio/hub/base.rb
115
+ - lib/zaikio/hub/basic_auth_middleware.rb
116
+ - lib/zaikio/hub/business_relationship.rb
117
+ - lib/zaikio/hub/client.rb
118
+ - lib/zaikio/hub/configuration.rb
119
+ - lib/zaikio/hub/connection.rb
120
+ - lib/zaikio/hub/current_organization.rb
121
+ - lib/zaikio/hub/current_person.rb
122
+ - lib/zaikio/hub/json_parser.rb
123
+ - lib/zaikio/hub/machine.rb
124
+ - lib/zaikio/hub/membership.rb
125
+ - lib/zaikio/hub/organization.rb
126
+ - lib/zaikio/hub/organization_membership.rb
127
+ - lib/zaikio/hub/person.rb
128
+ - lib/zaikio/hub/revoked_access_token.rb
129
+ - lib/zaikio/hub/role.rb
130
+ - lib/zaikio/hub/site.rb
131
+ - lib/zaikio/hub/software.rb
132
+ - lib/zaikio/hub/specialist.rb
133
+ - lib/zaikio/hub/subscription.rb
134
+ - lib/zaikio/hub/test_account.rb
135
+ - lib/zaikio/hub/version.rb
136
+ homepage: https://www.zaikio.com/
137
+ licenses:
138
+ - MIT
139
+ metadata:
140
+ changelog_uri: https://github.com/zaikio/zaikio-hub-ruby/blob/master/CHANGELOG.md
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: 2.6.5
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubygems_version: 3.1.4
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: Ruby API Client for Zaikio's Hub
160
+ test_files: []