datacentred 0.1.1pre → 1.1.1

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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +11 -0
  3. data/.coveralls.yml +1 -0
  4. data/.gitignore +9 -0
  5. data/.rubocop.yml +2 -0
  6. data/.yardopts +1 -0
  7. data/CODE_OF_CONDUCT.md +46 -0
  8. data/Gemfile.lock +39 -2
  9. data/LICENSE +21 -0
  10. data/README.md +160 -2
  11. data/circle.yml +0 -2
  12. data/datacentred.gemspec +17 -14
  13. data/docs/Datacentred.html +537 -0
  14. data/docs/Datacentred/Errors.html +260 -0
  15. data/docs/Datacentred/Errors/Error.html +139 -0
  16. data/docs/Datacentred/Errors/NotFound.html +145 -0
  17. data/docs/Datacentred/Errors/Unauthorized.html +149 -0
  18. data/docs/Datacentred/Errors/UnprocessableEntity.html +145 -0
  19. data/docs/Datacentred/Model.html +128 -0
  20. data/docs/Datacentred/Model/Base.html +255 -0
  21. data/docs/Datacentred/Model/Project.html +1729 -0
  22. data/docs/Datacentred/Model/Role.html +1830 -0
  23. data/docs/Datacentred/Model/Usage.html +510 -0
  24. data/docs/Datacentred/Model/User.html +832 -0
  25. data/docs/Datacentred/Model/Version.html +451 -0
  26. data/docs/Datacentred/Project.html +142 -0
  27. data/docs/Datacentred/Request.html +128 -0
  28. data/docs/Datacentred/Request/Base.html +675 -0
  29. data/docs/Datacentred/Request/Projects.html +1286 -0
  30. data/docs/Datacentred/Request/Roles.html +1286 -0
  31. data/docs/Datacentred/Request/Usage.html +315 -0
  32. data/docs/Datacentred/Request/Users.html +841 -0
  33. data/docs/Datacentred/Request/Versions.html +258 -0
  34. data/docs/Datacentred/Response.html +410 -0
  35. data/docs/Datacentred/Role.html +142 -0
  36. data/docs/Datacentred/Usage.html +142 -0
  37. data/docs/Datacentred/User.html +142 -0
  38. data/docs/Datacentred/Version.html +142 -0
  39. data/docs/_index.html +349 -0
  40. data/docs/class_list.html +51 -0
  41. data/docs/css/common.css +1 -0
  42. data/docs/css/full_list.css +58 -0
  43. data/docs/css/style.css +492 -0
  44. data/docs/file.README.html +231 -0
  45. data/docs/file_list.html +56 -0
  46. data/docs/frames.html +17 -0
  47. data/docs/index.html +231 -0
  48. data/docs/js/app.js +248 -0
  49. data/docs/js/full_list.js +216 -0
  50. data/docs/js/jquery.js +4 -0
  51. data/docs/method_list.html +643 -0
  52. data/docs/top-level-namespace.html +110 -0
  53. data/lib/datacentred.rb +65 -12
  54. data/lib/datacentred/error.rb +37 -15
  55. data/lib/datacentred/model/base.rb +21 -0
  56. data/lib/datacentred/model/project.rb +90 -31
  57. data/lib/datacentred/model/role.rb +89 -31
  58. data/lib/datacentred/model/usage.rb +16 -9
  59. data/lib/datacentred/model/user.rb +54 -22
  60. data/lib/datacentred/model/version.rb +17 -8
  61. data/lib/datacentred/request/base.rb +68 -31
  62. data/lib/datacentred/request/projects.rb +92 -24
  63. data/lib/datacentred/request/roles.rb +92 -24
  64. data/lib/datacentred/request/usage.rb +10 -1
  65. data/lib/datacentred/request/users.rb +58 -15
  66. data/lib/datacentred/request/versions.rb +13 -1
  67. data/lib/datacentred/response.rb +6 -2
  68. data/test/integration/authorization_test.rb +30 -0
  69. data/test/integration/projects_test.rb +11 -11
  70. data/test/integration/roles_test.rb +17 -17
  71. data/test/integration/usage_test.rb +8 -8
  72. data/test/integration/users_test.rb +23 -19
  73. data/test/integration/versions_test.rb +1 -2
  74. data/test/test_helper.rb +8 -5
  75. data/test/vcr_cassettes/not_authorized.yml +57 -0
  76. data/test/vcr_cassettes/unexpected_error.yml +56 -0
  77. metadata +115 -9
