kloudless 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 +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +62 -0
- data/Rakefile +10 -0
- data/TODO.md +26 -0
- data/kloudless.gemspec +25 -0
- data/lib/kloudless.rb +50 -0
- data/lib/kloudless/account.rb +33 -0
- data/lib/kloudless/account_key.rb +26 -0
- data/lib/kloudless/collection.rb +32 -0
- data/lib/kloudless/error.rb +172 -0
- data/lib/kloudless/event.rb +16 -0
- data/lib/kloudless/file.rb +50 -0
- data/lib/kloudless/folder.rb +41 -0
- data/lib/kloudless/http.rb +114 -0
- data/lib/kloudless/link.rb +34 -0
- data/lib/kloudless/model.rb +20 -0
- data/lib/kloudless/multipart_upload.rb +37 -0
- data/lib/kloudless/team.rb +9 -0
- data/lib/kloudless/version.rb +3 -0
- data/script/changelog +29 -0
- data/script/package +7 -0
- data/script/release +16 -0
- data/test/kloudless/account_key_test.rb +26 -0
- data/test/kloudless/account_test.rb +78 -0
- data/test/kloudless/collection_test.rb +49 -0
- data/test/kloudless/error_test.rb +23 -0
- data/test/kloudless/event_test.rb +20 -0
- data/test/kloudless/file_test.rb +63 -0
- data/test/kloudless/folder_test.rb +47 -0
- data/test/kloudless/http_test.rb +58 -0
- data/test/kloudless/link_test.rb +40 -0
- data/test/kloudless/multipart_upload_test.rb +38 -0
- data/test/kloudless/team_test.rb +11 -0
- data/test/kloudless_test.rb +25 -0
- data/test/test_helper.rb +17 -0
- metadata +150 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
module Kloudless
|
2
|
+
# https://developers.kloudless.com/docs#events
|
3
|
+
class Event < Model
|
4
|
+
# https://developers.kloudless.com/docs#events-list-file/folder-events
|
5
|
+
def self.list(account_id:, **params)
|
6
|
+
path = "/accounts/#{account_id}/events"
|
7
|
+
Kloudless::Collection.new(self, http.get(path, params: params))
|
8
|
+
end
|
9
|
+
|
10
|
+
# https://developers.kloudless.com/docs#events-retrieve-latest-cursor
|
11
|
+
def self.cursor(account_id:)
|
12
|
+
path = "/accounts/#{account_id}/events/latest"
|
13
|
+
new(http.get(path))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Kloudless
|
2
|
+
# https://developers.kloudless.com/docs#files
|
3
|
+
class File < Model
|
4
|
+
def self.upload(account_id:, **params)
|
5
|
+
path = "/accounts/#{account_id}/files"
|
6
|
+
new(http.post(path, params: params))
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.metadata(account_id:, file_id:)
|
10
|
+
path = "/accounts/#{account_id}/files/#{file_id}"
|
11
|
+
new(http.get(path))
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.rename(account_id:, file_id:, **params)
|
15
|
+
path = "/accounts/#{account_id}/files/#{file_id}"
|
16
|
+
new(http.patch(path, params: params))
|
17
|
+
end
|
18
|
+
|
19
|
+
# TODO: unclear how to post binary data over net-http
|
20
|
+
def self.update
|
21
|
+
raise NotImplementedError
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.download(account_id:, file_id:)
|
25
|
+
path = "/accounts/#{account_id}/files/#{file_id}/contents"
|
26
|
+
http.get_raw(path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.copy(account_id:, file_id:, parent_id:, **params)
|
30
|
+
path = "/accounts/#{account_id}/files/#{file_id}/copy"
|
31
|
+
params[:parent_id] = parent_id
|
32
|
+
new(http.post(path, params: params))
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.delete(account_id:, file_id:, **params)
|
36
|
+
path = "/accounts/#{account_id}/files/#{file_id}"
|
37
|
+
new(http.delete(path, params: params))
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.recent(account_ids:, **params)
|
41
|
+
path = "/accounts/#{account_ids.join(',')}/recent"
|
42
|
+
Kloudless::Collection.new(self, http.get(path, params: params))
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.search(account_ids:, **params)
|
46
|
+
path = "/accounts/#{account_ids.join(',')}/search"
|
47
|
+
Kloudless::Collection.new(self, http.get(path, params: params))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Kloudless
|
2
|
+
# https://developers.kloudless.com/docs#folders
|
3
|
+
class Folder < Model
|
4
|
+
# https://developers.kloudless.com/docs#folders-create-a-folder
|
5
|
+
def self.create(account_id:, **params)
|
6
|
+
path = "/accounts/#{account_id}/folders"
|
7
|
+
new(http.post(path, params: params))
|
8
|
+
end
|
9
|
+
|
10
|
+
# https://developers.kloudless.com/docs#folders-retrieve-folder-metadata
|
11
|
+
def self.metadata(account_id:, folder_id:)
|
12
|
+
path = "/accounts/#{account_id}/folders/#{folder_id}"
|
13
|
+
new(http.get(path))
|
14
|
+
end
|
15
|
+
|
16
|
+
# https://developers.kloudless.com/docs#folders-retrieve-folder-contents
|
17
|
+
def self.retrieve(account_id:, folder_id:, **params)
|
18
|
+
path = "/accounts/#{account_id}/folders/#{folder_id}/contents"
|
19
|
+
Kloudless::Collection.new(self, http.get(path, params: params))
|
20
|
+
end
|
21
|
+
|
22
|
+
# https://developers.kloudless.com/docs#folders-rename/move-a-folder
|
23
|
+
def self.rename(account_id:, folder_id:, **params)
|
24
|
+
path = "/accounts/#{account_id}/folders/#{folder_id}"
|
25
|
+
new(http.patch(path, params: params))
|
26
|
+
end
|
27
|
+
|
28
|
+
# https://developers.kloudless.com/docs#folders-copy-a-folder
|
29
|
+
def self.copy(account_id:, folder_id:, parent_id:, **params)
|
30
|
+
params[:parent_id] = parent_id
|
31
|
+
path = "/accounts/#{account_id}/folders/#{folder_id}/copy"
|
32
|
+
new(http.post(path, params: params))
|
33
|
+
end
|
34
|
+
|
35
|
+
# https://developers.kloudless.com/docs#folders-delete-a-folder
|
36
|
+
def self.delete(account_id:, folder_id:, **params)
|
37
|
+
path = "/accounts/#{account_id}/folders/#{folder_id}"
|
38
|
+
new(http.delete(path, params: params))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require "net/http"
|
2
|
+
|
3
|
+
module Kloudless
|
4
|
+
# Net::HTTP wrapper
|
5
|
+
class HTTP
|
6
|
+
# Public: Headers global to all requests
|
7
|
+
def self.headers
|
8
|
+
@headers ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.get(path, params: {}, headers: {})
|
12
|
+
uri = URI.parse(Kloudless::API_URL + path)
|
13
|
+
uri.query = URI.encode_www_form(params) if !params.empty?
|
14
|
+
|
15
|
+
request = Net::HTTP::Get.new(uri)
|
16
|
+
request.initialize_http_header(headers)
|
17
|
+
|
18
|
+
execute(request)
|
19
|
+
end
|
20
|
+
|
21
|
+
# TODO: decouple Kloudless::HTTP methods from #execute. Have methods return
|
22
|
+
# request object, defaults to json parsing, but allow the option for raw
|
23
|
+
def self.get_raw(path)
|
24
|
+
uri = URI.parse(Kloudless::API_URL + path)
|
25
|
+
request = Net::HTTP::Get.new(uri)
|
26
|
+
request.initialize_http_header(headers)
|
27
|
+
|
28
|
+
execute(request, parse_json: false)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def self.post(path, params: {}, headers: {})
|
33
|
+
uri = URI.parse(Kloudless::API_URL + path)
|
34
|
+
headers["Content-Type"] = "application/json"
|
35
|
+
|
36
|
+
request = Net::HTTP::Post.new(uri)
|
37
|
+
request.initialize_http_header(headers)
|
38
|
+
request.set_form_data(params) if !params.empty?
|
39
|
+
|
40
|
+
execute(request)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.patch(path, params: {}, headers: {})
|
44
|
+
uri = URI.parse(Kloudless::API_URL + path)
|
45
|
+
headers["Content-Type"] = "application/json"
|
46
|
+
|
47
|
+
request = Net::HTTP::Post.new(uri)
|
48
|
+
request.initialize_http_header(headers)
|
49
|
+
request.set_form_data(params) if !params.empty?
|
50
|
+
|
51
|
+
execute(request)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.delete(path, params: {}, headers: {})
|
55
|
+
uri = URI.parse(Kloudless::API_URL + path)
|
56
|
+
uri.query = URI.encode_www_form(params) if !params.empty?
|
57
|
+
|
58
|
+
request = Net::HTTP::Delete.new(uri)
|
59
|
+
request.initialize_http_header(headers)
|
60
|
+
|
61
|
+
execute(request)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.execute(request, parse_json: true)
|
65
|
+
uri = request.uri
|
66
|
+
@last_request = request
|
67
|
+
headers.each {|k,v| request[k] = v}
|
68
|
+
|
69
|
+
response = @mock_response || Net::HTTP.start(uri.hostname, uri.port, use_ssl: (uri.scheme == "https")) {|http|
|
70
|
+
http.request(request)
|
71
|
+
}
|
72
|
+
|
73
|
+
if parse_json
|
74
|
+
json = JSON.parse(response.body)
|
75
|
+
raise Kloudless::Error.from_json(json) if json["error_code"]
|
76
|
+
json
|
77
|
+
else
|
78
|
+
response
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Internal: Returns `response` the next time #execute is invoked for the
|
83
|
+
# duration of `blk`. Used for testing.
|
84
|
+
#
|
85
|
+
# mock = Struct.new(:body).new("{}")
|
86
|
+
# Kloudless::HTTP.mock_response(mock) do
|
87
|
+
# Kloudless::HTTP.get "/foo" # returns {}
|
88
|
+
# end
|
89
|
+
def self.mock_response(response = nil, &blk)
|
90
|
+
@mock_response = response || Struct.new(:body).new("{}")
|
91
|
+
blk.call
|
92
|
+
ensure
|
93
|
+
@mock_response = nil
|
94
|
+
end
|
95
|
+
|
96
|
+
require 'minitest/mock'
|
97
|
+
def self.expect(method, returns: nil, args: [], &blk)
|
98
|
+
begin
|
99
|
+
mock = MiniTest::Mock.new
|
100
|
+
Kloudless.http = mock
|
101
|
+
mock.expect(method, returns, args)
|
102
|
+
blk.call
|
103
|
+
mock.verify
|
104
|
+
ensure
|
105
|
+
Kloudless.http = Kloudless::HTTP
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Internal: Returns the last Net::HTTP request sent by #execute.
|
110
|
+
def self.last_request
|
111
|
+
@last_request
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Kloudless
|
2
|
+
# https://developers.kloudless.com/docs#links
|
3
|
+
class Link < Model
|
4
|
+
def self.list(account_ids:, **params)
|
5
|
+
path = "/accounts/#{account_ids.join(',')}/links"
|
6
|
+
Kloudless::Collection.new(self, http.get(path, params: params))
|
7
|
+
end
|
8
|
+
|
9
|
+
# https://developers.kloudless.com/docs#links-create-a-link
|
10
|
+
def self.create(account_id:, file_id:, **params)
|
11
|
+
params[:file_id] = file_id
|
12
|
+
path = "/accounts/#{account_id}/links"
|
13
|
+
new(http.post(path, params: params))
|
14
|
+
end
|
15
|
+
|
16
|
+
# https://developers.kloudless.com/docs#links-retrieve-a-link
|
17
|
+
def self.retrieve(account_id:, link_id:, **params)
|
18
|
+
path = "/accounts/#{account_id}/links/#{link_id}"
|
19
|
+
new(http.get(path, params: params))
|
20
|
+
end
|
21
|
+
|
22
|
+
# https://developers.kloudless.com/docs#links-update-a-link
|
23
|
+
def self.update(account_id:, link_id:, **params)
|
24
|
+
path = "/accounts/#{account_id}/links/#{link_id}"
|
25
|
+
new(http.patch(path, params: params))
|
26
|
+
end
|
27
|
+
|
28
|
+
# https://developers.kloudless.com/docs#links-delete-a-link
|
29
|
+
def self.delete(account_id:, link_id:)
|
30
|
+
path = "/accounts/#{account_id}/links/#{link_id}"
|
31
|
+
new(http.delete(path))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Kloudless
|
2
|
+
# Public: Base class for different API resources. e.g. Account, Team, Files.
|
3
|
+
class Model
|
4
|
+
def self.http
|
5
|
+
Kloudless.http
|
6
|
+
end
|
7
|
+
|
8
|
+
def http
|
9
|
+
self.class.http
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(attributes = {})
|
13
|
+
@attributes = attributes
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(name, *args, &blk)
|
17
|
+
@attributes[name.to_s] || super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Kloudless
|
2
|
+
# https://developers.kloudless.com/docs#multipart-upload
|
3
|
+
class MultipartUpload < Model
|
4
|
+
def self.init(account_id:, **params)
|
5
|
+
path = "/accounts/#{account_id}/multipart"
|
6
|
+
new(http.post(path, params: params))
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.retrieve(account_id:, multipart_id:)
|
10
|
+
path = "/accounts/#{account_id}/multipart/#{multipart_id}"
|
11
|
+
new(http.get(path))
|
12
|
+
end
|
13
|
+
|
14
|
+
# https://developers.kloudless.com/docs#multipart-upload-upload-part
|
15
|
+
def self.upload(account_id:, multipart_id:, part_number:, **params)
|
16
|
+
path = "/accounts/#{account_id}/multipart/#{multipart_id}"
|
17
|
+
params[:part_number] = part_number
|
18
|
+
new(http.put(path, params: params))
|
19
|
+
end
|
20
|
+
|
21
|
+
# https://developers.kloudless.com/docs#multipart-upload-finalize-multipart-session
|
22
|
+
def self.finalize(account_id:, multipart_id:)
|
23
|
+
path = "/accounts/#{account_id}/multipart/#{multipart_id}/complete"
|
24
|
+
new(http.post(path))
|
25
|
+
end
|
26
|
+
|
27
|
+
# https://developers.kloudless.com/docs#multipart-upload-abort-multipart-session
|
28
|
+
def self.abort(account_id:, multipart_id:)
|
29
|
+
path = "/accounts/#{account_id}/multipart/#{multipart_id}"
|
30
|
+
new(http.delete(path))
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
alias_method :delete, :abort
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/script/changelog
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env sh
|
2
|
+
# Usage: script/changelog [-r <repo>] [-b <base>] [-h <head>]
|
3
|
+
#
|
4
|
+
# repo: base string of GitHub repository url. e.g. "user_or_org/repository". Defaults to git remote url.
|
5
|
+
# base: git ref to compare from. e.g. "v1.3.1". Defaults to latest git tag.
|
6
|
+
# head: git ref to compare to. Defaults to "HEAD".
|
7
|
+
#
|
8
|
+
# Generate a changelog preview from pull requests merged between `base` and
|
9
|
+
# `head`.
|
10
|
+
#
|
11
|
+
set -e
|
12
|
+
|
13
|
+
[ $# -eq 0 ] && set -- --help
|
14
|
+
|
15
|
+
# parse args
|
16
|
+
repo=$(git remote -v | grep push | awk '{print $2}' | cut -d'/' -f4- | sed 's/\.git//')
|
17
|
+
base=$(git tag -l | sort -n | tail -n 1)
|
18
|
+
head="HEAD"
|
19
|
+
api_url="https://api.github.com"
|
20
|
+
|
21
|
+
echo "# $repo $base..$head"
|
22
|
+
echo
|
23
|
+
|
24
|
+
# get merged PR's. Better way is to query the API for these, but this is easier
|
25
|
+
for pr in $(git log --oneline $base..$head | grep "Merge pull request" | awk '{gsub("#",""); print $5}')
|
26
|
+
do
|
27
|
+
# frustrated with trying to pull out the right values, fell back to ruby
|
28
|
+
curl -s "$api_url/repos/$repo/pulls/$pr" | ruby -rjson -e 'pr=JSON.parse(STDIN.read); puts "* #{pr[%q(title)]} [##{pr[%q(number)]}](#{pr[%q(html_url)]})"'
|
29
|
+
done
|
data/script/package
ADDED
data/script/release
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
# Usage: script/release
|
3
|
+
# Build the package, tag a commit, push it to origin, and then release the
|
4
|
+
# package publicly.
|
5
|
+
|
6
|
+
set -e
|
7
|
+
|
8
|
+
version="$(script/package | grep Version: | awk '{print $2}')"
|
9
|
+
[ -n "$version" ] || exit 1
|
10
|
+
|
11
|
+
echo $version
|
12
|
+
git commit --allow-empty -a -m "Release $version"
|
13
|
+
git tag "v$version"
|
14
|
+
git push origin
|
15
|
+
git push origin "v$version"
|
16
|
+
gem push pkg/*-${version}.gem
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
require 'minitest'
|
3
|
+
|
4
|
+
class Kloudless::AccountKeyTest < Minitest::Test
|
5
|
+
def test_list_account_keys
|
6
|
+
Kloudless.http.expect(:get, returns: {"objects" => [{}]}, args: ["/accounts/1,2/keys", params: {}]) do
|
7
|
+
account_keys = Kloudless::AccountKey.list(account_ids: [1,2])
|
8
|
+
assert_kind_of Kloudless::Collection, account_keys
|
9
|
+
assert_kind_of Kloudless::AccountKey, account_keys.first
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_retrieve_account_key
|
14
|
+
Kloudless.http.expect(:get, args: ["/accounts/1/keys/2", params: {}]) do
|
15
|
+
account_key = Kloudless::AccountKey.retrieve(account_id: 1, key_id: 2)
|
16
|
+
assert_kind_of Kloudless::AccountKey, account_key
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_delete_account_key
|
21
|
+
Kloudless.http.expect(:delete, args: ["/accounts/1/keys/2"]) do
|
22
|
+
account_key = Kloudless::AccountKey.delete(account_id: 1, key_id: 2)
|
23
|
+
assert_kind_of Kloudless::AccountKey, account_key
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
|
3
|
+
class Kloudless::AccountTest < Minitest::Test
|
4
|
+
def test_list_accounts
|
5
|
+
json = JSON.parse <<-JSON
|
6
|
+
{
|
7
|
+
"total": 2,
|
8
|
+
"count": 2,
|
9
|
+
"page": 1,
|
10
|
+
"objects": [
|
11
|
+
{
|
12
|
+
"id": 90817234,
|
13
|
+
"account": "someone@gmail.com",
|
14
|
+
"service": "box",
|
15
|
+
"service_name": "Box",
|
16
|
+
"admin": false,
|
17
|
+
"active": true,
|
18
|
+
"created": "2015-03-17T20:42:17.954885Z",
|
19
|
+
"modified": "2015-03-17T20:42:17.954885Z",
|
20
|
+
"token_expiry": "2015-04-17T20:42:17.954885Z",
|
21
|
+
"refresh_token_expiry": null
|
22
|
+
},
|
23
|
+
{
|
24
|
+
"id": 6535892,
|
25
|
+
"account": "someone-else@gmail.com",
|
26
|
+
"service": "egnyte",
|
27
|
+
"service_name": "Egnyte",
|
28
|
+
"admin": false,
|
29
|
+
"active": true,
|
30
|
+
"created": "2015-03-17T20:42:18.627533Z",
|
31
|
+
"modified": "2015-03-17T20:42:18.627533Z",
|
32
|
+
"token_expiry": "2015-04-17T20:42:18.627533Z",
|
33
|
+
"refresh_token_expiry": null
|
34
|
+
}
|
35
|
+
]
|
36
|
+
}
|
37
|
+
JSON
|
38
|
+
|
39
|
+
Kloudless.http.expect(:get, returns: json, args: ["/accounts", {params: {}}]) do
|
40
|
+
accounts = Kloudless::Account.list
|
41
|
+
assert_kind_of Kloudless::Collection, accounts
|
42
|
+
assert_kind_of Kloudless::Account, accounts.first
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_import_account
|
47
|
+
attributes = {
|
48
|
+
account: "someone@gmail.com",
|
49
|
+
service: "copy",
|
50
|
+
token: "foo",
|
51
|
+
token_secret: "bar"
|
52
|
+
}
|
53
|
+
Kloudless.http.expect(:post, args: ["/accounts", {params: attributes}]) do
|
54
|
+
Kloudless::Account.import(attributes)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_retrieve_account
|
59
|
+
Kloudless.http.expect(:get, args: ["/accounts/1", {params: {active: false}}]) do
|
60
|
+
account = Kloudless::Account.retrieve(account_id: 1, active: false)
|
61
|
+
assert_kind_of Kloudless::Account, account
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_update_account
|
66
|
+
Kloudless.http.expect(:patch, args: ["/accounts/1", {params: {active: true}}]) do
|
67
|
+
account = Kloudless::Account.update(account_id: 1, active: true)
|
68
|
+
assert_kind_of Kloudless::Account, account
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_delete_account
|
73
|
+
Kloudless.http.expect(:delete, args: ["/accounts/1"]) do
|
74
|
+
account = Kloudless::Account.delete(account_id: 1)
|
75
|
+
assert_kind_of Kloudless::Account, account
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|