cloudally 0.1.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: 7316687a69f5efc3a9644196d14ab51d8e5fe3a435fbe392d633fef1cd6c5995
4
+ data.tar.gz: 83b8bc9d624b50f7d9e06899af6f44dce0f0c052a40700d64392c56dd7b56ef8
5
+ SHA512:
6
+ metadata.gz: aec4098aaa3dd430ff2a18f03fbb59f6d140297377899c4a350f399e0e80d6db7f619e30acda36d3515729eca23fa2cafc8ca5b9d27484ebce557f4a6997aecf
7
+ data.tar.gz: 5ab2fd0b3e793e9e7ad39d64a894f79668217c2c712005da420a218608c794f9996385d9f6414eeac6ec23717ae9c769656a18079060feaab39b40162ec1c770
data/.env.template ADDED
@@ -0,0 +1,4 @@
1
+ CLOUDALLY_CLIENT_ID=<client id>
2
+ CLOUDALLY_CLIENT_SECRET=<client secret>
3
+ CLOUDALLY_USER=<uername/mail>
4
+ CLOUDALLY_PASSWORD=<password>
data/.gitignore ADDED
@@ -0,0 +1,43 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+ /data/
13
+ *.log
14
+ *.txt
15
+ *.json
16
+ *.yml
17
+
18
+ # Used by dotenv library to load environment variables.
19
+ .env
20
+
21
+
22
+ ## Documentation cache and generated files:
23
+ /.yardoc/
24
+ /_yardoc/
25
+ /doc/
26
+ /rdoc/
27
+
28
+ ## Environment normalization:
29
+ /.bundle/
30
+ /vendor/bundle
31
+ /lib/bundler/man/
32
+
33
+ # for a library or gem, you might want to ignore these files since the code is
34
+ # intended to run in multiple environments; otherwise, check them in:
35
+ # Gemfile.lock
36
+ # .ruby-version
37
+ # .ruby-gemset
38
+
39
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
40
+ .rvmrc
41
+
42
+ # Used by RuboCop. Remote config files pulled in from inherit_from directive.
43
+ # .rubocop-https?--*
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-01-25
4
+
5
+ - Initial release
data/cloudally.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/cloudally/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "cloudally"
7
+ s.version = CloudAlly::VERSION
8
+ s.authors = ["Janco Tanis"]
9
+ s.email = "gems@jancology.com"
10
+ s.license = "MIT"
11
+
12
+
13
+ s.summary = %q{A Ruby wrapper for the CloudAlly Partner Portal REST APIs (readonly)}
14
+ s.homepage = "https://rubygems.org/gems/cloudally"
15
+
16
+ s.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
17
+
18
+ s.metadata["homepage_uri"] = s.homepage
19
+ s.metadata["source_code_uri"] = "https://github.com/jancotanis/cloudally"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ s.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
25
+ end
26
+ s.bindir = "exe"
27
+ s.executables = s.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
+ s.require_paths = ["lib"]
29
+
30
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
31
+ s.platform = Gem::Platform::RUBY
32
+ s.add_runtime_dependency('faraday', ['>= 0.7', '< 0.10'])
33
+ s.add_development_dependency "dotenv"
34
+ s.add_development_dependency "minitest"
35
+ s.add_development_dependency "rubocop"
36
+
37
+ end
@@ -0,0 +1,32 @@
1
+ require File.expand_path('../connection', __FILE__)
2
+ require File.expand_path('../request', __FILE__)
3
+ require File.expand_path('../authentication', __FILE__)
4
+
5
+
6
+ module CloudAlly
7
+ # @private
8
+ class API
9
+ # @private
10
+ attr_accessor *Configuration::VALID_OPTIONS_KEYS
11
+
12
+ # Creates a new API
13
+ def initialize(options={})
14
+ options = CloudAlly.options.merge(options)
15
+ Configuration::VALID_OPTIONS_KEYS.each do |key|
16
+ send("#{key}=", options[key])
17
+ end
18
+ end
19
+
20
+ def config
21
+ conf = {}
22
+ Configuration::VALID_OPTIONS_KEYS.each do |key|
23
+ conf[key] = send key
24
+ end
25
+ conf
26
+ end
27
+
28
+ include Connection
29
+ include Request
30
+ include Authentication
31
+ end
32
+ end
@@ -0,0 +1,65 @@
1
+ module CloudAlly
2
+ # Defines HTTP request methods
3
+ module Authentication
4
+
5
+ # Return an access token from authorization
6
+ def get_access_token(options={})
7
+ params = access_token_params.merge(options)
8
+
9
+ response = post("/auth/partner", params)
10
+ res = JSON.parse( response.body )
11
+
12
+ access_token = res[ "accessToken" ]
13
+
14
+ raise Exception.new 'Could not find valid accessToken' if access_token == '' || access_token.nil?
15
+ access_token
16
+ end
17
+
18
+ # Authorize to the CloudAlly portal and return access_token
19
+ def auth( options={} )
20
+ params = access_token_params.merge(options)
21
+ response = post("/auth", params)
22
+ # return access_token
23
+ process_token( response.body )
24
+ end
25
+ alias login auth
26
+
27
+ # Return an access token from authorization
28
+ def auth_refresh( token )
29
+ params = { refreshToken: token}
30
+
31
+ response = post( "/auth/refresh", params )
32
+ # return access_token
33
+ process_token( response.body )
34
+ end
35
+
36
+ # Authorize to the partner portal and return access_token
37
+ def auth_partner( options={} )
38
+ params = access_token_params.merge(options)
39
+ response = post("/auth/partner", params)
40
+ # return access_token
41
+ process_token( response.body )
42
+ end
43
+ alias partner_login auth_partner
44
+
45
+ private
46
+
47
+ def access_token_params
48
+ {
49
+ email: username,
50
+ password: password
51
+ }
52
+ end
53
+ def process_token( response )
54
+ at = nil
55
+ CloudAlly.configure do |config|
56
+ at = config.access_token = response[ "accessToken" ]
57
+ config.token_type = response[ "tokenType" ]
58
+ config.refresh_token = response[ "refreshToken" ]
59
+ config.token_expires = response[ "expiresIn" ]
60
+ end
61
+ raise Exception.new 'Could not find valid accessToken; response '+response.to_s if at == '' || at.nil?
62
+ at
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,66 @@
1
+
2
+ module CloudAlly
3
+ class Client
4
+ # Defines methods related to users
5
+ module PartnerPortal
6
+
7
+ # Get CloudAlly Partner settings.
8
+ #
9
+ # @see https://api.cloudally.com/documentation#/Partner%20Portal
10
+ def partners
11
+ get( "partners" )
12
+ end
13
+ alias :get_partner :partners
14
+
15
+ # Get Partner bills.
16
+ #
17
+ # @see https://api.cloudally.com/documentation#/Partner%20Portal
18
+ def partner_bills
19
+ get_paged( "partners/bills" )
20
+ end
21
+ # Get Partner bills.
22
+ #
23
+ # @see https://api.cloudally.com/documentation#/Partner%20Portal
24
+ def partner_status
25
+ get_paged( "partners/status" )
26
+ end
27
+ alias :get_status_by_partner :partner_status
28
+ # Get Partner tasks.
29
+ #
30
+ # @see https://api.cloudally.com/documentation#/Partner%20Portal
31
+ def partner_tasks
32
+ get_paged( "partners/tasks" )
33
+ end
34
+ # Get Partner resellers.
35
+ #
36
+ # @see https://api.cloudally.com/documentation#/Partner%20Portal
37
+ def partner_resellers( partner_id=nil )
38
+ if partner_id
39
+ get_paged( "partners/resellers/#{partner_id}" )
40
+ else
41
+ get_paged( "partners/resellers" )
42
+ end
43
+ end
44
+ # Get Partner resellers.
45
+ #
46
+ # @see https://api.cloudally.com/documentation#/Partner%20Portal
47
+ def get_resellers_list
48
+ partner_resellers()
49
+ end
50
+ # Get Partner resellers.
51
+ #
52
+ # @see https://api.cloudally.com/documentation#/Partner%20Portal
53
+ def get_reseller_by_partner_id partner_id
54
+ partner_resellers( partner_id )
55
+ end
56
+ # Get Partner users.
57
+ #
58
+ # @see https://api.cloudally.com/documentation#/Partner%20Portal
59
+ def partner_users
60
+ get_paged( "partners/users" )
61
+ end
62
+ alias :get_users_by_partner :partner_users
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,11 @@
1
+ module CloudAlly
2
+ # Wrapper for the CloudAlly REST API
3
+ #
4
+ # @note All methods have been separated into modules and follow the same grouping used in api docs
5
+ # @see https://api.cloudally.com/documentation
6
+ class Client < API
7
+ Dir[File.expand_path('../client/*.rb', __FILE__)].each{|f| require f}
8
+
9
+ include CloudAlly::Client::PartnerPortal
10
+ end
11
+ end
@@ -0,0 +1,103 @@
1
+ require_relative "./version"
2
+ module CloudAlly
3
+ # Defines constants and methods related to configuration
4
+ module Configuration
5
+ # An array of valid keys in the options hash when configuring a {CloudAlly::API}
6
+
7
+ VALID_OPTIONS_KEYS = [
8
+ :access_token,
9
+ :token_type,
10
+ :refresh_token,
11
+ :token_expires,
12
+ :client_id,
13
+ :client_secret,
14
+ :connection_options,
15
+ :username,
16
+ :password,
17
+ :endpoint,
18
+ :logger,
19
+ :format,
20
+ :page_size,
21
+ :user_agent
22
+ ].freeze
23
+
24
+ # By default, don't set a user access token
25
+ DEFAULT_ACCESS_TOKEN = nil
26
+ DEFAULT_TOKEN_TYPE = nil
27
+
28
+ # By default, don't set an application ID
29
+ DEFAULT_CLIENT_ID = nil
30
+
31
+ # By default, don't set an application secret
32
+ DEFAULT_CLIENT_SECRET = nil
33
+
34
+ # By default, don't set application username
35
+ DEFAULT_USERNAME = nil
36
+
37
+ # By default, don't set application password
38
+ DEFAULT_PASSWORD = nil
39
+
40
+ # By default, don't set any connection options
41
+ DEFAULT_CONNECTION_OPTIONS = {}
42
+
43
+ # The endpoint that will be used to connect if none is set
44
+ #
45
+ # @note There is no reason to use any other endpoint at this time
46
+ DEFAULT_ENDPOINT = "https://api.cloudally.com/v1/".freeze
47
+
48
+ # By default, don't turn on logging
49
+ DEFAULT_LOGGER = nil
50
+
51
+ # The response format appended to the path and sent in the 'Accept' header if none is set
52
+ #
53
+ # @note JSON is the only available format at this time
54
+ DEFAULT_FORMAT = :json
55
+
56
+ # The page size for paged rest responses
57
+ #
58
+ # @note default JSON is the only available format at this time
59
+ DEFAULT_PAGE_SIZE = 500
60
+
61
+ # The user agent that will be sent to the API endpoint if none is set
62
+ DEFAULT_USER_AGENT = "CloudAlly Ruby API wrapper #{CloudAlly::VERSION}".freeze
63
+
64
+ # @private
65
+ attr_accessor *VALID_OPTIONS_KEYS
66
+
67
+ # When this module is extended, set all configuration options to their default values
68
+ def self.extended(base)
69
+ base.reset
70
+ end
71
+
72
+ # Convenience method to allow configuration options to be set in a block
73
+ def configure
74
+ yield self
75
+ end
76
+
77
+ # Create a hash of options and their values
78
+ def options
79
+ VALID_OPTIONS_KEYS.inject({}) do |option, key|
80
+ option.merge!(key => send(key))
81
+ end
82
+ end
83
+
84
+ # Reset all configuration options to defaults
85
+ def reset
86
+ self.access_token = DEFAULT_ACCESS_TOKEN
87
+ self.token_type = DEFAULT_TOKEN_TYPE
88
+ self.client_id = DEFAULT_CLIENT_ID
89
+ self.client_secret = DEFAULT_CLIENT_SECRET
90
+ self.username = DEFAULT_USERNAME
91
+ self.password = DEFAULT_PASSWORD
92
+ self.connection_options = DEFAULT_CONNECTION_OPTIONS
93
+ self.endpoint = DEFAULT_ENDPOINT
94
+ self.logger = DEFAULT_LOGGER
95
+ self.format = DEFAULT_FORMAT
96
+ self.page_size = DEFAULT_PAGE_SIZE
97
+ self.user_agent = DEFAULT_USER_AGENT
98
+
99
+ self.refresh_token = nil
100
+ self.token_expires = nil
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,45 @@
1
+ require 'faraday'
2
+ #require 'faraday_middleware'
3
+ #Dir[File.expand_path('../../faraday/*.rb', __FILE__)].each{|f| require f}
4
+
5
+ module CloudAlly
6
+ # @private
7
+ module Connection
8
+ private
9
+
10
+ def connection
11
+ options = {
12
+ :headers => {
13
+ 'Accept' => "application/#{format}; charset=utf-8",
14
+ 'User-Agent' => user_agent
15
+ },
16
+ :url => endpoint
17
+ }.merge( connection_options )
18
+
19
+ Faraday::Connection.new(options) do |connection|
20
+ connection.use Faraday::Response::RaiseError
21
+ #connection.use FaradayMiddleware::RaiseHttpException
22
+ connection.adapter Faraday.default_adapter
23
+
24
+ #connection.use FaradayMiddleware::InstagramOAuth2, client_id, access_token
25
+ connection.authorization :Bearer, access_token if access_token
26
+ connection.headers['client-id'] = client_id
27
+ connection.headers['client-secret'] = client_secret
28
+ connection.response :json, :content_type => /\bjson$/
29
+ connection.use Faraday::Request::UrlEncoded
30
+
31
+ #connection.use FaradayMiddleware::LoudLogger if loud_logger
32
+ if logger
33
+ connection.response :logger, logger, { headers: true, bodies: true } do |l|
34
+ # filter json content
35
+ l.filter(/(\"password\"\:\")(.+?)(\".*)/, '\1[REMOVED]\3')
36
+ l.filter(/(\"accessToken\"\:\")(.+?)(\".*)/, '\1[REMOVED]\3')
37
+ # filter header content
38
+ l.filter(/(client-secret\:.)([^&]+)/, '\1[REMOVED]')
39
+ l.filter(/(Authorization\:.)([^&]+)/, '\1[REMOVED]')
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,117 @@
1
+ require 'uri'
2
+ require 'json'
3
+
4
+ module CloudAlly
5
+
6
+ # Defines HTTP request methods
7
+ module Request
8
+ class Entity
9
+ attr_reader :attributes
10
+ def initialize attributes
11
+ @attributes = attributes.clone.transform_keys( &:to_s )
12
+ end
13
+
14
+ def method_missing(method_sym, *arguments, &block)
15
+ len = arguments.length
16
+ if method = method_sym[/.*(?==\z)/m]
17
+ # assignment
18
+ if len != 1
19
+ raise! ArgumentError, "wrong number of arguments (given #{len}, expected 1)", caller(1)
20
+ end
21
+ @attributes[method] = arguments[ 0 ]
22
+ elsif @attributes.include? method_sym.to_s
23
+ r = @attributes[method_sym.to_s]
24
+ case r
25
+ when Hash
26
+ r = @attributes[method_sym.to_s] = self.class.new( r )
27
+ when Array
28
+ # make deep copy
29
+ @attributes[method_sym.to_s] = r = r.map{ |item| self.class.new( item ) } if r.length > 0 && r[0].is_a?( Hash )
30
+ r
31
+ else
32
+ r
33
+ end
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ def respond_to?(method_sym, include_private = false)
40
+ if @attributes.include? method_sym.to_s
41
+ true
42
+ else
43
+ super
44
+ end
45
+ end
46
+ end
47
+ # Perform an HTTP GET request and return entity
48
+ def get(path, options={})
49
+ response = request(:get, path, options)
50
+ :json.eql?( format ) ? Entity.new( response.body ) : response.body
51
+ end
52
+
53
+ # Perform an HTTP GET request for paged date sets responsind to
54
+ # Name Description
55
+ # pageSize The number of records to display per page
56
+ # page The page number
57
+ # nextPageToken Next page token
58
+ def get_paged( path, options={}, &block )
59
+ raise! ArgumentError, "Pages requests should be json formatted (given format '#{format}')" unless :json.eql? format
60
+ result = []
61
+ page = 1
62
+ total = page + 1
63
+ nextPage = ""
64
+ while page <= total
65
+ # https://api.cloudally.com/v3/api-docs/v1
66
+ followingPage = { pageSize: page_size }
67
+ followingPage.merge!( { page: page, nextPageToken: nextPage } ) unless nextPage.empty?
68
+
69
+ response = request(:get, path, options.merge( followingPage ) )
70
+ data = response.body
71
+ d = data[ "data" ].map{ |e| Entity.new(e) }
72
+ if block_given?
73
+ yield( d )
74
+ else
75
+ result += d
76
+ end
77
+ page += 1
78
+ total = data["totalPages"].to_i
79
+ nextPage = data["nextPageToken"]
80
+ end
81
+ d unless block_given?
82
+ end
83
+
84
+ # Perform an HTTP POST request
85
+ def post(path, options={})
86
+ request(:post, path, options)
87
+ end
88
+
89
+ # Perform an HTTP PUT request
90
+ def put(path, options={})
91
+ request(:put, path, options)
92
+ end
93
+
94
+ # Perform an HTTP DELETE request
95
+ def delete(path, options={})
96
+ request(:delete, path, options)
97
+ end
98
+
99
+ private
100
+
101
+ # Perform an HTTP request
102
+ def request( method, path, options )
103
+ response = connection().send( method ) do |request|
104
+ uri = URI::Parser.new
105
+ case method
106
+ when :get, :delete
107
+ request.url( uri.escape( path ), options )
108
+ when :post, :put
109
+ request.headers['Content-Type'] = "application/#{format}"
110
+ request.path = uri.escape( path )
111
+ request.body = options.to_json unless options.empty?
112
+ end
113
+ end
114
+ response
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module CloudAlly
3
+ VERSION = '0.1.0'
4
+ end
data/lib/cloudally.rb ADDED
@@ -0,0 +1,26 @@
1
+ require File.expand_path('../cloudally/configuration', __FILE__)
2
+ require File.expand_path('../cloudally/api', __FILE__)
3
+ require File.expand_path('../cloudally/client', __FILE__)
4
+ require File.expand_path('../cloudally/version', __FILE__)
5
+
6
+ module CloudAlly
7
+ extend Configuration
8
+
9
+ # Alias for CloudAlly::Client.new
10
+ #
11
+ # @return [CloudAlly::Client]
12
+ def self.client(options={})
13
+ CloudAlly::Client.new(options)
14
+ end
15
+
16
+ # Delegate to CloudAlly::Client
17
+ def self.method_missing(method, *args, &block)
18
+ return super unless client.respond_to?(method)
19
+ client.send(method, *args, &block)
20
+ end
21
+
22
+ # Delegate to CloudAlly::Client
23
+ def self.respond_to?(method, include_all=false)
24
+ return client.respond_to?(method, include_all) || super
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloudally
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Janco Tanis
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-01-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0.7'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '0.10'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '0.7'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.10'
33
+ - !ruby/object:Gem::Dependency
34
+ name: dotenv
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: minitest
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rubocop
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ description:
76
+ email: gems@jancology.com
77
+ executables: []
78
+ extensions: []
79
+ extra_rdoc_files: []
80
+ files:
81
+ - ".env.template"
82
+ - ".gitignore"
83
+ - CHANGELOG.md
84
+ - cloudally.gemspec
85
+ - lib/cloudally.rb
86
+ - lib/cloudally/api.rb
87
+ - lib/cloudally/authentication.rb
88
+ - lib/cloudally/client.rb
89
+ - lib/cloudally/client/partners.rb
90
+ - lib/cloudally/configuration.rb
91
+ - lib/cloudally/connection.rb
92
+ - lib/cloudally/request.rb
93
+ - lib/cloudally/version.rb
94
+ homepage: https://rubygems.org/gems/cloudally
95
+ licenses:
96
+ - MIT
97
+ metadata:
98
+ homepage_uri: https://rubygems.org/gems/cloudally
99
+ source_code_uri: https://github.com/jancotanis/cloudally
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 2.4.0
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubygems_version: 3.2.12
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: A Ruby wrapper for the CloudAlly Partner Portal REST APIs (readonly)
119
+ test_files: []