zaikio-hub 0.3.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 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: []