@@ -0,0 +1,110 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.9.9
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ pathId = "";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+
41
+
42
+ <span class="title">Top Level Namespace</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Top Level Namespace
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ </div>
80
+
81
+ <h2>Defined Under Namespace</h2>
82
+ <p class="children">
83
+
84
+
85
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="Datacentred.html" title="Datacentred (module)">Datacentred</a></span>
86
+
87
+
88
+
89
+
90
+ </p>
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+ </div>
101
+
102
+ <div id="footer">
103
+ Generated on Fri Sep 15 16:27:41 2017 by
104
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
+ 0.9.9 (ruby-2.3.1).
106
+ </div>
107
+
108
+ </div>
109
+ </body>
110
+ </html>
@@ -1,41 +1,94 @@
1
1
  require 'faraday'
2
2
  require 'json'
3
+ require 'recursive-open-struct'
3
4
  require 'time'
4
5
 
6
+ # Main Datacentred client module
7
+ #
8
+ # This library acts as a Ruby wrapper for the DataCentred API.
5
9
  module Datacentred
6
- require_relative 'datacentred/response'
7
10
  require_relative 'datacentred/error'
8
- require_relative 'datacentred/model/user'
11
+ require_relative 'datacentred/model/base'
9
12
  require_relative 'datacentred/model/project'
10
13
  require_relative 'datacentred/model/role'
11
14
  require_relative 'datacentred/model/usage'
15
+ require_relative 'datacentred/model/user'
12
16
  require_relative 'datacentred/model/version'
13
17
  require_relative 'datacentred/request/base'
14
18
  require_relative 'datacentred/request/projects'
15
- require_relative 'datacentred/request/users'
16
19
  require_relative 'datacentred/request/roles'
17
20
  require_relative 'datacentred/request/usage'
21
+ require_relative 'datacentred/request/users'
18
22
  require_relative 'datacentred/request/versions'
23
+ require_relative 'datacentred/response'
19
24
 
25
+ # Access key credential for the DataCentred API.
26
+ #
27
+ # This value is automatically loaded from the *DATACENTRED_ACCESS* environment variable.
28
+ #
29
+ # You can find your API credentials by logging into the dashboard at https://my.datacentred.io
30
+ #
31
+ # @return [String] API access key credential
20
32
  def self.access_key
21
33
  @@access_key ||= ENV['DATACENTRED_ACCESS']
22
34
  end
23
35
 
24
- def self.access_key=(val)
25
- @@access_key = val
36
+ # Set a new access key value.
37
+ #
38
+ # This will override any values loaded from environment variables.
39
+ #
40
+ # @param [String] new_access_key New access key value
41
+ # @return [String] API access key credential
42
+ def self.access_key=(new_access_key)
43
+ @@access_key = new_access_key
26
44
  end
27
45
 
46
+ # Secret key credential for the DataCentred API.
47
+ #
48
+ # This value is automatically loaded from the *DATACENTRED_SECRET* environment variable.
49
+ #
50
+ # You can find your API credentials by logging into the dashboard at https://my.datacentred.io
51
+ #
52
+ # @return [String] API secret key credential
28
53
  def self.secret_key
29
54
  @@secret_key ||= ENV['DATACENTRED_SECRET']
30
55
  end
31
56
 
32
- def self.secret_key=(val)
33
- @@secret_key = val
57
+ # Set a new secret key value.
58
+ #
59
+ # This will override any values loaded from environment variables.
60
+ #
61
+ # @param [String] new_secret_key New secret key value
62
+ #
63
+ # @return [String] API secret key credential
64
+ def self.secret_key=(new_secret_key)
65
+ @@secret_key = new_secret_key
34
66
  end
35
67
 
36
- User = Model::User
37
- Project = Model::Project
38
- Role = Model::Role
39
- Usage = Model::Usage
40
- Version = Model::Version
68
+ # Shorthand alias for {Model::Project}
69
+ # @see Model::Project
70
+ class Project < Model::Project; end
71
+
72
+ # Shorthand alias for {Model::Role}
73
+ # @see Model::Role
74
+ class Role < Model::Role; end
75
+
76
+ # Shorthand alias for {Model::Usage}
77
+ # @see Model::Usage
78
+ class Usage < Model::Usage; end
79
+
80
+ # Shorthand alias for {Model::User}
81
+ # @see Model::User
82
+ class User < Model::User; end
83
+
84
+ # Shorthand alias for {Model::Version}
85
+ # @see Model::Version
86
+ class Version < Model::Version; end
87
+
88
+ # Model classes representing RESTful API entities.
89
+ module Model ; end
90
+
91
+ # Request classes representing RESTful API interactions.
92
+ module Request
93
+ end
41
94
  end
