sonarqube 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE.txt +24 -0
- data/README.md +250 -0
- data/lib/sonarqube.rb +46 -0
- data/lib/sonarqube/api.rb +22 -0
- data/lib/sonarqube/client.rb +36 -0
- data/lib/sonarqube/client/groups.rb +124 -0
- data/lib/sonarqube/client/projects.rb +111 -0
- data/lib/sonarqube/client/users.rb +125 -0
- data/lib/sonarqube/configuration.rb +55 -0
- data/lib/sonarqube/error.rb +154 -0
- data/lib/sonarqube/objectified_hash.rb +51 -0
- data/lib/sonarqube/request.rb +92 -0
- data/lib/sonarqube/version.rb +5 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 226a939373b149524665b8094209ab3cf99df41f2e0392fdb8bc2c4741696e01
|
4
|
+
data.tar.gz: '08dd9fbb620105b33bba11f5847d25e4ea5c1f9be67ffcfab9dd2384ad58e4cb'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4f98a82d09ff64eb1c241bf48985fb14152b943347fa30d5067dd3482f8e94099cc118b12b7f617f0430cbcd0c49a64cef49e910de4998b1d42b91316ebc4c81
|
7
|
+
data.tar.gz: 80f4924d4c7b9c921102e1f37bc267f7c1736603b7d62f2c4faba0444009caba931d95f65c74b11c2683ccc2ecbe1e1c8f33efa76ba26528e17dc7ef3761303d
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (c) 2020 Lucas Mariani <marianilucas@gmail.com>
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
15
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
16
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
17
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
18
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
19
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
20
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
21
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
22
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
23
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
24
|
+
POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
# Sonarqube
|
2
|
+
|
3
|
+
[![Build Status](https://img.shields.io/github/workflow/status/psyreactor/sonarqube/CI/master)](https://github.com/psyreactor/sonarqube/actions?query=workflow%3ARuby)
|
4
|
+
[![Inline docs](https://inch-ci.org/github/psyreactor/sonarqube.svg)](https://inch-ci.org/github/psyreactor/sonarqube)
|
5
|
+
[![Gem version](https://img.shields.io/gem/v/sonarqube.svg)](https://rubygems.org/gems/sonarqube)
|
6
|
+
[![License](https://img.shields.io/badge/license-BSD-red.svg)](https://github.com/psyreactor/sonarqube/blob/master/LICENSE.txt)
|
7
|
+
|
8
|
+
[website](https://psyreactor.github.io/sonarqube) |
|
9
|
+
[documentation](https://www.rubydoc.info/gems/sonarqube/frames)
|
10
|
+
|
11
|
+
Sonarqube is a Ruby wrapper and CLI for the Sonarqube API
|
12
|
+
As of version `1.0.0` this gem only supports Sonarqube 7.9.
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Install it from rubygems:
|
17
|
+
|
18
|
+
```sh
|
19
|
+
gem install sonarqube
|
20
|
+
```
|
21
|
+
|
22
|
+
Or add to a Gemfile:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
gem 'sonarqube'
|
26
|
+
# gem 'sonarqube', github: 'psyreactor/sonarqube'
|
27
|
+
```
|
28
|
+
|
29
|
+
Mac OS users can install using Homebrew (may not be the latest version):
|
30
|
+
|
31
|
+
```sh
|
32
|
+
brew install sonarqube-gem
|
33
|
+
```
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
### Configuration example
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
Sonarqube.configure do |config|
|
41
|
+
config.endpoint = 'https://example.net:9000' # API endpoint URL, default: ENV['SONARQUBE_API_ENDPOINT']
|
42
|
+
config.private_token = 'Dfrt938dSgAOWd4' # user's private token, default: ENV['SONARQUBE_API_PRIVATE_TOKEN']
|
43
|
+
# Optional
|
44
|
+
# config.user_agent = 'Custom User Agent' # user agent, default: 'Sonarqube Ruby Gem [version]'
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
### Usage examples
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
# set an API endpoint
|
52
|
+
Sonarqube.endpoint = 'https://example.net:9000'
|
53
|
+
# => "https://example.net:9000"
|
54
|
+
|
55
|
+
# set a user private token
|
56
|
+
Sonarqube.private_token = 'Dfrt938dSgAOWd4'
|
57
|
+
# => "Dfrt938dSgAOWd4"
|
58
|
+
|
59
|
+
# configure a proxy server
|
60
|
+
Sonarqube.http_proxy('proxyhost', 8888)
|
61
|
+
# proxy server with basic auth
|
62
|
+
Sonarqube.http_proxy('proxyhost', 8888, 'proxyuser', 'strongpasswordhere')
|
63
|
+
# set timeout for responses
|
64
|
+
ENV['SONARQUBE_API_HTTPARTY_OPTIONS'] = '{read_timeout: 60}'
|
65
|
+
```
|
66
|
+
|
67
|
+
### initialize a new client with custom headers
|
68
|
+
```ruby
|
69
|
+
g = Sonarqube.client(
|
70
|
+
endpoint: 'https://example.com:9000',
|
71
|
+
private_token: 'Dfrt938dSgAOWd4',
|
72
|
+
httparty: {
|
73
|
+
headers: { 'Cookie' => 'sonarqube_canary=true' }
|
74
|
+
}
|
75
|
+
)
|
76
|
+
# => #<Sonarqube::Client:0x00000001e62408 @endpoint="https://api.example.com", @private_token="qEsq1pt6HJPaNciie3MG", @user_agent="Sonarqube Ruby Gem 2.0.0">
|
77
|
+
```
|
78
|
+
|
79
|
+
### Projects
|
80
|
+
|
81
|
+
#### Create Project
|
82
|
+
```ruby
|
83
|
+
project = Sonarqube.project_create('new_project')
|
84
|
+
# => <Sonarqube::ObjectifiedHash:46200 {hash: {"project"=>{"key"=>"new_project", "name"=>"new_project", "qualifier"=>"TRK", "visibility"=>"public"}}}
|
85
|
+
project.project.key
|
86
|
+
# => "new_project"
|
87
|
+
project.to_hash
|
88
|
+
# => {"project"=>{"key"=>"new_project", "name"=>"new_project", "qualifier"=>"TRK", "visibility"=>"public"}}
|
89
|
+
```
|
90
|
+
|
91
|
+
#### Delete Project
|
92
|
+
```ruby
|
93
|
+
project = Sonarqube.project_delete('test')
|
94
|
+
# => #<Sonarqube::ObjectifiedHash:46220 {hash: {}}
|
95
|
+
project.to_hash.empty?
|
96
|
+
# => true
|
97
|
+
```
|
98
|
+
|
99
|
+
#### Search Project
|
100
|
+
```ruby
|
101
|
+
projects = Sonarqube.project_search()
|
102
|
+
# => #<Sonarqube::ObjectifiedHash:46240 {hash: {"paging"=>{"pageIndex"=>1, "pageSize"=>100, "total"=>2}, "components"=>[{"organization"=>"default-organization", "key"=>"old_key", "name"=>"new_proyect", "qualifier"=>"TRK", "visibility"=>"private"}, {"organization"=>"default-organization", "key"=>"test", "name"=>"test", "qualifier"=>"TRK", "visibility"=>"public"}]}}
|
103
|
+
projects.components.each do | project |
|
104
|
+
puts "name: #{project.name}"
|
105
|
+
puts "key: #{project.key}"
|
106
|
+
end
|
107
|
+
# name: new_proyect
|
108
|
+
# key: old_key
|
109
|
+
# name: test
|
110
|
+
# key: test
|
111
|
+
```
|
112
|
+
|
113
|
+
### Users
|
114
|
+
|
115
|
+
#### Create User
|
116
|
+
```ruby
|
117
|
+
user = Sonarqube.user_create('new_user', 'key_new_user' ,'secretpassword')
|
118
|
+
# => #<Sonarqube::ObjectifiedHash:46320 {hash: {"user"=>{"login"=>"login_name", "name"=>"name_user", "scmAccounts"=>[], "active"=>true, "local"=>true}}}
|
119
|
+
user.user.login
|
120
|
+
# login_name
|
121
|
+
user.user.name
|
122
|
+
# name_user
|
123
|
+
```
|
124
|
+
|
125
|
+
#### Delete User
|
126
|
+
```ruby
|
127
|
+
user = Sonarqube.user_delete('test')
|
128
|
+
# => #<Sonarqube::ObjectifiedHash:46220 {hash: {}}
|
129
|
+
user.to_hash.empty?
|
130
|
+
# => true
|
131
|
+
```
|
132
|
+
|
133
|
+
#### Search User
|
134
|
+
```ruby
|
135
|
+
users = Sonarqube.users_search()
|
136
|
+
# => #<Sonarqube::ObjectifiedHash:46340 {hash: {"paging"=>{"pageIndex"=>1, "pageSize"=>50, "total"=>5}, "users"=>[{"login"=>"admin", "name"=>"Administrator", "active"=>true, "groups"=>["sonar-administrators", "sonar-users"], "tokensCount"=>1, "local"=>true, "externalIdentity"=>"admin", "externalProvider"=>"sonarqube", "lastConnectionDate"=>"2020-08-22T23:09:14+0000"}, {"login"=>"new_user", "name"=>"key_new_user", "active"=>true, "groups"=>["sonar-users"], "tokensCount"=>0, "local"=>true, "externalIdentity"=>"new_user", "externalProvider"=>"sonarqube"}, {"login"=>"login_name", "name"=>"name_user", "active"=>true, "groups"=>["sonar-users"], "tokensCount"=>0, "local"=>true, "externalIdentity"=>"login_name", "externalProvider"=>"sonarqube"}, {"login"=>"test3", "name"=>"test QA", "active"=>true, "groups"=>["sonar-users"], "tokensCount"=>0, "local"=>true, "externalIdentity"=>"test3", "externalProvider"=>"sonarqube"}, {"login"=>"newlogin", "name"=>"test QA", "active"=>true, "groups"=>["sonar-users"], "tokensCount"=>0, "local"=>true, "externalIdentity"=>"newlogin", "externalProvider"=>"sonarqube"}]}}
|
137
|
+
users.users.each do | user |
|
138
|
+
puts "name: #{user.name}"
|
139
|
+
puts "login: #{user.login}"
|
140
|
+
puts "lastConection: #{user.lastConnectionDate}" if defined? user.lastConnectionDate
|
141
|
+
end
|
142
|
+
# name: Administrator
|
143
|
+
# login: admin
|
144
|
+
# lastConection: 2020-08-22T23:09:14+0000
|
145
|
+
# name: key_new_user
|
146
|
+
# login: new_user
|
147
|
+
# name: name_user
|
148
|
+
# login: login_name
|
149
|
+
# name: test QA
|
150
|
+
# login: test3
|
151
|
+
# name: test QA
|
152
|
+
# login: newlogin
|
153
|
+
```
|
154
|
+
|
155
|
+
### Groups
|
156
|
+
|
157
|
+
#### Create Group
|
158
|
+
```ruby
|
159
|
+
group = Sonarqube.create_group('New-Group', {description: 'Sonarqube group users'})
|
160
|
+
# => #<Sonarqube::ObjectifiedHash:46500 {hash: {"group"=>{"uuid"=>"AXQYrrgCsrvdoo0YodNM", "organization"=>"default-organization", "name"=>"New-Group", "description"=>"Sonarqube group users", "membersCount"=>0, "default"=>false}}}
|
161
|
+
group.group.uuid
|
162
|
+
# AXQYrrgCsrvdoo0YodNM
|
163
|
+
group.group.description
|
164
|
+
# Sonarqube group users
|
165
|
+
```
|
166
|
+
|
167
|
+
#### Delete Group
|
168
|
+
```ruby
|
169
|
+
group = Sonarqube.group_delete('New-Group')
|
170
|
+
# => #<Sonarqube::ObjectifiedHash:46220 {hash: {}}
|
171
|
+
group.to_hash.empty?
|
172
|
+
# => true
|
173
|
+
```
|
174
|
+
|
175
|
+
#### Search Group
|
176
|
+
```ruby
|
177
|
+
groups = Sonarqube.search_groups({ q: 'sonar-users' })
|
178
|
+
# => #<Sonarqube::ObjectifiedHash:46520 {hash: {"paging"=>{"pageIndex"=>1, "pageSize"=>100, "total"=>1}, "groups"=>[{"uuid"=>"AXOt93S3gMZPhbn-E_O7", "name"=>"sonar-users", "description"=>"Any new users created will automatically join this group", "membersCount"=>5, "default"=>true}]}}
|
179
|
+
groups.groups.each do | group |
|
180
|
+
puts "name: #{group.name}"
|
181
|
+
puts "login: #{group.description}"
|
182
|
+
puts "membersCount: #{group.membersCount}"
|
183
|
+
end
|
184
|
+
# name: sonar-users
|
185
|
+
# description: Any new users created will automatically join this group
|
186
|
+
# MembersCount: 5
|
187
|
+
```
|
188
|
+
|
189
|
+
For more information, refer to [documentation](https://www.rubydoc.info/gems/sonarqube/frames).
|
190
|
+
|
191
|
+
## Development
|
192
|
+
|
193
|
+
### With a dockerized Sonarqube instance
|
194
|
+
|
195
|
+
```shell
|
196
|
+
docker-compose up -d sonarqube # Will start the Sonarqube instance in the background (approx. 3 minutes)
|
197
|
+
```
|
198
|
+
|
199
|
+
After a while, your Sonarqube instance will be accessible on http://localhost:9000.
|
200
|
+
|
201
|
+
You can login with the admin/admin user/password.
|
202
|
+
|
203
|
+
You can now setup a personal access token here: http://localhost:9000
|
204
|
+
|
205
|
+
Once you have your token, set the variables to the correct values in the `docker.env` file.
|
206
|
+
|
207
|
+
Then, launch the tool:
|
208
|
+
|
209
|
+
```shell
|
210
|
+
docker-compose run app
|
211
|
+
```
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
Sonarqube.users
|
215
|
+
=> [#<Sonarqube::ObjectifiedHash:47231290771040 {hash: {"id"=>1, "name"=>"Administrator", "username"=>"root", ...]
|
216
|
+
```
|
217
|
+
|
218
|
+
To launch the specs:
|
219
|
+
|
220
|
+
```shell
|
221
|
+
docker-compose run app rake spec
|
222
|
+
```
|
223
|
+
|
224
|
+
|
225
|
+
### With an external Sonarqube instance
|
226
|
+
|
227
|
+
First, set the variables to the correct values in the `docker.env` file.
|
228
|
+
|
229
|
+
Then, launch the tool:
|
230
|
+
|
231
|
+
```shell
|
232
|
+
docker-compose run app
|
233
|
+
```
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
Sonarqube.users
|
237
|
+
=> [#<Sonarqube::ObjectifiedHash:47231290771040 {hash: {"id"=>1, "name"=>"Administrator", "username"=>"root", ...]
|
238
|
+
```
|
239
|
+
|
240
|
+
To launch the specs,
|
241
|
+
|
242
|
+
```shell
|
243
|
+
docker-compose run app rake spec
|
244
|
+
```
|
245
|
+
|
246
|
+
For more information see [CONTRIBUTING.md](https://github.com/psyreactor/sonarqube/blob/master/CONTRIBUTING.md).
|
247
|
+
|
248
|
+
## License
|
249
|
+
|
250
|
+
Released under the BSD 2-clause license. See LICENSE.txt for details.
|
data/lib/sonarqube.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sonarqube/version'
|
4
|
+
require 'sonarqube/objectified_hash'
|
5
|
+
require 'sonarqube/configuration'
|
6
|
+
require 'sonarqube/error'
|
7
|
+
require 'sonarqube/request'
|
8
|
+
require 'sonarqube/api'
|
9
|
+
require 'sonarqube/client'
|
10
|
+
|
11
|
+
module Sonarqube
|
12
|
+
extend Configuration
|
13
|
+
|
14
|
+
# Alias for Sonarqube::Client.new
|
15
|
+
#
|
16
|
+
# @return [Sonarqube::Client]
|
17
|
+
def self.client(options = {})
|
18
|
+
Sonarqube::Client.new(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Delegate to Sonarqube::Client
|
22
|
+
def self.method_missing(method, *args, &block)
|
23
|
+
return super unless client.respond_to?(method)
|
24
|
+
|
25
|
+
client.send(method, *args, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Delegate to Sonarqube::Client
|
29
|
+
def self.respond_to_missing?(method_name, include_private = false)
|
30
|
+
client.respond_to?(method_name) || super
|
31
|
+
end
|
32
|
+
|
33
|
+
# Delegate to HTTParty.http_proxy
|
34
|
+
def self.http_proxy(address = nil, port = nil, username = nil, password = nil)
|
35
|
+
Sonarqube::Request.http_proxy(address, port, username, password)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns an unsorted array of available client methods.
|
39
|
+
#
|
40
|
+
# @return [Array<Symbol>]
|
41
|
+
def self.actions
|
42
|
+
hidden =
|
43
|
+
/endpoint|private_token|auth_token|user_agent|get|post|put|\Adelete\z|validate\z|request_defaults|httparty/
|
44
|
+
(Sonarqube::Client.instance_methods - Object.methods).reject { |e| e[hidden] }
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sonarqube
|
4
|
+
# @private
|
5
|
+
class API < Request
|
6
|
+
# @private
|
7
|
+
attr_accessor(*Configuration::VALID_OPTIONS_KEYS)
|
8
|
+
# @private
|
9
|
+
alias auth_token= private_token=
|
10
|
+
|
11
|
+
# Creates a new API.
|
12
|
+
# @raise [Error:MissingCredentials]
|
13
|
+
def initialize(options = {})
|
14
|
+
options = Sonarqube.options.merge(options)
|
15
|
+
(Configuration::VALID_OPTIONS_KEYS + [:auth_token]).each do |key|
|
16
|
+
send("#{key}=", options[key]) if options[key]
|
17
|
+
end
|
18
|
+
request_defaults
|
19
|
+
self.class.headers 'User-Agent' => user_agent
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sonarqube
|
4
|
+
# Wrapper for the Sonarqube REST API.
|
5
|
+
class Client < API
|
6
|
+
Dir[File.expand_path('client/*.rb', __dir__)].each { |f| require f }
|
7
|
+
|
8
|
+
# Please keep in alphabetical order
|
9
|
+
include Groups
|
10
|
+
include Projects
|
11
|
+
include Users
|
12
|
+
|
13
|
+
# Text representation of the client, masking private token.
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
def inspect
|
17
|
+
inspected = super
|
18
|
+
inspected.sub! @private_token, only_show_last_four_chars(@private_token) if @private_token
|
19
|
+
inspected
|
20
|
+
end
|
21
|
+
|
22
|
+
# Utility method for URL encoding of a string.
|
23
|
+
# Copied from https://ruby-doc.org/stdlib-2.7.0/libdoc/erb/rdoc/ERB/Util.html
|
24
|
+
#
|
25
|
+
# @return [String]
|
26
|
+
def url_encode(url)
|
27
|
+
url.to_s.b.gsub(/[^a-zA-Z0-9_\-.~]/n) { |m| sprintf('%%%02X', m.unpack1('C')) } # rubocop:disable Style/FormatString, Style/FormatStringToken
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def only_show_last_four_chars(token)
|
33
|
+
"#{'*' * (token.size - 4)}#{token[-4..-1]}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Sonarqube::Client
|
4
|
+
# Defines methods related to groups.
|
5
|
+
# @see https://SONAR_URL/web_api/api/user_groups
|
6
|
+
module Groups
|
7
|
+
# Search for user groups.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# Sonarqube.groups_search
|
11
|
+
# Sonarqube.groups_search({ ps: 3, p: 2 })
|
12
|
+
# Sonarqube.groups_search({ ps: 3, p: 2 , q: sonar-users})
|
13
|
+
#
|
14
|
+
# @param [Hash] options A customizable set of options.
|
15
|
+
# @option options [String] :f Comma-separated list of the fields to be returned in response. All the fields are returned by default.
|
16
|
+
# @option options [Integer] :ps Page size number of projects to return per page
|
17
|
+
# @option options [Integer] :p The page to retrieve
|
18
|
+
# @option options [String] :q Limit search to names that contain the supplied string.
|
19
|
+
# (Any provided options will be passed to Sonarqube. See {https://SONAR_URL/web_api/api/user_groups/search}
|
20
|
+
# @return [Array<Sonarqube::ObjectifiedHash>]
|
21
|
+
def search_groups(options = {})
|
22
|
+
get('/api/user_groups/search', query: options)
|
23
|
+
end
|
24
|
+
alias groups_search search_groups
|
25
|
+
|
26
|
+
# Creates a new group.
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# Sonarqube.create_group('new-group')
|
30
|
+
# Sonarqube.create_group('sonarqube', { description: 'New Sonarqube project' })
|
31
|
+
#
|
32
|
+
# @param [String] name(required) The name of a group.
|
33
|
+
# @param [Hash] options A customizable set of options.
|
34
|
+
# @option options [String] :description Description for the new group. A group description cannot be larger than 200 characters.
|
35
|
+
# @return [Sonarqube::ObjectifiedHash] Information about created group.
|
36
|
+
def create_group(name, options = {})
|
37
|
+
body = { name: name }.merge(options)
|
38
|
+
post('/api/user_groups/create', body: body)
|
39
|
+
end
|
40
|
+
alias group_create create_group
|
41
|
+
|
42
|
+
# Delete's a group.
|
43
|
+
#
|
44
|
+
# @example
|
45
|
+
# Sonarqube.delete_group('projecto')
|
46
|
+
#
|
47
|
+
# @param [String] name(required) The name of a group
|
48
|
+
# @return [Sonarqube::ObjectifiedHash] Empty hash response.
|
49
|
+
def delete_group(name)
|
50
|
+
post('/api/user_groups/delete', body: { name: name })
|
51
|
+
end
|
52
|
+
alias group_delete delete_group
|
53
|
+
|
54
|
+
# Update group.
|
55
|
+
#
|
56
|
+
# @example
|
57
|
+
# Sonarqube.group_members('AXQRcKrW9pRiZzanEJ2E')
|
58
|
+
# Sonarqube.group_members('AXQRcKrW9pRiZzanEJ2E, { description: 'update group description })
|
59
|
+
#
|
60
|
+
# @param [String] id(required) The ID of a group.
|
61
|
+
# @param [Hash] options A customizable set of options.
|
62
|
+
# @option options [String] :description New optional description for the group. A group description cannot be larger than 200 characters. If value is not defined, then description is not changed.
|
63
|
+
# @option options [String] :name New optional name for the group. A group name cannot be larger than 255 characters and must be unique. Value 'anyone' (whatever the case) is reserved and cannot be used. If value is empty or not defined, then name is not changed.
|
64
|
+
# @return [Array<Sonarqube::ObjectifiedHash>]
|
65
|
+
def update_group(id, options = {})
|
66
|
+
post('/api/user_groups/update', body: { id: id }.merge!(options))
|
67
|
+
end
|
68
|
+
alias group_update update_group
|
69
|
+
|
70
|
+
# Add a user to a group.
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# Sonarqube.add_member('AXQRcKrW9pRiZzanEJ2E', 'test-user')
|
74
|
+
# Sonarqube.add_member('AXQRcKrW9pRiZzanEJ2E', 'test-user', {name: 'sonar-groups'})
|
75
|
+
#
|
76
|
+
# @param [String] id(required) The id of group.
|
77
|
+
# @param [String] login(required) The login of user.
|
78
|
+
# @param [Hash] options A customizable set of options.
|
79
|
+
# @option options [String] :name Optional name of group.
|
80
|
+
# @return [Sonarqube::ObjectifiedHash]
|
81
|
+
def add_member(id = nil, login = nil, options = {})
|
82
|
+
raise ArgumentError, 'Missing required parameters' if id.nil? || login.nil?
|
83
|
+
|
84
|
+
post('/api/user_groups/add_user', body: { id: id, login: login }.merge!(options))
|
85
|
+
end
|
86
|
+
alias member_add add_member
|
87
|
+
|
88
|
+
# Reomve a user to a group.
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
# Sonarqube.remove_member('AXQRcKrW9pRiZzanEJ2E', 'test-user')
|
92
|
+
# Sonarqube.remove_member('AXQRcKrW9pRiZzanEJ2E', 'test-user', {name: 'sonar-groups'})
|
93
|
+
#
|
94
|
+
# @param [String] id(required) The id of group.
|
95
|
+
# @param [String] login(required) The login of user.
|
96
|
+
# @param [Hash] options A customizable set of options.
|
97
|
+
# @option options [String] :name Optional name of group.
|
98
|
+
# @return [Sonarqube::ObjectifiedHash]
|
99
|
+
def remove_member(id = nil, login = nil, options = {})
|
100
|
+
raise ArgumentError, 'Missing required parameters' if id.nil? || login.nil?
|
101
|
+
|
102
|
+
post('/api/user_groups/remove_user', body: { id: id, login: login }.merge!(options))
|
103
|
+
end
|
104
|
+
alias member_remove remove_member
|
105
|
+
|
106
|
+
# List members of group.
|
107
|
+
#
|
108
|
+
# @example
|
109
|
+
# Sonarqube.list_members({id: 'AXQRcKrW9pRiZzanEJ2E'})
|
110
|
+
# Sonarqube.list_members({name: 'sonar-groups'})
|
111
|
+
#
|
112
|
+
# @param [Hash] options A customizable set of options.
|
113
|
+
# @option options [String] :name(required) Name of group.
|
114
|
+
# @option options [String] :id(required) Id of group.
|
115
|
+
# @return [Sonarqube::ObjectifiedHash] Information about added team member.
|
116
|
+
def list_members(options = {})
|
117
|
+
raise ArgumentError, 'Missing required parameters' if options[:id].nil? && options[:name].nil?
|
118
|
+
|
119
|
+
get('/api/user_groups/users', query: options)
|
120
|
+
end
|
121
|
+
|
122
|
+
alias members_list list_members
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Sonarqube::Client
|
4
|
+
# Defines methods related to projects.
|
5
|
+
# @see https://SONAR_URL/web_api/api/projects
|
6
|
+
module Projects
|
7
|
+
# Search for projects by name.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# Sonarqube.project_search()
|
11
|
+
# Sonarqube.project_search({ p: 2 })
|
12
|
+
# Sonarqube.search_projects({ ps: 42, p: 5 })
|
13
|
+
#
|
14
|
+
# @param [Hash] options A customizable set of options.
|
15
|
+
# @option options [String] :analyzedBefore Filter the projects for which last analysis is older than the given date
|
16
|
+
# @option options [Boolean] :onProvisionedOnly Filter the projects that are provisioned
|
17
|
+
# @option options [Integer] :ps Page size number of projects to return per page
|
18
|
+
# @option options [Integer] :p The page to retrieve
|
19
|
+
# @option options [String] :qualifiers Filter the results with the specified qualifiers (TRK,VW,APP)
|
20
|
+
# @option options [String] :q Limit search to component names that contain the supplied string or component keys that contain the supplied string
|
21
|
+
# @option options [String] :projects Comma-separated list of project keys
|
22
|
+
# (Any provided options will be passed to Sonarqube. See {https://SONAR_URL/web_api/api/projects/search}
|
23
|
+
# @return [Array<Sonarqube::ObjectifiedHash>]
|
24
|
+
def projects_search(options = {})
|
25
|
+
get('/api/projects/search', query: options)
|
26
|
+
end
|
27
|
+
alias search_projects projects_search
|
28
|
+
|
29
|
+
# Creates a new project.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# Sonarqube.create_project('sonarqube','sonarqube)
|
33
|
+
# Sonarqube.create_project('viking', 'ragnar' { visibility: 'private' })
|
34
|
+
#
|
35
|
+
# @param [String] name The name of a project.
|
36
|
+
# @param [String] key The key of a project.
|
37
|
+
# @param [Hash] options A customizable set of options.
|
38
|
+
# @option options [String] :visibility Visibility of a project (public or private).
|
39
|
+
# @return [Sonarqube::ObjectifiedHash] Information about created project.
|
40
|
+
def create_project(name, key = nil, options = {})
|
41
|
+
key = name if key.nil?
|
42
|
+
post('/api/projects/create', body: { name: name, project: key }.merge(options))
|
43
|
+
end
|
44
|
+
|
45
|
+
alias project_create create_project
|
46
|
+
# Deletes a project.
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# Sonarqube.delete_project(4)
|
50
|
+
#
|
51
|
+
# @param [String] key The key of project.
|
52
|
+
# @return [Sonarqube::ObjectifiedHash] Information about deleted project.
|
53
|
+
def delete_project(key)
|
54
|
+
post('/api/projects/delete', body: { project: key })
|
55
|
+
end
|
56
|
+
|
57
|
+
alias project_delete delete_project
|
58
|
+
# Gets a list of project hooks.
|
59
|
+
#
|
60
|
+
# @example
|
61
|
+
# Sonarqube.project_update_key(42)
|
62
|
+
# Sonarqube.project_update_key('sonarqube')
|
63
|
+
#
|
64
|
+
# @param [String] key_ori The original key of a project.
|
65
|
+
# @param [String] key_new The New key of a project.
|
66
|
+
# @return [Array<Sonarqube::ObjectifiedHash>]
|
67
|
+
def project_update_key(key_ori, key_new)
|
68
|
+
post('/api/projects/update_key', body: { from: key_ori, to: key_new })
|
69
|
+
end
|
70
|
+
|
71
|
+
alias update_key_project project_update_key
|
72
|
+
# Gets a project hook.
|
73
|
+
#
|
74
|
+
# @example
|
75
|
+
# Sonarqube.project_hook(42, 5)
|
76
|
+
# Sonarqube.project_hook('sonarqube', 5)
|
77
|
+
#
|
78
|
+
# @param [String] project The name fo project.
|
79
|
+
# @param [String] visibility The visibility of a project.
|
80
|
+
# @return [Sonarqube::ObjectifiedHash]
|
81
|
+
def project_update_visibility(project, visibility)
|
82
|
+
post('/api/projects/update_visibility', body: { project: project, visibility: visibility })
|
83
|
+
end
|
84
|
+
|
85
|
+
alias update_visibility_project project_update_visibility
|
86
|
+
# Bulk delete projects.
|
87
|
+
#
|
88
|
+
# @example
|
89
|
+
# Sonarqube.project_bulk_delete()
|
90
|
+
# Sonarqube.project_bulk_delete({ p: 2 })
|
91
|
+
# Sonarqube.project_bulk_delete({ ps: 42, p: 5 })
|
92
|
+
#
|
93
|
+
# @param [Hash] options A customizable set of options.
|
94
|
+
# @option options [String] :analyzedBefore Filter the projects for which last analysis is older than the given date.
|
95
|
+
# @option options [Boolean] :onProvisionedOnly Filter the projects that are provisioned
|
96
|
+
# @option options [String] :qualifiers Filter the results with the specified qualifiers (TRK,VW,APP)
|
97
|
+
# @option options [String] :q Limit search to component names that contain the supplied string or component keys that contain the supplied string
|
98
|
+
# @option options [String] :projects Comma-separated list of project keys
|
99
|
+
# (Any provided options will be passed to Sonarqube. See {https://SONAR_URL/web_api/api/projects/bulk_delete}
|
100
|
+
# @return [Array<Sonarqube::ObjectifiedHash>]
|
101
|
+
def projects_bulk_delete(options = {})
|
102
|
+
if options[:analyzedBefore].nil? && options[:projects].nil? && options[:q].nil?
|
103
|
+
raise ArgumentError, 'Missing required parameters'
|
104
|
+
end
|
105
|
+
|
106
|
+
post('/api/projects/bulk_delete', body: options)
|
107
|
+
end
|
108
|
+
|
109
|
+
alias delete_bulk_projects projects_bulk_delete
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Sonarqube::Client
|
4
|
+
# Defines methods related to users.
|
5
|
+
# @see https://SONAR_URL/web_api/api/users
|
6
|
+
module Users
|
7
|
+
# Gets a list of users.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# Sonarqube.users_search()
|
11
|
+
# Sonarqube.users_search(query: { p: 1, ps: 10 })
|
12
|
+
# Sonarqube.users_search(query: { p: 1, ps: 10, q: 'sonarqube' })
|
13
|
+
#
|
14
|
+
# @param [Hash] options A customizable set of options.
|
15
|
+
# @option options [Integer] :ps Page size number of users to return per page
|
16
|
+
# @option options [Integer] :p The page to retrieve
|
17
|
+
# @option options [String] :q Filter on login, name and email
|
18
|
+
# @return [Array<Sonarqube::ObjectifiedHash>]
|
19
|
+
def users_search(options = {})
|
20
|
+
get('/api/users/search', query: options)
|
21
|
+
end
|
22
|
+
|
23
|
+
alias search_users users_search
|
24
|
+
# Creates a new user.
|
25
|
+
# Requires authentication from an admin account.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# Sonarqube.create_user('joe', 'joe', 'secret', , { mail: 'joe@foo.org' })
|
29
|
+
# or
|
30
|
+
# Sonarqube.create_user('joe', 'joe', 'secret')
|
31
|
+
#
|
32
|
+
# @param [String] name(required) The name of a user.
|
33
|
+
# @param [String] login(required) The login of a user.
|
34
|
+
# @param [String] password(required only is local user) The password of a user.
|
35
|
+
# @param [Hash] options A customizable set of options.
|
36
|
+
# @option options [String] :email The emails of a user.
|
37
|
+
# @option options [String] :local Specify if the user should be authenticated from SonarQube server or from an external authentication system. Password should not be set when local is set to false.
|
38
|
+
# @option options [String] :scmAccount List of SCM accounts. To set several values, the parameter must be called once for each value.
|
39
|
+
# @param [Hash] options A customizable set of options.
|
40
|
+
# @return [Sonarqube::ObjectifiedHash] Information about created user.
|
41
|
+
def create_user(login, name, password = nil, options = {})
|
42
|
+
body = { login: login, password: password, name: name }
|
43
|
+
body.merge!(options)
|
44
|
+
post('/api/users/create', body: body)
|
45
|
+
end
|
46
|
+
|
47
|
+
alias user_create create_user
|
48
|
+
# Updates a user.
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# Sonarqube.update_user('joe', { email: 'joe.smith@foo.org', name: 'Joe' })
|
52
|
+
#
|
53
|
+
# @param [String] login(required) The login of a user
|
54
|
+
# @param [Hash] options A customizable set of options.
|
55
|
+
# @option options [String] :email The email of a user.
|
56
|
+
# @option options [String] :name The name of a user.
|
57
|
+
# @option options [String] :scmAccount SCM accounts. To set several values, the parameter must be called once for each value.
|
58
|
+
# @param [Hash] options A customizable set of options.
|
59
|
+
# @return [Sonarqube::ObjectifiedHash] Information about update user.
|
60
|
+
def update_user(login, options = {})
|
61
|
+
post('/api/users/update', body: { login: login }.merge!(options))
|
62
|
+
end
|
63
|
+
|
64
|
+
alias user_update update_user
|
65
|
+
# Blocks the specified user. Available only for admin.
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# Sonarqube.block_user(15)
|
69
|
+
#
|
70
|
+
# @param [String] login(required) The login of a user
|
71
|
+
# @param [Hash] options A customizable set of options.
|
72
|
+
def deactivate_user(login, options = {})
|
73
|
+
post('/api/users/deactivate', body: { login: login }.merge!(options))
|
74
|
+
end
|
75
|
+
|
76
|
+
alias user_deactivate deactivate_user
|
77
|
+
# Change password the specified user. Available only for admin.
|
78
|
+
#
|
79
|
+
# @example
|
80
|
+
# Sonarqube.change_password_user('joe', 'password')
|
81
|
+
# Sonarqube.change_password_user('admin', 'password', admin)
|
82
|
+
#
|
83
|
+
# @param [String] login(required) The login of a user
|
84
|
+
# @param [String] password(required) New password for login
|
85
|
+
# @param [String] old_password(optional) Previous password. Required when changing one's own password.
|
86
|
+
# @param [Hash] options A customizable set of options.
|
87
|
+
def change_password_user(login, password, old_password = nil, options = {})
|
88
|
+
body = { login: login, password: password }
|
89
|
+
body = { old_password: old_password }.merge!(body) unless old_password.nil?
|
90
|
+
post('/api/users/change_password', body: body.merge!(options))
|
91
|
+
end
|
92
|
+
|
93
|
+
alias user_change_password change_password_user
|
94
|
+
# Creates a new user session.
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# Sonarqube.session('jack@example.com', 'secret12345')
|
98
|
+
#
|
99
|
+
# @param [String] login(required) The login of a user
|
100
|
+
# @param [String] new_login(required) The new login of a user
|
101
|
+
# @param [Hash] options A customizable set of options.
|
102
|
+
# @return [Sonarqube::ObjectifiedHash]
|
103
|
+
def update_login_user(login, new_login, options = {})
|
104
|
+
post('/api/users/update_login', body: { login: login, newLogin: new_login }.merge!(options))
|
105
|
+
end
|
106
|
+
|
107
|
+
alias user_update_login update_login_user
|
108
|
+
# Lists the groups a user belongs to.
|
109
|
+
#
|
110
|
+
# @example
|
111
|
+
# Sonarqube.group_user
|
112
|
+
#
|
113
|
+
# @param [String] login A customizable set of options.
|
114
|
+
# @param [Hash] options A customizable set of options.
|
115
|
+
# @option options [Integer] :page The page number.
|
116
|
+
# @option options [Integer] :per_page The number of results per page.
|
117
|
+
# @option options [String] :from The start date for paginated results.
|
118
|
+
# @return [Array<Sonarqube::ObjectifiedHash>]
|
119
|
+
def groups_user(login, options = {})
|
120
|
+
get('/api/users/groups', query: { login: login }.merge!(options))
|
121
|
+
end
|
122
|
+
|
123
|
+
alias user_groups groups_user
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sonarqube
|
4
|
+
# Defines constants and methods related to configuration.
|
5
|
+
module Configuration
|
6
|
+
# An array of valid keys in the options hash when configuring a Sonarqube::API.
|
7
|
+
VALID_OPTIONS_KEYS = %i[endpoint private_token user_agent httparty].freeze
|
8
|
+
|
9
|
+
# The user agent that will be sent to the API endpoint if none is set.
|
10
|
+
DEFAULT_USER_AGENT = "Sonarqube Ruby Gem #{Sonarqube::VERSION}"
|
11
|
+
|
12
|
+
# @private
|
13
|
+
attr_accessor(*VALID_OPTIONS_KEYS)
|
14
|
+
# @private
|
15
|
+
alias auth_token= private_token=
|
16
|
+
|
17
|
+
# Sets all configuration options to their default values
|
18
|
+
# when this module is extended.
|
19
|
+
def self.extended(base)
|
20
|
+
base.reset
|
21
|
+
end
|
22
|
+
|
23
|
+
# Convenience method to allow configuration options to be set in a block.
|
24
|
+
def configure
|
25
|
+
yield self
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a hash of options and their values.
|
29
|
+
def options
|
30
|
+
VALID_OPTIONS_KEYS.inject({}) do |option, key|
|
31
|
+
option.merge!(key => send(key))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Resets all configuration options to the defaults.
|
36
|
+
def reset
|
37
|
+
self.endpoint = ENV['SONARQUBE_API_ENDPOINT']
|
38
|
+
self.private_token = ENV['SONARQUBE_API_PRIVATE_TOKEN']
|
39
|
+
self.httparty = get_httparty_config(ENV['SONARQUBE_API_HTTPARTY_OPTIONS'])
|
40
|
+
self.user_agent = DEFAULT_USER_AGENT
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Allows HTTParty config to be specified in ENV using YAML hash.
|
46
|
+
def get_httparty_config(options)
|
47
|
+
return if options.nil?
|
48
|
+
|
49
|
+
httparty = Sonarqube::CLI::Helpers.yaml_load(options)
|
50
|
+
raise ArgumentError, 'HTTParty config should be a Hash.' unless httparty.is_a? Hash
|
51
|
+
|
52
|
+
Sonarqube::CLI::Helpers.symbolize_keys httparty
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sonarqube
|
4
|
+
module Error
|
5
|
+
# Custom error class for rescuing from all Sonarqube errors.
|
6
|
+
class Error < StandardError; end
|
7
|
+
|
8
|
+
# Raised when API endpoint credentials not configured.
|
9
|
+
class MissingCredentials < Error; end
|
10
|
+
|
11
|
+
# Raised when impossible to parse response body.
|
12
|
+
class Parsing < Error; end
|
13
|
+
|
14
|
+
# Custom error class for rescuing from HTTP response errors.
|
15
|
+
class ResponseError < Error
|
16
|
+
POSSIBLE_MESSAGE_KEYS = %i[message error_description error].freeze
|
17
|
+
|
18
|
+
def initialize(response)
|
19
|
+
@response = response
|
20
|
+
super(build_error_message)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Status code returned in the HTTP response.
|
24
|
+
#
|
25
|
+
# @return [Integer]
|
26
|
+
def response_status
|
27
|
+
@response.code
|
28
|
+
end
|
29
|
+
|
30
|
+
# Body content returned in the HTTP response
|
31
|
+
#
|
32
|
+
# @return [String]
|
33
|
+
def response_message
|
34
|
+
@response.parsed_response.message
|
35
|
+
end
|
36
|
+
|
37
|
+
# Additional error context returned by some API endpoints
|
38
|
+
#
|
39
|
+
# @return [String]
|
40
|
+
def error_code
|
41
|
+
if @response.respond_to?(:error_code)
|
42
|
+
@response.error_code
|
43
|
+
else
|
44
|
+
''
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Human friendly message.
|
51
|
+
#
|
52
|
+
# @return [String]
|
53
|
+
def build_error_message
|
54
|
+
parsed_response = classified_response
|
55
|
+
message = check_error_keys(parsed_response)
|
56
|
+
"Server responded with code #{@response.code}, message: " \
|
57
|
+
"#{handle_message(message)}. " \
|
58
|
+
"Request URI: #{@response.request.base_uri}#{@response.request.path}"
|
59
|
+
end
|
60
|
+
|
61
|
+
# Error keys vary across the API, find the first key that the parsed_response
|
62
|
+
# object responds to and return that, otherwise return the original.
|
63
|
+
def check_error_keys(resp)
|
64
|
+
key = POSSIBLE_MESSAGE_KEYS.find { |k| resp.respond_to?(k) }
|
65
|
+
key ? resp.send(key) : resp
|
66
|
+
end
|
67
|
+
|
68
|
+
# Parse the body based on the classification of the body content type
|
69
|
+
#
|
70
|
+
# @return parsed response
|
71
|
+
def classified_response
|
72
|
+
if @response.respond_to?('headers')
|
73
|
+
@response.headers['content-type'] == 'text/plain' ? { message: @response.to_s } : @response.parsed_response
|
74
|
+
else
|
75
|
+
@response.parsed_response
|
76
|
+
end
|
77
|
+
rescue Sonarqube::Error::Parsing
|
78
|
+
# Return stringified response when receiving a
|
79
|
+
# parsing error to avoid obfuscation of the
|
80
|
+
# api error.
|
81
|
+
#
|
82
|
+
# note: The Sonarqube API does not always return valid
|
83
|
+
# JSON when there are errors.
|
84
|
+
@response.to_s
|
85
|
+
end
|
86
|
+
|
87
|
+
# Handle error response message in case of nested hashes
|
88
|
+
def handle_message(message)
|
89
|
+
case message
|
90
|
+
when Sonarqube::ObjectifiedHash
|
91
|
+
message.to_h.sort.map do |key, val|
|
92
|
+
"'#{key}' #{(val.is_a?(Hash) ? val.sort.map { |k, v| "(#{k}: #{v.join(' ')})" } : [val].flatten).join(' ')}"
|
93
|
+
end.join(', ')
|
94
|
+
when Array
|
95
|
+
message.join(' ')
|
96
|
+
else
|
97
|
+
message
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Raised when API endpoint returns the HTTP status code 400.
|
103
|
+
class BadRequest < ResponseError; end
|
104
|
+
|
105
|
+
# Raised when API endpoint returns the HTTP status code 401.
|
106
|
+
class Unauthorized < ResponseError; end
|
107
|
+
|
108
|
+
# Raised when API endpoint returns the HTTP status code 403.
|
109
|
+
class Forbidden < ResponseError; end
|
110
|
+
|
111
|
+
# Raised when API endpoint returns the HTTP status code 404.
|
112
|
+
class NotFound < ResponseError; end
|
113
|
+
|
114
|
+
# Raised when API endpoint returns the HTTP status code 405.
|
115
|
+
class MethodNotAllowed < ResponseError; end
|
116
|
+
|
117
|
+
# Raised when API endpoint returns the HTTP status code 406.
|
118
|
+
class NotAcceptable < ResponseError; end
|
119
|
+
|
120
|
+
# Raised when API endpoint returns the HTTP status code 409.
|
121
|
+
class Conflict < ResponseError; end
|
122
|
+
|
123
|
+
# Raised when API endpoint returns the HTTP status code 422.
|
124
|
+
class Unprocessable < ResponseError; end
|
125
|
+
|
126
|
+
# Raised when API endpoint returns the HTTP status code 429.
|
127
|
+
class TooManyRequests < ResponseError; end
|
128
|
+
|
129
|
+
# Raised when API endpoint returns the HTTP status code 500.
|
130
|
+
class InternalServerError < ResponseError; end
|
131
|
+
|
132
|
+
# Raised when API endpoint returns the HTTP status code 502.
|
133
|
+
class BadGateway < ResponseError; end
|
134
|
+
|
135
|
+
# Raised when API endpoint returns the HTTP status code 503.
|
136
|
+
class ServiceUnavailable < ResponseError; end
|
137
|
+
|
138
|
+
# HTTP status codes mapped to error classes.
|
139
|
+
STATUS_MAPPINGS = {
|
140
|
+
400 => BadRequest,
|
141
|
+
401 => Unauthorized,
|
142
|
+
403 => Forbidden,
|
143
|
+
404 => NotFound,
|
144
|
+
405 => MethodNotAllowed,
|
145
|
+
406 => NotAcceptable,
|
146
|
+
409 => Conflict,
|
147
|
+
422 => Unprocessable,
|
148
|
+
429 => TooManyRequests,
|
149
|
+
500 => InternalServerError,
|
150
|
+
502 => BadGateway,
|
151
|
+
503 => ServiceUnavailable
|
152
|
+
}.freeze
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sonarqube
|
4
|
+
# Converts hashes to the objects.
|
5
|
+
class ObjectifiedHash
|
6
|
+
# Creates a new ObjectifiedHash object.
|
7
|
+
def initialize(hash)
|
8
|
+
@hash = hash
|
9
|
+
@data = hash.each_with_object({}) do |(key, value), data|
|
10
|
+
value = self.class.new(value) if value.is_a? Hash
|
11
|
+
value = value.map { |v| v.is_a?(Hash) ? self.class.new(v) : v } if value.is_a? Array
|
12
|
+
data[key.to_s] = value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Hash] The original hash.
|
17
|
+
def to_hash
|
18
|
+
hash
|
19
|
+
end
|
20
|
+
alias to_h to_hash
|
21
|
+
|
22
|
+
# @return [String] Formatted string with the class name, object id and original hash.
|
23
|
+
def inspect
|
24
|
+
"#<#{self.class}:#{object_id} {hash: #{hash.inspect}}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](key)
|
28
|
+
data[key]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :hash, :data
|
34
|
+
|
35
|
+
# Respond to messages for which `self.data` has a key
|
36
|
+
def method_missing(method_name, *args, &block)
|
37
|
+
if data.key?(method_name.to_s)
|
38
|
+
data[method_name.to_s]
|
39
|
+
elsif data.respond_to?(method_name)
|
40
|
+
warn 'WARNING: Please convert ObjectifiedHash object to hash before calling Hash methods on it.'
|
41
|
+
data.send(method_name, *args, &block)
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def respond_to_missing?(method_name, include_private = false)
|
48
|
+
hash.keys.map(&:to_sym).include?(method_name.to_sym) || super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'httparty'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Sonarqube
|
7
|
+
# @private
|
8
|
+
class Request
|
9
|
+
include HTTParty
|
10
|
+
format :json
|
11
|
+
headers 'Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded'
|
12
|
+
parser(proc { |body, _| parse(body) })
|
13
|
+
|
14
|
+
attr_accessor :private_token, :endpoint
|
15
|
+
|
16
|
+
# Converts the response body to an ObjectifiedHash.
|
17
|
+
def self.parse(body)
|
18
|
+
body = decode(body)
|
19
|
+
|
20
|
+
if body.is_a? Hash
|
21
|
+
ObjectifiedHash.new body
|
22
|
+
elsif body
|
23
|
+
true
|
24
|
+
elsif !body
|
25
|
+
false
|
26
|
+
elsif body.nil?
|
27
|
+
false
|
28
|
+
else
|
29
|
+
raise Error::Parsing, "Couldn't parse a response body"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Decodes a JSON response into Ruby object.
|
34
|
+
def self.decode(response)
|
35
|
+
response ? JSON.load(response) : {}
|
36
|
+
rescue JSON::ParserError
|
37
|
+
raise Error::Parsing, 'The response is not a valid JSON'
|
38
|
+
end
|
39
|
+
|
40
|
+
%w[get post put delete].each do |method|
|
41
|
+
define_method method do |path, options = {}|
|
42
|
+
params = options.dup
|
43
|
+
|
44
|
+
httparty_config(params)
|
45
|
+
|
46
|
+
unless params[:unauthenticated]
|
47
|
+
params[:headers] ||= {}
|
48
|
+
params[:headers].merge!(authorization_header)
|
49
|
+
end
|
50
|
+
|
51
|
+
validate self.class.send(method, @endpoint + path, params)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Checks the response code for common errors.
|
56
|
+
# Returns parsed response for successful requests.
|
57
|
+
def validate(response)
|
58
|
+
error_klass = Error::STATUS_MAPPINGS[response.code]
|
59
|
+
raise error_klass, response if error_klass
|
60
|
+
|
61
|
+
parsed = response.parsed_response
|
62
|
+
parsed.client = self if parsed.respond_to?(:client=)
|
63
|
+
parsed.parse_headers!(response.headers) if parsed.respond_to?(:parse_headers!)
|
64
|
+
parsed
|
65
|
+
end
|
66
|
+
|
67
|
+
# Sets a base_uri and default_params for requests.
|
68
|
+
# @raise [Error::MissingCredentials] if endpoint not set.
|
69
|
+
def request_defaults
|
70
|
+
raise Error::MissingCredentials, 'Please set an endpoint to API' unless @endpoint
|
71
|
+
|
72
|
+
self.class.default_params
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# Returns an Authorization header hash
|
78
|
+
#
|
79
|
+
# @raise [Error::MissingCredentials] if private_token and auth_token are not set.
|
80
|
+
def authorization_header
|
81
|
+
raise Error::MissingCredentials, 'Please provide a private_token for user' unless @private_token
|
82
|
+
|
83
|
+
{ 'Authorization' => "Basic #{Base64.encode64("#{private_token}:")}" }
|
84
|
+
end
|
85
|
+
|
86
|
+
# Set HTTParty configuration
|
87
|
+
# @see https://github.com/jnunemaker/httparty
|
88
|
+
def httparty_config(options)
|
89
|
+
options.merge!(httparty) if httparty
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sonarqube
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mariani Lucas
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-08-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.14'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.14.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.14'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.14.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: terminal-table
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.5'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.5.1
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '1.5'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.5.1
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rake
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
type: :development
|
61
|
+
prerelease: false
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: rspec
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
type: :development
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: webmock
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
description: Ruby client for Sonarqube API
|
96
|
+
email:
|
97
|
+
- marianilucas@gmail.com
|
98
|
+
executables: []
|
99
|
+
extensions: []
|
100
|
+
extra_rdoc_files: []
|
101
|
+
files:
|
102
|
+
- CHANGELOG.md
|
103
|
+
- LICENSE.txt
|
104
|
+
- README.md
|
105
|
+
- lib/sonarqube.rb
|
106
|
+
- lib/sonarqube/api.rb
|
107
|
+
- lib/sonarqube/client.rb
|
108
|
+
- lib/sonarqube/client/groups.rb
|
109
|
+
- lib/sonarqube/client/projects.rb
|
110
|
+
- lib/sonarqube/client/users.rb
|
111
|
+
- lib/sonarqube/configuration.rb
|
112
|
+
- lib/sonarqube/error.rb
|
113
|
+
- lib/sonarqube/objectified_hash.rb
|
114
|
+
- lib/sonarqube/request.rb
|
115
|
+
- lib/sonarqube/version.rb
|
116
|
+
homepage: https://github.com/psyreactor/sonarqube-ruby
|
117
|
+
licenses:
|
118
|
+
- BSD-2-Clause
|
119
|
+
metadata: {}
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '2.5'
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubygems_version: 3.1.2
|
136
|
+
signing_key:
|
137
|
+
specification_version: 4
|
138
|
+
summary: A Ruby wrapper for the Sonarqube API
|
139
|
+
test_files: []
|