gitlab-triage 1.11.0 → 1.14.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitlab/CODEOWNERS +1 -1
- data/.gitlab/merge_request_templates/Release.md +1 -1
- data/README.md +40 -0
- data/gitlab-triage.gemspec +2 -0
- data/lib/gitlab/triage/api_query_builders/base_query_param_builder.rb +11 -2
- data/lib/gitlab/triage/api_query_builders/multi_query_param_builder.rb +10 -2
- data/lib/gitlab/triage/engine.rb +53 -2
- data/lib/gitlab/triage/filters/discussions_conditions_filter.rb +60 -0
- data/lib/gitlab/triage/graphql_network.rb +54 -0
- data/lib/gitlab/triage/graphql_queries/query_builder.rb +47 -0
- data/lib/gitlab/triage/graphql_queries/threads_query.rb +31 -0
- data/lib/gitlab/triage/graphql_queries/user_notes_query.rb +23 -0
- data/lib/gitlab/triage/network_adapters/graphql_adapter.rb +69 -0
- data/lib/gitlab/triage/policies_resources/rule_resources.rb +2 -11
- data/lib/gitlab/triage/policies_resources/summary_resources.rb +2 -11
- data/lib/gitlab/triage/url_builders/url_builder.rb +7 -3
- data/lib/gitlab/triage/validators/params_validator.rb +1 -1
- data/lib/gitlab/triage/version.rb +1 -1
- metadata +37 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c40a53d7edad2e8472cb3a7a503af2d45d31dfb4b7192e12b170016cd47b0b23
|
4
|
+
data.tar.gz: 35c4cfc534faee10e6d1d02b6108d3300ad4cee20883e6e1299d97c5315a0cde
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 27424e4cab78096d186c11b9508340b50f70b04fdf9ee942bc1063dd3e4a0b14e4f10ebe7651947f3e32bd891cff98eb33db2315cd021aaef86e1f38e3dc9552
|
7
|
+
data.tar.gz: 3c4e60ac07b46c9c755d8b85c39fed6addc39370b268885090e7cf822456877e1f3bf241adf7288c5f5df6cda802284ca05cfa8c91d79d92dc04086c36d8a182
|
data/.gitlab/CODEOWNERS
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# The official maintainers
|
2
|
-
* @rymai @godfat @markglenfletcher
|
2
|
+
* @rymai @godfat-gitlab @markglenfletcher
|
@@ -32,4 +32,4 @@ with the latest commit from https://gitlab.com/gitlab-org/gitlab-triage/commits/
|
|
32
32
|
- Checklist after merging:
|
33
33
|
- [ ] [Update the release notes for the newly created tag](docs/release_process.md#how-to).
|
34
34
|
|
35
|
-
/label ~"Engineering Productivity" ~"ep::triage" ~"tooling::workflow"
|
35
|
+
/label ~"Engineering Productivity" ~"ep::triage" ~tooling ~"tooling::workflow"
|
data/README.md
CHANGED
@@ -141,6 +141,8 @@ Available condition types:
|
|
141
141
|
- [`assignee_member` condition](#assignee-member-condition)
|
142
142
|
- [`source_branch` condition](#source-branch-condition)
|
143
143
|
- [`target_branch` condition](#target-branch-condition)
|
144
|
+
- [`weight` condition](#weight-condition)
|
145
|
+
- [`discussions` condition](#discussions-condition)
|
144
146
|
- [`ruby` condition](#ruby-condition)
|
145
147
|
|
146
148
|
##### Date condition
|
@@ -467,6 +469,44 @@ conditions:
|
|
467
469
|
target_branch: 'master'
|
468
470
|
```
|
469
471
|
|
472
|
+
##### Weight condition
|
473
|
+
|
474
|
+
Accepts a string per the [API documentation](https://docs.gitlab.com/ee/api/issues.html#list-issues).
|
475
|
+
This condition is only applicable for issues (not merge requests).
|
476
|
+
|
477
|
+
| State | Type | Value |
|
478
|
+
| --------- | ---- | ------ |
|
479
|
+
| Any weight | string | `Any` |
|
480
|
+
| No weight | string | `None` |
|
481
|
+
| Specific weight | integer | integer |
|
482
|
+
|
483
|
+
Example:
|
484
|
+
|
485
|
+
```yml
|
486
|
+
conditions:
|
487
|
+
weight: Any
|
488
|
+
```
|
489
|
+
|
490
|
+
##### Discussions condition
|
491
|
+
|
492
|
+
Accepts a hash of fields.
|
493
|
+
|
494
|
+
| Field | Type | Values | Required |
|
495
|
+
| --------- | ---- | ---- | -------- |
|
496
|
+
| `attribute` | string | `threads`, `notes` | yes |
|
497
|
+
| `condition` | string | `less_than`, `greater_than` | yes |
|
498
|
+
| `threshold` | integer | integer | yes |
|
499
|
+
|
500
|
+
Example:
|
501
|
+
|
502
|
+
```yml
|
503
|
+
conditions:
|
504
|
+
discussions:
|
505
|
+
attribute: threads
|
506
|
+
condition: greater_than
|
507
|
+
threshold: 15
|
508
|
+
```
|
509
|
+
|
470
510
|
##### Ruby condition
|
471
511
|
|
472
512
|
This condition allows users to write a Ruby expression to be evaluated for
|
data/gitlab-triage.gemspec
CHANGED
@@ -20,6 +20,8 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
|
22
22
|
spec.add_dependency 'activesupport', '~> 5.1'
|
23
|
+
spec.add_dependency 'globalid', '~> 0.4'
|
24
|
+
spec.add_dependency 'graphql-client', '~> 0.16'
|
23
25
|
spec.add_dependency 'httparty', '~> 0.17'
|
24
26
|
|
25
27
|
spec.add_development_dependency 'bundler'
|
@@ -2,16 +2,25 @@ module Gitlab
|
|
2
2
|
module Triage
|
3
3
|
module APIQueryBuilders
|
4
4
|
class BaseQueryParamBuilder
|
5
|
-
attr_reader :param_name, :param_contents
|
5
|
+
attr_reader :param_name, :param_contents, :allowed_values
|
6
6
|
|
7
|
-
def initialize(param_name, param_contents)
|
7
|
+
def initialize(param_name, param_contents, allowed_values: nil)
|
8
8
|
@param_name = param_name
|
9
9
|
@param_contents = param_contents
|
10
|
+
@allowed_values = allowed_values
|
11
|
+
|
12
|
+
validate_allowed_values! if allowed_values
|
10
13
|
end
|
11
14
|
|
12
15
|
def build_param
|
13
16
|
"&#{param_name}=#{param_content.strip}"
|
14
17
|
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def validate_allowed_values!
|
22
|
+
ParamsValidator.new([{ name: param_name, type: String, values: allowed_values }], { param_name => param_contents }).validate!
|
23
|
+
end
|
15
24
|
end
|
16
25
|
end
|
17
26
|
end
|
@@ -6,14 +6,22 @@ module Gitlab
|
|
6
6
|
class MultiQueryParamBuilder < BaseQueryParamBuilder
|
7
7
|
attr_reader :separator
|
8
8
|
|
9
|
-
def initialize(param_name, param_contents, separator)
|
9
|
+
def initialize(param_name, param_contents, separator, allowed_values: nil)
|
10
10
|
@separator = separator
|
11
|
-
super(param_name, param_contents)
|
11
|
+
super(param_name, Array(param_contents), allowed_values: allowed_values)
|
12
12
|
end
|
13
13
|
|
14
14
|
def param_content
|
15
15
|
param_contents.map(&:strip).join(separator)
|
16
16
|
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def validate_allowed_values!
|
21
|
+
param_contents.each do |param|
|
22
|
+
ParamsValidator.new([{ name: param_name, type: String, values: allowed_values }], { param_name => param }).validate!
|
23
|
+
end
|
24
|
+
end
|
17
25
|
end
|
18
26
|
end
|
19
27
|
end
|
data/lib/gitlab/triage/engine.rb
CHANGED
@@ -7,6 +7,7 @@ require_relative 'filters/votes_conditions_filter'
|
|
7
7
|
require_relative 'filters/no_additional_labels_conditions_filter'
|
8
8
|
require_relative 'filters/author_member_conditions_filter'
|
9
9
|
require_relative 'filters/assignee_member_conditions_filter'
|
10
|
+
require_relative 'filters/discussions_conditions_filter'
|
10
11
|
require_relative 'filters/ruby_conditions_filter'
|
11
12
|
require_relative 'limiters/date_field_limiter'
|
12
13
|
require_relative 'action'
|
@@ -19,7 +20,10 @@ require_relative 'api_query_builders/single_query_param_builder'
|
|
19
20
|
require_relative 'api_query_builders/multi_query_param_builder'
|
20
21
|
require_relative 'url_builders/url_builder'
|
21
22
|
require_relative 'network'
|
23
|
+
require_relative 'graphql_network'
|
22
24
|
require_relative 'network_adapters/httparty_adapter'
|
25
|
+
require_relative 'network_adapters/graphql_adapter'
|
26
|
+
require_relative 'graphql_queries/query_builder'
|
23
27
|
require_relative 'ui'
|
24
28
|
|
25
29
|
module Gitlab
|
@@ -27,7 +31,14 @@ module Gitlab
|
|
27
31
|
class Engine
|
28
32
|
attr_reader :per_page, :policies, :options
|
29
33
|
|
30
|
-
|
34
|
+
DEFAULT_NETWORK_ADAPTER = Gitlab::Triage::NetworkAdapters::HttpartyAdapter
|
35
|
+
DEFAULT_GRAPHQL_ADAPTER = Gitlab::Triage::NetworkAdapters::GraphqlAdapter
|
36
|
+
ALLOWED_STATE_VALUES = {
|
37
|
+
issues: %w[opened closed],
|
38
|
+
merge_requests: %w[opened closed merged]
|
39
|
+
}.with_indifferent_access.freeze
|
40
|
+
|
41
|
+
def initialize(policies:, options:, network_adapter_class: DEFAULT_NETWORK_ADAPTER, graphql_network_adapter_class: DEFAULT_GRAPHQL_ADAPTER)
|
31
42
|
options.host_url = policies.delete(:host_url) { options.host_url }
|
32
43
|
options.api_version = policies.delete(:api_version) { 'v4' }
|
33
44
|
options.dry_run = ENV['TEST'] == 'true' if options.dry_run.nil?
|
@@ -36,6 +47,7 @@ module Gitlab
|
|
36
47
|
@policies = policies
|
37
48
|
@options = options
|
38
49
|
@network_adapter_class = network_adapter_class
|
50
|
+
@graphql_network_adapter_class = graphql_network_adapter_class
|
39
51
|
|
40
52
|
assert_all!
|
41
53
|
assert_project_id!
|
@@ -62,6 +74,10 @@ module Gitlab
|
|
62
74
|
@network ||= Network.new(network_adapter)
|
63
75
|
end
|
64
76
|
|
77
|
+
def graphql_network
|
78
|
+
@graphql_network ||= GraphqlNetwork.new(graphql_network_adapter)
|
79
|
+
end
|
80
|
+
|
65
81
|
private
|
66
82
|
|
67
83
|
def assert_project_id!
|
@@ -94,6 +110,10 @@ module Gitlab
|
|
94
110
|
@network_adapter ||= @network_adapter_class.new(options)
|
95
111
|
end
|
96
112
|
|
113
|
+
def graphql_network_adapter
|
114
|
+
@graphql_network_adapter ||= @graphql_network_adapter_class.new(options)
|
115
|
+
end
|
116
|
+
|
97
117
|
def rule_conditions(rule)
|
98
118
|
rule.fetch(:conditions) { {} }
|
99
119
|
end
|
@@ -158,8 +178,13 @@ module Gitlab
|
|
158
178
|
# retrieving the resources for every rule is inefficient
|
159
179
|
# however, previous rules may affect those upcoming
|
160
180
|
resources = network.query_api(build_get_url(resource_type, conditions))
|
181
|
+
iids = resources.pluck('iid').map(&:to_s)
|
182
|
+
|
183
|
+
graphql_query = build_graphql_query(resource_type, conditions)
|
184
|
+
graphql_resources = graphql_network.query(graphql_query, source: options.source_id, iids: iids) if graphql_query.present?
|
161
185
|
# In some filters/actions we want to know which resource type it is
|
162
186
|
attach_resource_type(resources, resource_type)
|
187
|
+
decorate_resources_with_graphql_data(resources, graphql_resources)
|
163
188
|
|
164
189
|
puts "\n\n* Found #{resources.count} resources..."
|
165
190
|
print "* Filtering resources..."
|
@@ -180,6 +205,13 @@ module Gitlab
|
|
180
205
|
resources.each { |resource| resource[:type] ||= resource_type }
|
181
206
|
end
|
182
207
|
|
208
|
+
def decorate_resources_with_graphql_data(resources, graphql_resources)
|
209
|
+
return if graphql_resources.nil?
|
210
|
+
|
211
|
+
graphql_resources_by_id = graphql_resources.to_h { |resource| [resource[:id], resource] }
|
212
|
+
resources.each { |resource| resource.merge!(graphql_resources_by_id[resource[:id]].to_h) }
|
213
|
+
end
|
214
|
+
|
183
215
|
def process_action(policy)
|
184
216
|
Action.process(
|
185
217
|
policy: policy,
|
@@ -213,6 +245,10 @@ module Gitlab
|
|
213
245
|
results << Filters::AssigneeMemberConditionsFilter.new(resource, conditions[:assignee_member], network).calculate
|
214
246
|
end
|
215
247
|
|
248
|
+
if conditions[:discussions]
|
249
|
+
results << Filters::DiscussionsConditionsFilter.new(resource, conditions[:discussions]).calculate
|
250
|
+
end
|
251
|
+
|
216
252
|
if conditions[:ruby]
|
217
253
|
results << Filters::RubyConditionsFilter.new(resource, conditions, network).calculate
|
218
254
|
end
|
@@ -244,7 +280,13 @@ module Gitlab
|
|
244
280
|
condition_builders << APIQueryBuilders::MultiQueryParamBuilder.new('not[labels]', conditions[:forbidden_labels], ',')
|
245
281
|
end
|
246
282
|
|
247
|
-
|
283
|
+
if conditions[:state]
|
284
|
+
condition_builders << APIQueryBuilders::SingleQueryParamBuilder.new(
|
285
|
+
'state',
|
286
|
+
conditions[:state],
|
287
|
+
allowed_values: ALLOWED_STATE_VALUES[resource_type])
|
288
|
+
end
|
289
|
+
|
248
290
|
condition_builders << APIQueryBuilders::SingleQueryParamBuilder.new('milestone', Array(conditions[:milestone])[0]) if conditions[:milestone]
|
249
291
|
condition_builders << APIQueryBuilders::SingleQueryParamBuilder.new('source_branch', conditions[:source_branch]) if conditions[:source_branch]
|
250
292
|
condition_builders << APIQueryBuilders::SingleQueryParamBuilder.new('target_branch', conditions[:target_branch]) if conditions[:target_branch]
|
@@ -253,6 +295,10 @@ module Gitlab
|
|
253
295
|
condition_builders << APIQueryBuilders::DateQueryParamBuilder.new(conditions.delete(:date))
|
254
296
|
end
|
255
297
|
|
298
|
+
if conditions[:weight] && resource_type.to_sym == :issues
|
299
|
+
condition_builders << APIQueryBuilders::SingleQueryParamBuilder.new('weight', conditions[:weight])
|
300
|
+
end
|
301
|
+
|
256
302
|
condition_builders.each do |condition_builder|
|
257
303
|
params[condition_builder.param_name] = condition_builder.param_content
|
258
304
|
end
|
@@ -266,6 +312,11 @@ module Gitlab
|
|
266
312
|
params: params
|
267
313
|
).build
|
268
314
|
end
|
315
|
+
|
316
|
+
def build_graphql_query(resource_type, conditions)
|
317
|
+
Gitlab::Triage::GraphqlQueries::QueryBuilder
|
318
|
+
.new(options.source, resource_type, conditions)
|
319
|
+
end
|
269
320
|
end
|
270
321
|
end
|
271
322
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative 'base_conditions_filter'
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
module Triage
|
5
|
+
module Filters
|
6
|
+
class DiscussionsConditionsFilter < BaseConditionsFilter
|
7
|
+
ATTRIBUTES = %w[notes threads].freeze
|
8
|
+
CONDITIONS = %w[greater_than less_than].freeze
|
9
|
+
|
10
|
+
def self.filter_parameters
|
11
|
+
[
|
12
|
+
{
|
13
|
+
name: :attribute,
|
14
|
+
type: String,
|
15
|
+
values: ATTRIBUTES
|
16
|
+
},
|
17
|
+
{
|
18
|
+
name: :condition,
|
19
|
+
type: String,
|
20
|
+
values: CONDITIONS
|
21
|
+
},
|
22
|
+
{
|
23
|
+
name: :threshold,
|
24
|
+
type: Numeric
|
25
|
+
}
|
26
|
+
]
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize_variables(condition)
|
30
|
+
@attribute = condition[:attribute].to_sym
|
31
|
+
@condition = condition[:condition].to_sym
|
32
|
+
@threshold = condition[:threshold]
|
33
|
+
end
|
34
|
+
|
35
|
+
def resource_value
|
36
|
+
if @attribute == :notes
|
37
|
+
@resource[:user_notes_count]
|
38
|
+
else
|
39
|
+
@resource.dig(:discussions, :nodes)&.count do |node|
|
40
|
+
!node&.dig(:notes, :nodes, 0, :system)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def condition_value
|
46
|
+
@threshold
|
47
|
+
end
|
48
|
+
|
49
|
+
def calculate
|
50
|
+
case @condition
|
51
|
+
when :greater_than
|
52
|
+
resource_value.to_i > condition_value
|
53
|
+
when :less_than
|
54
|
+
resource_value.to_i < condition_value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
require 'net/protocol'
|
3
|
+
require 'globalid'
|
4
|
+
|
5
|
+
require_relative 'retryable'
|
6
|
+
require_relative 'ui'
|
7
|
+
require_relative 'errors'
|
8
|
+
|
9
|
+
module Gitlab
|
10
|
+
module Triage
|
11
|
+
class GraphqlNetwork
|
12
|
+
attr_reader :options, :adapter
|
13
|
+
|
14
|
+
def initialize(adapter)
|
15
|
+
@adapter = adapter
|
16
|
+
@options = adapter.options
|
17
|
+
end
|
18
|
+
|
19
|
+
def query(graphql_query, variables = {})
|
20
|
+
return if graphql_query.blank?
|
21
|
+
|
22
|
+
response = {}
|
23
|
+
resources = []
|
24
|
+
|
25
|
+
parsed_graphql_query = adapter.parse(graphql_query.query)
|
26
|
+
|
27
|
+
begin
|
28
|
+
print '.'
|
29
|
+
|
30
|
+
response = adapter.query(
|
31
|
+
parsed_graphql_query,
|
32
|
+
resource_path: graphql_query.resource_path,
|
33
|
+
variables: variables.merge(after: response.delete(:end_cursor))
|
34
|
+
)
|
35
|
+
|
36
|
+
resources.concat(Array.wrap(response.delete(:results)))
|
37
|
+
end while response.delete(:more_pages)
|
38
|
+
|
39
|
+
resources
|
40
|
+
.map { |resource| resource.deep_transform_keys(&:underscore) }
|
41
|
+
.map(&:with_indifferent_access)
|
42
|
+
.map { |resource| resource.merge(id: extract_id_from_global_id(resource[:id])) }
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def extract_id_from_global_id(global_id)
|
48
|
+
return if global_id.blank?
|
49
|
+
|
50
|
+
GlobalID.parse(global_id).model_id.to_i
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'user_notes_query'
|
2
|
+
require_relative 'threads_query'
|
3
|
+
|
4
|
+
module Gitlab
|
5
|
+
module Triage
|
6
|
+
module GraphqlQueries
|
7
|
+
class QueryBuilder
|
8
|
+
def initialize(source_type, resource_type, conditions)
|
9
|
+
@source_type = source_type.to_s.singularize
|
10
|
+
@resource_type = resource_type
|
11
|
+
@conditions = conditions
|
12
|
+
end
|
13
|
+
|
14
|
+
def resource_path
|
15
|
+
[source_type, resource_type]
|
16
|
+
end
|
17
|
+
|
18
|
+
def query
|
19
|
+
return if query_template.nil?
|
20
|
+
|
21
|
+
format(query_template, source_type: source_type, resource_type: resource_type.to_s.camelize(:lower), group_query: group_query)
|
22
|
+
end
|
23
|
+
|
24
|
+
delegate :present?, to: :query_template
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :source_type, :resource_type, :conditions
|
29
|
+
|
30
|
+
def query_template
|
31
|
+
case conditions.dig(:discussions, :attribute).to_s
|
32
|
+
when 'notes'
|
33
|
+
UserNotesQuery
|
34
|
+
when 'threads'
|
35
|
+
ThreadsQuery
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def group_query
|
40
|
+
return if source_type != 'group'
|
41
|
+
|
42
|
+
', includeSubgroups: true'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Gitlab
|
2
|
+
module Triage
|
3
|
+
module GraphqlQueries
|
4
|
+
ThreadsQuery = <<-GRAPHQL.freeze # rubocop:disable Naming/ConstantName
|
5
|
+
query($source: ID!, $after: String, $iids: [String!]) {
|
6
|
+
%{source_type}(fullPath: $source) {
|
7
|
+
id
|
8
|
+
%{resource_type}(after: $after, iids: $iids%{group_query}) {
|
9
|
+
pageInfo {
|
10
|
+
hasNextPage
|
11
|
+
endCursor
|
12
|
+
}
|
13
|
+
nodes {
|
14
|
+
id
|
15
|
+
discussions {
|
16
|
+
nodes {
|
17
|
+
notes {
|
18
|
+
nodes {
|
19
|
+
system
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
GRAPHQL
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Gitlab
|
2
|
+
module Triage
|
3
|
+
module GraphqlQueries
|
4
|
+
UserNotesQuery = <<-GRAPHQL.freeze # rubocop:disable Naming/ConstantName
|
5
|
+
query($source: ID!, $after: String, $iids: [String!]) {
|
6
|
+
%{source_type}(fullPath: $source) {
|
7
|
+
id
|
8
|
+
%{resource_type}(after: $after, iids: $iids%{group_query}) {
|
9
|
+
pageInfo {
|
10
|
+
hasNextPage
|
11
|
+
endCursor
|
12
|
+
}
|
13
|
+
nodes {
|
14
|
+
id
|
15
|
+
userNotesCount
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
GRAPHQL
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'graphql/client'
|
2
|
+
require 'graphql/client/http'
|
3
|
+
|
4
|
+
require_relative 'base_adapter'
|
5
|
+
require_relative '../ui'
|
6
|
+
require_relative '../errors'
|
7
|
+
|
8
|
+
module Gitlab
|
9
|
+
module Triage
|
10
|
+
module NetworkAdapters
|
11
|
+
class GraphqlAdapter < BaseAdapter
|
12
|
+
Client = GraphQL::Client
|
13
|
+
|
14
|
+
def query(graphql_query, resource_path: [], variables: {})
|
15
|
+
response = client.query(graphql_query, variables: variables, context: { token: options.token })
|
16
|
+
|
17
|
+
raise_on_error!(response)
|
18
|
+
|
19
|
+
parsed_response = parse_response(response, resource_path)
|
20
|
+
|
21
|
+
return { results: {} } if parsed_response.nil?
|
22
|
+
return { results: parsed_response.map(&:to_h) } if parsed_response.is_a?(Client::List)
|
23
|
+
return { results: parsed_response.to_h } unless parsed_response.nodes?
|
24
|
+
|
25
|
+
{
|
26
|
+
more_pages: parsed_response.page_info.has_next_page,
|
27
|
+
end_cursor: parsed_response.page_info.end_cursor,
|
28
|
+
results: parsed_response.nodes.map(&:to_h)
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
delegate :parse, to: :client
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def parse_response(response, resource_path)
|
37
|
+
resource_path.reduce(response.data) { |data, resource| data&.send(resource) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def raise_on_error!(response)
|
41
|
+
return if response.errors.blank?
|
42
|
+
|
43
|
+
puts Gitlab::Triage::UI.debug response.inspect if options.debug
|
44
|
+
|
45
|
+
raise "There was an error: #{response.errors.messages.to_json}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def http_client
|
49
|
+
Client::HTTP.new("#{options.host_url}/api/graphql") do
|
50
|
+
def headers(context) # rubocop:disable Lint/NestedMethodDefinition
|
51
|
+
{
|
52
|
+
'Content-type' => 'application/json',
|
53
|
+
'PRIVATE-TOKEN' => context[:token]
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def schema
|
60
|
+
@schema ||= Client.load_schema(http_client)
|
61
|
+
end
|
62
|
+
|
63
|
+
def client
|
64
|
+
@client ||= Client.new(schema: schema, execute: http_client).tap { |client| client.allow_dynamic_queries = true }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -1,20 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'delegate'
|
4
4
|
|
5
5
|
module Gitlab
|
6
6
|
module Triage
|
7
7
|
module PoliciesResources
|
8
|
-
|
9
|
-
include Enumerable
|
10
|
-
extend Forwardable
|
11
|
-
|
12
|
-
def initialize(new_resources)
|
13
|
-
@resources = new_resources
|
14
|
-
end
|
15
|
-
|
16
|
-
def_delegator :@resources, :each
|
17
|
-
end
|
8
|
+
RuleResources = Class.new(SimpleDelegator)
|
18
9
|
end
|
19
10
|
end
|
20
11
|
end
|
@@ -1,20 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'delegate'
|
4
4
|
|
5
5
|
module Gitlab
|
6
6
|
module Triage
|
7
7
|
module PoliciesResources
|
8
|
-
|
9
|
-
include Enumerable
|
10
|
-
extend Forwardable
|
11
|
-
|
12
|
-
def initialize(new_rule_to_resources)
|
13
|
-
@rule_to_resources = new_rule_to_resources
|
14
|
-
end
|
15
|
-
|
16
|
-
def_delegator :@rule_to_resources, :each
|
17
|
-
end
|
8
|
+
SummaryResources = Class.new(SimpleDelegator)
|
18
9
|
end
|
19
10
|
end
|
20
11
|
end
|
@@ -17,7 +17,7 @@ module Gitlab
|
|
17
17
|
|
18
18
|
def build
|
19
19
|
url = base_url
|
20
|
-
url << "/#{
|
20
|
+
url << "/#{percent_encode(@resource_id.to_s)}" if @resource_id
|
21
21
|
url << "/#{@sub_resource_type}" if @sub_resource_type
|
22
22
|
url << params_string if @params
|
23
23
|
url
|
@@ -31,16 +31,20 @@ module Gitlab
|
|
31
31
|
|
32
32
|
def base_url
|
33
33
|
url = host_with_api_url
|
34
|
-
url << "/#{@source}/#{
|
34
|
+
url << "/#{@source}/#{percent_encode(@source_id.to_s)}" unless @all
|
35
35
|
url << "/#{@resource_type}" if @resource_type
|
36
36
|
url
|
37
37
|
end
|
38
38
|
|
39
39
|
def params_string
|
40
40
|
"?" << @params.map do |k, v|
|
41
|
-
"#{k}=#{v}"
|
41
|
+
"#{percent_encode(k.to_s)}=#{percent_encode(v.to_s)}"
|
42
42
|
end.join("&")
|
43
43
|
end
|
44
|
+
|
45
|
+
def percent_encode(str)
|
46
|
+
CGI.escape(str).gsub('+', '%20')
|
47
|
+
end
|
44
48
|
end
|
45
49
|
end
|
46
50
|
end
|
@@ -34,7 +34,7 @@ module Gitlab
|
|
34
34
|
def validate_parameter_content(value)
|
35
35
|
@parameter_definitions.each do |param|
|
36
36
|
if param[:values]
|
37
|
-
raise InvalidParameter, "#{param[:name]} must be
|
37
|
+
raise InvalidParameter, "#{param[:name]} must be one of #{param[:values].join(',')}" unless param[:values].include?(value[param[:name]])
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab-triage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.14.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitLab
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -24,6 +24,34 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '5.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: globalid
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: graphql-client
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.16'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.16'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: httparty
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -158,16 +186,22 @@ files:
|
|
158
186
|
- lib/gitlab/triage/filters/assignee_member_conditions_filter.rb
|
159
187
|
- lib/gitlab/triage/filters/author_member_conditions_filter.rb
|
160
188
|
- lib/gitlab/triage/filters/base_conditions_filter.rb
|
189
|
+
- lib/gitlab/triage/filters/discussions_conditions_filter.rb
|
161
190
|
- lib/gitlab/triage/filters/member_conditions_filter.rb
|
162
191
|
- lib/gitlab/triage/filters/merge_request_date_conditions_filter.rb
|
163
192
|
- lib/gitlab/triage/filters/name_conditions_filter.rb
|
164
193
|
- lib/gitlab/triage/filters/no_additional_labels_conditions_filter.rb
|
165
194
|
- lib/gitlab/triage/filters/ruby_conditions_filter.rb
|
166
195
|
- lib/gitlab/triage/filters/votes_conditions_filter.rb
|
196
|
+
- lib/gitlab/triage/graphql_network.rb
|
197
|
+
- lib/gitlab/triage/graphql_queries/query_builder.rb
|
198
|
+
- lib/gitlab/triage/graphql_queries/threads_query.rb
|
199
|
+
- lib/gitlab/triage/graphql_queries/user_notes_query.rb
|
167
200
|
- lib/gitlab/triage/limiters/base_limiter.rb
|
168
201
|
- lib/gitlab/triage/limiters/date_field_limiter.rb
|
169
202
|
- lib/gitlab/triage/network.rb
|
170
203
|
- lib/gitlab/triage/network_adapters/base_adapter.rb
|
204
|
+
- lib/gitlab/triage/network_adapters/graphql_adapter.rb
|
171
205
|
- lib/gitlab/triage/network_adapters/httparty_adapter.rb
|
172
206
|
- lib/gitlab/triage/network_adapters/test_adapter.rb
|
173
207
|
- lib/gitlab/triage/option_parser.rb
|
@@ -213,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
213
247
|
- !ruby/object:Gem::Version
|
214
248
|
version: '0'
|
215
249
|
requirements: []
|
216
|
-
rubygems_version: 3.1.
|
250
|
+
rubygems_version: 3.1.4
|
217
251
|
signing_key:
|
218
252
|
specification_version: 4
|
219
253
|
summary: GitLab triage automation project.
|