@@ -1,28 +1,50 @@
1
1
  module Datacentred
2
- class Error < StandardError
2
+ # Behaviours and exceptions for recoverable errors.
3
+ module Errors
4
+ # Test server response and raise appropriate error if an error has been returned.
5
+ #
6
+ # @raise [Error] Appropriate error for server response code.
7
+ # @return [nil] Returns nil on success
3
8
  def self.raise_unless_successful(status, body)
4
9
  return if status.to_s.start_with? "2" # 2xx
5
- err = Datacentred::errors[status]
10
+ err = errors[status]
6
11
  message = body&.fetch("errors")&.first&.fetch("detail")
7
12
  if err
8
- raise err, message
13
+ raise err, message || status.to_s
9
14
  else
10
- raise Datacentred::Error, "Error #{status}: #{message}"
15
+ raise Error, "Error #{status}: #{message}"
11
16
  end
12
17
  end
13
- end
14
18
 
15
- class NotFoundError < StandardError; end
16
- class UnprocessableEntity < StandardError; end
17
- class Unauthorized < StandardError; end
19
+ # Datacentred base error
20
+ class Error < StandardError ; end
21
+
22
+ # Raised when an entity cannot be located using the unique id specified.
23
+ #
24
+ # Corresponds to a HTTP 404 error.
25
+ class NotFound < Error; end
26
+
27
+ # Raised usually when data validations fail on operations that mutate state.
28
+ #
29
+ # Corresponds to a HTTP 422 error.
30
+ class UnprocessableEntity < Error; end
18
31
 
19
- private
32
+ # Raised when credentials are invalid.
33
+ #
34
+ # Credentials may be invalid because they're incorrect, or because they correspond to an account
35
+ # that does not have the correct permissions to access the API.
36
+ #
37
+ # Corresponds to a HTTP 403 error.
38
+ class Unauthorized < Error; end
20
39
 
21
- def self.errors
22
- {
23
- 401 => Unauthorized,
24
- 404 => NotFoundError,
25
- 422 => UnprocessableEntity
26
- }
40
+ private
41
+
42
+ def self.errors
43
+ {
44
+ 401 => Datacentred::Errors::Unauthorized,
45
+ 404 => Datacentred::Errors::NotFound,
46
+ 422 => Datacentred::Errors::UnprocessableEntity
47
+ }
48
+ end
27
49
  end
28
50
  end
@@ -0,0 +1,21 @@
1
+ module Datacentred
2
+ module Model
3
+ # Base class for all API models.
4
+ #
5
+ # Uses Recursive Structs to allow nested property access.
6
+ class Base < RecursiveOpenStruct
7
+ # Instantiate a new model object.
8
+ #
9
+ # @param [Hash] params Object properties as returned by the API.
10
+ def initialize(params, _opts=nil)
11
+ params.delete "links" if params['links']
12
+
13
+ ["created_at", "updated_at", "last_updated_at"].each do |key|
14
+ params[key] = Time.parse(params[key]) if params[key]
15
+ end
16
+
17
+ super params, recurse_over_arrays: true
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,43 +1,102 @@
1
1
  module Datacentred
2
2
  module Model
3
- class Project < OpenStruct
4
- def initialize(params)
5
- params.delete("links") if params
6
- params["created_at"] = Time.parse params["created_at"]
7
- params["updated_at"] = Time.parse params["updated_at"]
8
- super(params)
9
- end
3
+ # A project on your DataCentred account.
4
+ #
5
+ # Projects (also called "Cloud Projects" or "Tenants") are a way of grouping together users and resources.
6
+ #
7
+ # All projects created in your DataCented account are backed by a corresponding project in OpenStack's identity service (Keystone).
8
+ #
9
+ # @attr [String] id
10
+ # @attr [String] name
11
+ # @attr [Hash] quota_set
12
+ # @attr_reader [Time] created_at
13
+ # @attr_reader [Time] updated_at
14
+ class Project < Base
15
+ class << self
16
+ # Create a new project.
17
+ #
18
+ # @param [Hash] params Project attributes
19
+ # @raise [Errors::UnprocessableEntity] Raised if validations fail for the supplied attributes.
20
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
21
+ # @return [Project] New project.
22
+ def create(params)
23
+ new Request::Projects.create params
24
+ end
10
25
 
11
- def self.all
12
- Request::Projects.list.map{ |project| new(project) }
13
- end
26
+ # List all available projects.
27
+ #
28
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
29
+ # @return [[Project]] A collection of all projects on this account.
30
+ def all
31
+ Request::Projects.list.map{|project| new project }
32
+ end
14
33
 
15
- def self.find(id)
16
- new Request::Projects.show(id)
17
- end
34
+ # Find a project by unique ID.
35
+ #
36
+ # @param [String] id The unique identifier for this project.
37
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
38
+ # @raise [Errors::NotFound] Raised if the project couldn't be found.
39
+ # @return [Project] The project, if it exists.
40
+ def find(id)
41
+ new Request::Projects.show id
42
+ end
18
43
 
19
- def self.create(params)
20
- new Request::Projects.create(params)
21
- end
44
+ # Update a project by unique ID.
45
+ #
46
+ # @param [String] id The unique identifier for this project.
47
+ # @param [Hash] params Project attributes.
48
+ # @raise [Errors::UnprocessableEntity] Raised if validations fail for the supplied attributes.
49
+ # @raise [Errors::NotFound] Raised if the project could not be found.
50
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
51
+ # @return [Project] The updated project.
52
+ def update(id, params)
53
+ new Request::Projects.update id, params
54
+ end
22
55
 
23
- def self.update(id, params)
24
- new Request::Projects.update(id, params)
25
- end
56
+ # Permanently remove the specified project.
57
+ #
58
+ # @param [String] id The unique identifier for this project.
59
+ # @raise [Errors::NotFound] Raised if the project couldn't be found.
60
+ # @raise [Errors::UnprocessableEntity] Raised if validations fail for the specified project.
61
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
62
+ # @return [Boolean] Confirms the user was destroyed.
63
+ def destroy(id)
64
+ Request::Projects.destroy id
65
+ true
66
+ end
26
67
 
27
- def self.remove(id)
28
- Request::Projects.destroy(id)
29
- end
30
-
31
- def self.users(id)
32
- Request::Projects.list_users(id).map{ |user| new(user) }
33
- end
68
+ # List all users assigned to this project.
69
+ #
70
+ # @param [String] id The unique identifier for this project.
71
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
72
+ # @return [[User]] A collection of the project's users.
73
+ def users(id)
74
+ Request::Projects.list_users(id).map{|user| new user }
75
+ end
34
76
 
35
- def self.add_user(project_id, user_id)
36
- Request::Projects.add_user(project_id, user_id)
37
- end
77
+ # Add a new user to this project, giving them access to it via OpenStack.
78
+ #
79
+ # @param [String] project_id The unique identifier for this project.
80
+ # @param [String] user_id The unique identifier for this user.
81
+ # @raise [Errors::NotFound] Raised if the project or user couldn't be found.
82
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
83
+ # @return [Boolean] Confirms the user was added (or is already present).
84
+ def add_user(project_id:, user_id:)
85
+ Request::Projects.add_user project_id, user_id
86
+ true
87
+ end
38
88
 
39
- def self.remove_user(project_id, user_id)
40
- Request::Projects.remove_user(project_id, user_id)
89
+ # Remove user from this project, revoking their access to it on OpenStack.
90
+ #
91
+ # @param [String] project_id The unique identifier for this project.
92
+ # @param [String] user_id The unique identifier for this user.
93
+ # @raise [Errors::NotFound] Raised if project or user couldn't be found.
94
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
95
+ # @return [Boolean] Confirms that user was removed (or is already absent).
96
+ def remove_user(project_id:, user_id:)
97
+ Request::Projects.remove_user project_id, user_id
98
+ true
99
+ end
41
100
  end
42
101
  end
43
102
  end
@@ -1,43 +1,101 @@
1
1
  module Datacentred
2
2
  module Model
3
- class Role < OpenStruct
4
- def initialize(params)
5
- params.delete("links")
6
- params["created_at"] = Time.parse params["created_at"]
7
- params["updated_at"] = Time.parse params["updated_at"]
8
- super(params)
9
- end
3
+ # A role on your DataCentred account.
4
+ #
5
+ # Roles allow simple setup of user permissions via the creation of roles, then assigning those roles to users.
6
+ #
7
+ # @attr [String] id
8
+ # @attr [String] name
9
+ # @attr [Boolean] admin
10
+ # @attr [[String]] permissions
11
+ # @attr_reader [Time] created_at
12
+ # @attr_reader [Time] updated_at
13
+ class Role < Base
14
+ class << self
15
+ # Create a new role.
16
+ #
17
+ # @param [Hash] params Role attributes.
18
+ # @raise [Errors::UnprocessableEntity] Raised if validations fail for the supplied attributes.
19
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
20
+ # @return [Role] New role.
21
+ def create(params)
22
+ new Request::Roles.create params
23
+ end
10
24
 
11
- def self.all
12
- Request::Roles.list.map{|role| new(role) }
13
- end
25
+ # List all available roles.
26
+ #
27
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
28
+ # @return [[Role]] A collection of all roles on this account.
29
+ def all
30
+ Request::Roles.list.map{|role| new role }
31
+ end
14
32
 
15
- def self.find(id)
16
- new Request::Roles.show(id)
17
- end
33
+ # Find a role by unique ID.
34
+ #
35
+ # @param [String] id The unique identifier for this role.
36
+ # @raise [Errors::NotFound] Raised if the role couldn't be found.
37
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
38
+ # @return [Role] The role, if it exists.
39
+ def find(id)
40
+ new Request::Roles.show id
41
+ end
18
42
 
19
- def self.create(params)
20
- new Request::Roles.create(params)
21
- end
43
+ # Update a role by unique ID.
44
+ #
45
+ # @param [String] id The unique identifier for this role.
46
+ # @param [Hash] params Role attributes.
47
+ # @raise [Errors::UnprocessableEntity] Raised if validations fail for the supplied attributes.
48
+ # @raise [Errors::NotFound] Raised if the role doesn't exist.
49
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
50
+ # @return [Role] The updated role.
51
+ def update(id, params)
52
+ new Request::Roles.update id, params
53
+ end
22
54
 
23
- def self.update(id, params)
24
- new Request::Roles.update(id, params)
25
- end
55
+ # Permanently remove the specified role.
56
+ #
57
+ # @param [String] id The unique identifier for this role.
58
+ # @raise [Errors::NotFound] Raised if the role couldn't be found.
59
+ # @raise [Errors::UnprocessableEntity] Raised if validations fail for the specifed role.
60
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
61
+ # @return [Boolean] Confirms the role was destroyed.
62
+ def destroy(id)
63
+ Request::Roles.destroy id
64
+ true
65
+ end
26
66
 
27
- def self.delete(id)
28
- Request::Roles.destroy(id)
29
- end
30
-
31
- def self.users(id)
32
- Request::Roles.list_users(id).map{|user| new(user) }
33
- end
67
+ # List all users assigned to this role.
68
+ #
69
+ # @param [String] id The unique identifier for this role.
70
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
71
+ # @return [[User]] A collection of the role's users.
72
+ def users(id)
73
+ Request::Roles.list_users(id).map{|user| new user }
74
+ end
34
75
 
35
- def self.add_user(role_id, user_id)
36
- Request::Roles.add_user(role_id, user_id)
37
- end
76
+ # Add new user to this role, giving them the associated permissions.
77
+ #
78
+ # @param [String] role_id The unique identifier for this role.
79
+ # @param [String] user_id The unique identifier for this user.
80
+ # @raise [Errors::NotFound] Raised if the role or user couldn't be found.
81
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
82
+ # @return [Boolean] Confirms that user was added (or is already present).
83
+ def add_user(role_id:, user_id:)
84
+ Request::Roles.add_user role_id, user_id
85
+ true
86
+ end
38
87
 
39
- def self.remove_user(role_id, user_id)
40
- Request::Roles.remove_user(role_id, user_id)
88
+ # Remove user from this role, revoking the associated permissions.
89
+ #
90
+ # @param [String] role_id The unique identifier for this role.
91
+ # @param [String] user_id The unique identifier for this user.
92
+ # @raise [Errors::NotFound] Raised if the role or user coundn't be found.
93
+ # @raise [Errors::Unauthorized] Raised if credentials aren't valid.
94
+ # @return [Boolean] Confirms that user was removed (or is already absent).
95
+ def remove_user(role_id:, user_id:)
96
+ Request::Roles.remove_user role_id, user_id
97
+ true
98
+ end
41
99
  end
42
100
  end
43
101
  end