gitlab-triage 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +131 -1
- data/gitlab-triage.gemspec +1 -0
- data/lib/gitlab/triage/api_query_builders/base_query_param_builder.rb +18 -0
- data/lib/gitlab/triage/api_query_builders/multi_query_param_builder.rb +20 -0
- data/lib/gitlab/triage/api_query_builders/single_query_param_builder.rb +13 -0
- data/lib/gitlab/triage/engine.rb +45 -34
- data/lib/gitlab/triage/expand_condition.rb +21 -0
- data/lib/gitlab/triage/expand_condition/sequence.rb +19 -0
- data/lib/gitlab/triage/expand_condition/sequence/expansion.rb +187 -0
- data/lib/gitlab/triage/filters/assignee_member_conditions_filter.rb +13 -0
- data/lib/gitlab/triage/filters/author_member_conditions_filter.rb +13 -0
- data/lib/gitlab/triage/{limiters/base_conditions_limiter.rb → filters/base_conditions_filter.rb} +24 -12
- data/lib/gitlab/triage/{limiters/date_conditions_limiter.rb → filters/date_conditions_filter.rb} +4 -4
- data/lib/gitlab/triage/{limiters/forbidden_labels_conditions_limiter.rb → filters/forbidden_labels_conditions_filter.rb} +3 -7
- data/lib/gitlab/triage/{limiters/member_conditions_limiter.rb → filters/member_conditions_filter.rb} +5 -5
- data/lib/gitlab/triage/{limiters/name_conditions_limiter.rb → filters/name_conditions_filter.rb} +3 -7
- data/lib/gitlab/triage/{limiters/no_additional_labels_conditions_limiter.rb → filters/no_additional_labels_conditions_filter.rb} +4 -4
- data/lib/gitlab/triage/filters/ruby_conditions_filter.rb +31 -0
- data/lib/gitlab/triage/{limiters/votes_conditions_limiter.rb → filters/votes_conditions_filter.rb} +4 -4
- data/lib/gitlab/triage/network.rb +5 -0
- data/lib/gitlab/triage/resource/base.rb +46 -0
- data/lib/gitlab/triage/resource/context.rb +34 -0
- data/lib/gitlab/triage/resource/milestone.rb +82 -0
- data/lib/gitlab/triage/version.rb +1 -1
- metadata +35 -14
- data/lib/gitlab/triage/filter_builders/base_filter_builder.rb +0 -18
- data/lib/gitlab/triage/filter_builders/multi_filter_builder.rb +0 -20
- data/lib/gitlab/triage/filter_builders/single_filter_builder.rb +0 -13
- data/lib/gitlab/triage/limiters/assignee_member_conditions_limiter.rb +0 -13
- data/lib/gitlab/triage/limiters/author_member_conditions_limiter.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ab21a6dffa12d92f9321ac3c6d2bc0c92627fad703fceb7ebf68494d8f5b259
|
4
|
+
data.tar.gz: e5868058935e55d70ccadaada8030a8bec92dee6ceeeea2f9996cd60f3823cdf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a875bebf70cd7dcc2a6c5f064d692535cdf98b664323fb640b22fed7f52ac4d5f49e8b21adb5b4942f626e4f4638d23f3042109ae13c495ae9c732d177e9298f
|
7
|
+
data.tar.gz: 01a627062e23ba73d40be5cc7260a92aa7be9ec30b9fb694585305ae919435174099a701d6045998c03738ee7fd7dcf0cac9ebf821abfea9b4697fd6f8f55e06
|
data/README.md
CHANGED
@@ -44,7 +44,7 @@ resource_rules:
|
|
44
44
|
interval_type: days
|
45
45
|
interval: 5
|
46
46
|
state: opened
|
47
|
-
|
47
|
+
labels:
|
48
48
|
- No Label
|
49
49
|
actions:
|
50
50
|
labels:
|
@@ -89,6 +89,7 @@ Available condition types:
|
|
89
89
|
- [`no_additional_labels` condition](#no-additional-labels-condition)
|
90
90
|
- [`author_member` condition](#author-member-condition)
|
91
91
|
- [`assignee_member` condition](#assignee-member-condition)
|
92
|
+
- [`ruby` condition](#ruby-condition)
|
92
93
|
|
93
94
|
##### Date condition
|
94
95
|
|
@@ -177,6 +178,71 @@ conditions:
|
|
177
178
|
- feature proposal
|
178
179
|
```
|
179
180
|
|
181
|
+
###### Labels over sequences
|
182
|
+
|
183
|
+
The name of a label can contain one or more sequence conditions, written
|
184
|
+
like `{0..9}`, which means `0`, `1`, `2`, and so on up to `9`. For each
|
185
|
+
number, the rule will be duplicated with the new label name.
|
186
|
+
|
187
|
+
Example:
|
188
|
+
|
189
|
+
```yml
|
190
|
+
resource_rules:
|
191
|
+
issues:
|
192
|
+
rules:
|
193
|
+
- name: Add missing ~"missed\-deliverable" label
|
194
|
+
conditions:
|
195
|
+
labels:
|
196
|
+
- missed:{10..11}.{0..1}
|
197
|
+
- deliverable
|
198
|
+
actions:
|
199
|
+
labels:
|
200
|
+
- missed deliverable
|
201
|
+
```
|
202
|
+
|
203
|
+
Which will be expanded into:
|
204
|
+
|
205
|
+
```yml
|
206
|
+
resource_rules:
|
207
|
+
issues:
|
208
|
+
rules:
|
209
|
+
- name: Add missing ~"missed\-deliverable" label
|
210
|
+
conditions:
|
211
|
+
labels:
|
212
|
+
- missed:10.0
|
213
|
+
- deliverable
|
214
|
+
actions:
|
215
|
+
labels:
|
216
|
+
- missed deliverable
|
217
|
+
|
218
|
+
- name: Add missing ~"missed\-deliverable" label
|
219
|
+
conditions:
|
220
|
+
labels:
|
221
|
+
- missed:10.1
|
222
|
+
- deliverable
|
223
|
+
actions:
|
224
|
+
labels:
|
225
|
+
- missed deliverable
|
226
|
+
|
227
|
+
- name: Add missing ~"missed\-deliverable" label
|
228
|
+
conditions:
|
229
|
+
labels:
|
230
|
+
- missed:11.0
|
231
|
+
- deliverable
|
232
|
+
actions:
|
233
|
+
labels:
|
234
|
+
- missed deliverable
|
235
|
+
|
236
|
+
- name: Add missing ~"missed\-deliverable" label
|
237
|
+
conditions:
|
238
|
+
labels:
|
239
|
+
- missed:11.1
|
240
|
+
- deliverable
|
241
|
+
actions:
|
242
|
+
labels:
|
243
|
+
- missed deliverable
|
244
|
+
```
|
245
|
+
|
180
246
|
##### Forbidden labels condition
|
181
247
|
|
182
248
|
Accepts an array of strings. Each element in the array represents the name of a label to filter on.
|
@@ -251,6 +317,70 @@ conditions:
|
|
251
317
|
source_id: 9970
|
252
318
|
```
|
253
319
|
|
320
|
+
##### Ruby condition
|
321
|
+
|
322
|
+
This condition allows users to write a Ruby expression to be evaluated for
|
323
|
+
each resource. If it evaluates to a truthy value, it satisfies the condition.
|
324
|
+
If it evaluates to a falsey value, it does not satisfy the condition.
|
325
|
+
|
326
|
+
Accepts a string as the Ruby expression.
|
327
|
+
|
328
|
+
Example:
|
329
|
+
|
330
|
+
```yml
|
331
|
+
conditions:
|
332
|
+
ruby: Date.today > milestone.succ.start_date
|
333
|
+
```
|
334
|
+
|
335
|
+
In the above example, this describes that we want to act on the resources
|
336
|
+
which passed the next active milestone's starting date.
|
337
|
+
|
338
|
+
Here `milestone` will return a `Gitlab::Triage::Resource::Milestone` object,
|
339
|
+
representing the milestone of the questioning resource. `Milestone#succ` would
|
340
|
+
return the next active milestone, based on the `start_date` of all milestones
|
341
|
+
along with the representing milestone. If the milestone was coming from a
|
342
|
+
project, then it's based on all active milestones in that project. If the
|
343
|
+
milestone was coming from a group, then it's based on all active milestones
|
344
|
+
in the group.
|
345
|
+
|
346
|
+
If we also want to handle some edge cases, for example, a resource might not
|
347
|
+
have a milestone, and a milestone might not be active, and there might not
|
348
|
+
have a next milestone. We could instead write something like:
|
349
|
+
|
350
|
+
```yml
|
351
|
+
conditions:
|
352
|
+
ruby: milestone&.active? && milestone&.succ && Date.today > milestone.succ.start_date
|
353
|
+
```
|
354
|
+
|
355
|
+
This will make it only act on resources which have active milestones and
|
356
|
+
there exists next milestone which has already started.
|
357
|
+
|
358
|
+
Here's a list of currently available API:
|
359
|
+
|
360
|
+
##### API
|
361
|
+
|
362
|
+
| Name | Return type | Description |
|
363
|
+
| ---- | ---- | ---- |
|
364
|
+
| milestone | Milestone | The milestone attached to the resource |
|
365
|
+
|
366
|
+
##### Methods for `Milestone`
|
367
|
+
|
368
|
+
| Method | Return type | Description |
|
369
|
+
| ---- | ---- | ---- |
|
370
|
+
| id | Integer | The id of the milestone |
|
371
|
+
| iid | Integer | The iid of the milestone |
|
372
|
+
| project_id | Integer | The project id of the milestone if available |
|
373
|
+
| group_id | Integer | The group id of the milestone if available |
|
374
|
+
| title | String | The title of the milestone |
|
375
|
+
| description | String | The description of the milestone |
|
376
|
+
| state | String | The state of the milestone. Could be `active` or `closed` |
|
377
|
+
| due_date | Date | The due date of the milestone. Could be `nil` |
|
378
|
+
| start_date | Date | The start date of the milestone. Could be `nil` |
|
379
|
+
| updated_at | Time | The updated timestamp of the milestone |
|
380
|
+
| created_at | Time | The created timestamp of the milestone |
|
381
|
+
| succ | Milestone | The next active milestone beside this milestone |
|
382
|
+
| active? | Boolean | `true` if `state` is `active`; `false` otherwise |
|
383
|
+
|
254
384
|
#### Actions field
|
255
385
|
|
256
386
|
Used to declare an action to be carried out on a resource if **all** conditions are satisfied.
|
data/gitlab-triage.gemspec
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Gitlab
|
2
|
+
module Triage
|
3
|
+
module APIQueryBuilders
|
4
|
+
class BaseQueryParamBuilder
|
5
|
+
attr_reader :param_name, :param_contents
|
6
|
+
|
7
|
+
def initialize(param_name, param_contents)
|
8
|
+
@param_name = param_name
|
9
|
+
@param_contents = param_contents
|
10
|
+
end
|
11
|
+
|
12
|
+
def build_param
|
13
|
+
"&#{param_name}=#{param_content}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'base_query_param_builder'
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
module Triage
|
5
|
+
module APIQueryBuilders
|
6
|
+
class MultiQueryParamBuilder < BaseQueryParamBuilder
|
7
|
+
attr_reader :separator
|
8
|
+
|
9
|
+
def initialize(param_name, param_contents, separator)
|
10
|
+
@separator = separator
|
11
|
+
super(param_name, param_contents)
|
12
|
+
end
|
13
|
+
|
14
|
+
def param_content
|
15
|
+
param_contents.join(separator)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/gitlab/triage/engine.rb
CHANGED
@@ -1,19 +1,21 @@
|
|
1
1
|
require 'active_support/all'
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
5
|
-
require_relative '
|
6
|
-
require_relative '
|
7
|
-
require_relative '
|
8
|
-
require_relative '
|
3
|
+
require_relative 'expand_condition'
|
4
|
+
require_relative 'filters/date_conditions_filter'
|
5
|
+
require_relative 'filters/votes_conditions_filter'
|
6
|
+
require_relative 'filters/forbidden_labels_conditions_filter'
|
7
|
+
require_relative 'filters/no_additional_labels_conditions_filter'
|
8
|
+
require_relative 'filters/author_member_conditions_filter'
|
9
|
+
require_relative 'filters/assignee_member_conditions_filter'
|
10
|
+
require_relative 'filters/ruby_conditions_filter'
|
9
11
|
require_relative 'command_builders/comment_body_builder'
|
10
12
|
require_relative 'command_builders/comment_command_builder'
|
11
13
|
require_relative 'command_builders/label_command_builder'
|
12
14
|
require_relative 'command_builders/remove_label_command_builder'
|
13
15
|
require_relative 'command_builders/cc_command_builder'
|
14
16
|
require_relative 'command_builders/status_command_builder'
|
15
|
-
require_relative '
|
16
|
-
require_relative '
|
17
|
+
require_relative 'api_query_builders/single_query_param_builder'
|
18
|
+
require_relative 'api_query_builders/multi_query_param_builder'
|
17
19
|
require_relative 'url_builders/url_builder'
|
18
20
|
require_relative 'network'
|
19
21
|
require_relative 'network_adapters/httparty_adapter'
|
@@ -42,10 +44,10 @@ module Gitlab
|
|
42
44
|
puts "Performing a dry run.\n\n" if options.dry_run
|
43
45
|
|
44
46
|
resource_rules.each do |type, resource|
|
45
|
-
puts Gitlab::Triage::UI.header("Processing rules for #{type}")
|
47
|
+
puts Gitlab::Triage::UI.header("Processing rules for #{type}", char: '-')
|
46
48
|
puts
|
47
49
|
resource[:rules].each do |rule|
|
48
|
-
puts Gitlab::Triage::UI.header("Processing rule:
|
50
|
+
puts Gitlab::Triage::UI.header("Processing rule: **#{rule[:name]}**", char: '-')
|
49
51
|
puts
|
50
52
|
process_rule(type, rule)
|
51
53
|
end
|
@@ -87,40 +89,49 @@ module Gitlab
|
|
87
89
|
end
|
88
90
|
|
89
91
|
def process_rule(resource_type, rule)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
92
|
+
ExpandCondition.perform(rule_conditions(rule)) do |conditions|
|
93
|
+
# retrieving the resources for every rule is inefficient
|
94
|
+
# however, previous rules may affect those upcoming
|
95
|
+
resources = network.query_api(options.token, build_get_url(resource_type, conditions))
|
96
|
+
puts "\n\n* Found #{resources.count} resources..."
|
97
|
+
print "* Limiting resources..."
|
98
|
+
resources = limit_resources(resources, conditions)
|
99
|
+
puts "\n* Total resource after limiting: #{resources.count} resources"
|
100
|
+
process_resources(resource_type, resources, rule)
|
101
|
+
end
|
98
102
|
end
|
99
103
|
|
100
104
|
def limit_resources(resources, conditions)
|
105
|
+
net = { host_url: host_url, api_version: api_version, token: options.token, network: network }
|
106
|
+
|
101
107
|
resources.select do |resource|
|
102
108
|
results = []
|
103
|
-
|
104
|
-
results <<
|
105
|
-
results <<
|
106
|
-
results <<
|
107
|
-
results <<
|
108
|
-
results <<
|
109
|
-
|
109
|
+
|
110
|
+
results << Filters::DateConditionsFilter.new(resource, conditions[:date]).calculate if conditions[:date]
|
111
|
+
results << Filters::VotesConditionsFilter.new(resource, conditions[:upvotes]).calculate if conditions[:upvotes]
|
112
|
+
results << Filters::ForbiddenLabelsConditionsFilter.new(resource, conditions[:forbidden_labels]).calculate if conditions[:forbidden_labels]
|
113
|
+
results << Filters::NoAdditionalLabelsConditionsFilter.new(resource, conditions.fetch(:labels) { [] }).calculate if conditions[:no_additional_labels]
|
114
|
+
results << Filters::AuthorMemberConditionsFilter.new(resource, conditions[:author_member], net).calculate if conditions[:author_member]
|
115
|
+
results << Filters::AssigneeMemberConditionsFilter.new(resource, conditions[:assignee_member], net).calculate if conditions[:assignee_member]
|
116
|
+
results << Filters::RubyConditionsFilter.new(resource, conditions, net).calculate if conditions[:ruby]
|
117
|
+
|
118
|
+
results.all?
|
110
119
|
end
|
111
120
|
end
|
112
121
|
|
113
122
|
def process_resources(resource_type, resources, rule)
|
123
|
+
if options.dry_run
|
124
|
+
puts "\nThe following comments would be posted for the rule **#{rule[:name]}**:\n\n"
|
125
|
+
end
|
126
|
+
|
114
127
|
resources.each do |resource|
|
115
128
|
comment = build_comment(rule_actions(rule), resource: resource)
|
116
129
|
|
117
130
|
if options.dry_run
|
118
|
-
puts "
|
119
|
-
|
120
|
-
|
131
|
+
puts "# #{resource[:web_url]}\n```\n#{comment}\n```\n"
|
132
|
+
else
|
133
|
+
network.post_api(options.token, build_post_url(resource_type, resource), comment)
|
121
134
|
end
|
122
|
-
|
123
|
-
network.post_api(options.token, build_post_url(resource_type, resource), comment)
|
124
135
|
end
|
125
136
|
end
|
126
137
|
|
@@ -144,12 +155,12 @@ module Gitlab
|
|
144
155
|
}
|
145
156
|
|
146
157
|
condition_builders = []
|
147
|
-
condition_builders <<
|
148
|
-
condition_builders <<
|
149
|
-
condition_builders <<
|
158
|
+
condition_builders << APIQueryBuilders::MultiQueryParamBuilder.new('labels', conditions[:labels], ',') if conditions[:labels]
|
159
|
+
condition_builders << APIQueryBuilders::SingleQueryParamBuilder.new('state', conditions[:state]) if conditions[:state]
|
160
|
+
condition_builders << APIQueryBuilders::MultiQueryParamBuilder.new('milestone', conditions[:milestone], ',') if conditions[:milestone]
|
150
161
|
|
151
162
|
condition_builders.each do |condition_builder|
|
152
|
-
params[condition_builder.
|
163
|
+
params[condition_builder.param_name] = condition_builder.param_content
|
153
164
|
end
|
154
165
|
|
155
166
|
get_url = UrlBuilders::UrlBuilder.new(
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'expand_condition/sequence'
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
module Triage
|
5
|
+
module ExpandCondition
|
6
|
+
PIPELINE = [
|
7
|
+
Sequence
|
8
|
+
].freeze
|
9
|
+
|
10
|
+
def self.perform(conditions, pipeline = PIPELINE, &block)
|
11
|
+
expand([conditions], pipeline).each(&block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.expand(conditions, pipeline = PIPELINE)
|
15
|
+
pipeline.inject(conditions) do |result, job|
|
16
|
+
result.flat_map(&job.method(:expand))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'sequence/expansion'
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
module Triage
|
5
|
+
module ExpandCondition
|
6
|
+
module Sequence
|
7
|
+
def self.expand(conditions)
|
8
|
+
labels = conditions[:labels]
|
9
|
+
|
10
|
+
return conditions unless labels
|
11
|
+
|
12
|
+
Expansion.perform(labels).map do |new_labels|
|
13
|
+
conditions.merge(labels: new_labels)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
module Gitlab
|
2
|
+
module Triage
|
3
|
+
module ExpandCondition
|
4
|
+
module Sequence
|
5
|
+
module Expansion
|
6
|
+
PATTERN = /\{(\d+)\.\.(\d+)\}/
|
7
|
+
|
8
|
+
# This method will take a list of strings, which contains the
|
9
|
+
# sequence pattern, and expand them into each possible matches.
|
10
|
+
# For example, suppose the input is:
|
11
|
+
#
|
12
|
+
# * a:{0..1}
|
13
|
+
# * b:{2..3}
|
14
|
+
# * c
|
15
|
+
#
|
16
|
+
# The result would be:
|
17
|
+
#
|
18
|
+
# * * a:0
|
19
|
+
# * b:2
|
20
|
+
# * c
|
21
|
+
# * * a:0
|
22
|
+
# * b:3
|
23
|
+
# * c
|
24
|
+
# * * a:1
|
25
|
+
# * b:2
|
26
|
+
# * c
|
27
|
+
# * * a:1
|
28
|
+
# * b:3
|
29
|
+
# * c
|
30
|
+
#
|
31
|
+
# We get this by picking the 1st number from the 1st string,
|
32
|
+
# which is 0 from "a:{0..1}", and the 1st number from the
|
33
|
+
# 2nd string, which is 2 from "b:{2..3}", and since the 3rd
|
34
|
+
# string is just a fixed string, we just pick it.
|
35
|
+
#
|
36
|
+
# This way we have the first possible match, that is:
|
37
|
+
#
|
38
|
+
# * a:0
|
39
|
+
# * b:2
|
40
|
+
# * c
|
41
|
+
#
|
42
|
+
# Then we repeat the process by picking the next number for the
|
43
|
+
# next possible match, starting from the least significant
|
44
|
+
# string, which is c, but there's nothing more to pick. Then we
|
45
|
+
# go to the next one, which will be the 2nd string: "b:{2..3}",
|
46
|
+
# and we pick the 2nd number for it: 3. Since we have a new pick,
|
47
|
+
# we have a new possible match:
|
48
|
+
#
|
49
|
+
# * a:0
|
50
|
+
# * b:3
|
51
|
+
# * c
|
52
|
+
#
|
53
|
+
# Again we repeat the process, and 2nd string doesn't have more
|
54
|
+
# choices therefore we need to go to the 1st string now. When
|
55
|
+
# this happens, we'll need to reset the picks from the previous
|
56
|
+
# string, thus 2nd string will go back to 2. The next number for
|
57
|
+
# the 1st string is 1, and then we form the new match:
|
58
|
+
#
|
59
|
+
# * a:1
|
60
|
+
# * b:2
|
61
|
+
# * c
|
62
|
+
#
|
63
|
+
# The next step will be the last match by picking the next number
|
64
|
+
# from the 2nd string again: 3, and we get:
|
65
|
+
#
|
66
|
+
# * a:1
|
67
|
+
# * b:3
|
68
|
+
# * c
|
69
|
+
#
|
70
|
+
# The method will stop here because it had walked through all the
|
71
|
+
# possible combinations. The total number of results is the product
|
72
|
+
# of numbers of sequences.
|
73
|
+
#
|
74
|
+
# Note that a string can contain multiple sequences, and it will
|
75
|
+
# also walk through them one by one. For example, given:
|
76
|
+
#
|
77
|
+
# * a:{0..1}:{2..3}
|
78
|
+
# * c
|
79
|
+
#
|
80
|
+
# We'll get:
|
81
|
+
#
|
82
|
+
# * * a:0:2
|
83
|
+
# * c
|
84
|
+
# * * a:0:3
|
85
|
+
# * c
|
86
|
+
# * * a:1:2
|
87
|
+
# * c
|
88
|
+
# * * a:1:3
|
89
|
+
# * c
|
90
|
+
def self.perform(strings)
|
91
|
+
expanded_strings = strings.map(&method(:expand_sequences))
|
92
|
+
|
93
|
+
product_of_all(expanded_strings)
|
94
|
+
end
|
95
|
+
|
96
|
+
# This method returns the product of list of lists. For example,
|
97
|
+
# giving it [%w[a:0 a:1], %w[b:2 b:3], %w[c]] will return:
|
98
|
+
#
|
99
|
+
# [%w[a:0 b:2 c], %w[a:0 b:3 c], %w[a:1 b:2 c], %w[a:1 b:3 c]]
|
100
|
+
def self.product_of_all(expanded_strings)
|
101
|
+
expanded_strings.first.product(*expanded_strings.drop(1))
|
102
|
+
end
|
103
|
+
|
104
|
+
# This method expands the string from the sequences. For example,
|
105
|
+
# giving it "a:{0..1}:{2..3}" will return:
|
106
|
+
#
|
107
|
+
# %w[
|
108
|
+
# a:0:2
|
109
|
+
# a:0:3
|
110
|
+
# a:1:2
|
111
|
+
# a:1:3
|
112
|
+
# ]
|
113
|
+
def self.expand_sequences(string)
|
114
|
+
expand(string, scan_sequences(string))
|
115
|
+
end
|
116
|
+
|
117
|
+
# This method extracts the sequences from the string. For example,
|
118
|
+
# giving it "a:{0..1}:{2..3}" will return:
|
119
|
+
#
|
120
|
+
# [0..1, 2..3]
|
121
|
+
def self.scan_sequences(string)
|
122
|
+
string.scan(PATTERN).map do |(lower, upper)|
|
123
|
+
Integer(lower)..Integer(upper)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# This recursive method does the heavy lifting. It substitutes the
|
128
|
+
# sequence patterns in a string with a picked number from the
|
129
|
+
# sequence, and collect all the results. Here's an example:
|
130
|
+
#
|
131
|
+
# expand("a:{0..1}:{2..3}", [0..1, 2..3])
|
132
|
+
#
|
133
|
+
# This means that we want to pick the numbers from the sequences,
|
134
|
+
# and fill them back to the string containing the pattern in the
|
135
|
+
# respective order. We don't care which pattern it is because
|
136
|
+
# the order should have spoken for it. The result will be:
|
137
|
+
#
|
138
|
+
# %w[
|
139
|
+
# a:0:2
|
140
|
+
# a:0:3
|
141
|
+
# a:1:2
|
142
|
+
# a:1:3
|
143
|
+
# ]
|
144
|
+
#
|
145
|
+
# We start by picking the first sequence, which is 0..1 here. We
|
146
|
+
# want all the possible picks, thus we flat_map on it, substituting
|
147
|
+
# the first pattern with the picked number. This means we get:
|
148
|
+
#
|
149
|
+
# "a:0:{2..3}"
|
150
|
+
#
|
151
|
+
# For the first iteration. Before we jump to the next pick from the
|
152
|
+
# sequence, we recursively do this again on the current string,
|
153
|
+
# which only has one sequence pattern left. It will be called like:
|
154
|
+
#
|
155
|
+
# expand("a:0:{2..3}", [2..3])
|
156
|
+
#
|
157
|
+
# Because we also dropped the first sequence we have already used.
|
158
|
+
# On the next recursive call, we don't have any sequences left,
|
159
|
+
# therefore we just return the current string: "a:0:2".
|
160
|
+
#
|
161
|
+
# Flattening the recursion, it might look like this:
|
162
|
+
#
|
163
|
+
# (0..1).flat_map do |x|
|
164
|
+
# (2..3).flat_map do |y|
|
165
|
+
# "a:{0..1}:{2..3}".sub(PATTERN, x.to_s).sub(PATTERN, y.to_s)
|
166
|
+
# end
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
# So here we could clearly see that we go deep first, substituting
|
170
|
+
# the least significant pattern first, and then go back to the
|
171
|
+
# previous one, until there's nothing more to pick.
|
172
|
+
def self.expand(string, sequences)
|
173
|
+
if sequences.empty?
|
174
|
+
[string]
|
175
|
+
else
|
176
|
+
remainings = sequences.drop(1)
|
177
|
+
|
178
|
+
sequences.first.flat_map do |number|
|
179
|
+
expand(string.sub(PATTERN, number.to_s), remainings)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
data/lib/gitlab/triage/{limiters/base_conditions_limiter.rb → filters/base_conditions_filter.rb}
RENAMED
@@ -2,61 +2,73 @@ require 'active_support/all'
|
|
2
2
|
|
3
3
|
module Gitlab
|
4
4
|
module Triage
|
5
|
-
module
|
6
|
-
class
|
5
|
+
module Filters
|
6
|
+
class BaseConditionsFilter
|
7
7
|
def initialize(resource, condition)
|
8
8
|
@resource = resource
|
9
9
|
validate_condition(condition)
|
10
10
|
initialize_variables(condition)
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
14
|
-
|
13
|
+
def calculate
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.filter_parameters
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.params_filter_names(params = nil)
|
22
|
+
params ||= filter_parameters
|
15
23
|
|
16
24
|
params.map do |param|
|
17
25
|
param[:name]
|
18
26
|
end
|
19
27
|
end
|
20
28
|
|
21
|
-
def self.
|
22
|
-
|
29
|
+
def self.all_params_filter_names
|
30
|
+
params_filter_names
|
23
31
|
end
|
24
32
|
|
25
33
|
def self.params_checking_condition_value
|
26
|
-
|
34
|
+
params_filter_names params_check_for_field(:values)
|
27
35
|
end
|
28
36
|
|
29
37
|
def self.params_checking_condition_type
|
30
|
-
|
38
|
+
params_filter_names params_check_for_field(:type)
|
31
39
|
end
|
32
40
|
|
33
41
|
def self.params_check_for_field(field)
|
34
|
-
|
42
|
+
filter_parameters.select do |param|
|
35
43
|
param[field].present?
|
36
44
|
end
|
37
45
|
end
|
38
46
|
|
47
|
+
private
|
48
|
+
|
39
49
|
def validate_condition(condition)
|
40
50
|
validate_required_parameters(condition)
|
41
51
|
validate_parameter_types(condition)
|
42
52
|
validate_parameter_content(condition)
|
43
53
|
end
|
44
54
|
|
55
|
+
def initialize_variables(condition); end
|
56
|
+
|
45
57
|
def validate_required_parameters(condition)
|
46
|
-
self.class.
|
58
|
+
self.class.filter_parameters.each do |param|
|
47
59
|
raise ArgumentError, "#{param[:name]} is a required parameter" unless condition[param[:name]]
|
48
60
|
end
|
49
61
|
end
|
50
62
|
|
51
63
|
def validate_parameter_types(condition)
|
52
|
-
self.class.
|
64
|
+
self.class.filter_parameters.each do |param|
|
53
65
|
param_types = Array(param[:type]).flatten
|
54
66
|
raise ArgumentError, "#{param[:name]} must be of type #{param[:type]}" unless param_types.any? { |type| condition[param[:name]].is_a?(type) }
|
55
67
|
end
|
56
68
|
end
|
57
69
|
|
58
70
|
def validate_parameter_content(condition)
|
59
|
-
self.class.
|
71
|
+
self.class.filter_parameters.each do |param|
|
60
72
|
if param[:values]
|
61
73
|
raise ArgumentError, "#{param[:name]} must be of one of #{param[:values].join(',')}" unless param[:values].include?(condition[param[:name]])
|
62
74
|
end
|
data/lib/gitlab/triage/{limiters/date_conditions_limiter.rb → filters/date_conditions_filter.rb}
RENAMED
@@ -1,14 +1,14 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'base_conditions_filter'
|
2
2
|
|
3
3
|
module Gitlab
|
4
4
|
module Triage
|
5
|
-
module
|
6
|
-
class
|
5
|
+
module Filters
|
6
|
+
class DateConditionsFilter < BaseConditionsFilter
|
7
7
|
ATTRIBUTES = %w[updated_at created_at].freeze
|
8
8
|
CONDITIONS = %w[older_than newer_than].freeze
|
9
9
|
INTERVAL_TYPES = %w[days weeks months years].freeze
|
10
10
|
|
11
|
-
def self.
|
11
|
+
def self.filter_parameters
|
12
12
|
[
|
13
13
|
{
|
14
14
|
name: :attribute,
|
@@ -1,13 +1,9 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'base_conditions_filter'
|
2
2
|
|
3
3
|
module Gitlab
|
4
4
|
module Triage
|
5
|
-
module
|
6
|
-
class
|
7
|
-
def self.limiter_parameters
|
8
|
-
[]
|
9
|
-
end
|
10
|
-
|
5
|
+
module Filters
|
6
|
+
class ForbiddenLabelsConditionsFilter < BaseConditionsFilter
|
11
7
|
def validate_condition(condition)
|
12
8
|
raise ArgumentError, 'condition must be an array containing forbidden label values' unless condition.is_a?(Array)
|
13
9
|
end
|
data/lib/gitlab/triage/{limiters/member_conditions_limiter.rb → filters/member_conditions_filter.rb}
RENAMED
@@ -1,10 +1,10 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'base_conditions_filter'
|
2
2
|
require_relative '../url_builders/url_builder'
|
3
3
|
|
4
4
|
module Gitlab
|
5
5
|
module Triage
|
6
|
-
module
|
7
|
-
class
|
6
|
+
module Filters
|
7
|
+
class MemberConditionsFilter < BaseConditionsFilter
|
8
8
|
SOURCES = %w[project group].freeze
|
9
9
|
CONDITIONS = %w[member_of not_member_of].freeze
|
10
10
|
|
@@ -14,7 +14,7 @@ module Gitlab
|
|
14
14
|
super(resource, condition)
|
15
15
|
end
|
16
16
|
|
17
|
-
def self.
|
17
|
+
def self.filter_parameters
|
18
18
|
[
|
19
19
|
{
|
20
20
|
name: :source,
|
@@ -61,7 +61,7 @@ module Gitlab
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def members
|
64
|
-
@members ||= @network.
|
64
|
+
@members ||= @network.query_api_cached(@net[:token], member_url)
|
65
65
|
end
|
66
66
|
|
67
67
|
def member_url
|
data/lib/gitlab/triage/{limiters/name_conditions_limiter.rb → filters/name_conditions_filter.rb}
RENAMED
@@ -1,13 +1,9 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'base_conditions_filter'
|
2
2
|
|
3
3
|
module Gitlab
|
4
4
|
module Triage
|
5
|
-
module
|
6
|
-
class
|
7
|
-
def self.limiter_parameters
|
8
|
-
[]
|
9
|
-
end
|
10
|
-
|
5
|
+
module Filters
|
6
|
+
class NameConditionsFilter < BaseConditionsFilter
|
11
7
|
def initialize_variables(matching_name)
|
12
8
|
@attribute = :name
|
13
9
|
@matching_name = matching_name
|
@@ -1,10 +1,10 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'base_conditions_filter'
|
2
2
|
|
3
3
|
module Gitlab
|
4
4
|
module Triage
|
5
|
-
module
|
6
|
-
class
|
7
|
-
def self.
|
5
|
+
module Filters
|
6
|
+
class NoAdditionalLabelsConditionsFilter < BaseConditionsFilter
|
7
|
+
def self.filter_parameters
|
8
8
|
[]
|
9
9
|
end
|
10
10
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'base_conditions_filter'
|
2
|
+
require_relative '../resource/context'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
module Gitlab
|
6
|
+
module Triage
|
7
|
+
module Filters
|
8
|
+
class RubyConditionsFilter < BaseConditionsFilter
|
9
|
+
def self.limiter_parameters
|
10
|
+
[{ name: :ruby, type: String }]
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(resource, condition, net = {})
|
14
|
+
super(resource, condition)
|
15
|
+
|
16
|
+
@net = net
|
17
|
+
end
|
18
|
+
|
19
|
+
def calculate
|
20
|
+
!!Resource::Context.new(@resource, @net).eval(@expression)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def initialize_variables(condition)
|
26
|
+
@expression = condition[:ruby]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/gitlab/triage/{limiters/votes_conditions_limiter.rb → filters/votes_conditions_filter.rb}
RENAMED
@@ -1,13 +1,13 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'base_conditions_filter'
|
2
2
|
|
3
3
|
module Gitlab
|
4
4
|
module Triage
|
5
|
-
module
|
6
|
-
class
|
5
|
+
module Filters
|
6
|
+
class VotesConditionsFilter < BaseConditionsFilter
|
7
7
|
ATTRIBUTES = %w[upvotes downvotes].freeze
|
8
8
|
CONDITIONS = %w[greater_than less_than].freeze
|
9
9
|
|
10
|
-
def self.
|
10
|
+
def self.filter_parameters
|
11
11
|
[
|
12
12
|
{
|
13
13
|
name: :attribute,
|
@@ -14,6 +14,11 @@ module Gitlab
|
|
14
14
|
def initialize(adapter, options = {})
|
15
15
|
@adapter = adapter
|
16
16
|
@options = options
|
17
|
+
@cache = Hash.new { |hash, key| hash[key] = {} }
|
18
|
+
end
|
19
|
+
|
20
|
+
def query_api_cached(token, url)
|
21
|
+
@cache.dig(token, url) || @cache[token][url] = query_api(token, url)
|
17
22
|
end
|
18
23
|
|
19
24
|
def query_api(token, url)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative '../url_builders/url_builder'
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
module Triage
|
5
|
+
module Resource
|
6
|
+
class Base
|
7
|
+
attr_reader :resource, :net
|
8
|
+
|
9
|
+
def initialize(new_resource, new_net)
|
10
|
+
@resource = new_resource
|
11
|
+
@net = new_net
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def network
|
17
|
+
net[:network]
|
18
|
+
end
|
19
|
+
|
20
|
+
def url(params = {})
|
21
|
+
UrlBuilders::UrlBuilder.new(
|
22
|
+
net_opts.merge(params: { per_page: 100 }.merge(params))
|
23
|
+
).build
|
24
|
+
end
|
25
|
+
|
26
|
+
def net_opts
|
27
|
+
{
|
28
|
+
host_url: net[:host_url],
|
29
|
+
api_version: net[:api_version],
|
30
|
+
resource_type: self.class.name.demodulize.underscore.pluralize,
|
31
|
+
source: source,
|
32
|
+
source_id: resource[:"#{source.singularize}_id"]
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def source
|
37
|
+
if resource[:project_id]
|
38
|
+
'projects'
|
39
|
+
elsif resource[:group_id]
|
40
|
+
'groups'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require_relative 'milestone'
|
3
|
+
|
4
|
+
module Gitlab
|
5
|
+
module Triage
|
6
|
+
module Resource
|
7
|
+
class Context < Base
|
8
|
+
EvaluationError = Class.new(RuntimeError)
|
9
|
+
|
10
|
+
def eval(ruby)
|
11
|
+
instance_eval <<~RUBY
|
12
|
+
begin
|
13
|
+
#{ruby}
|
14
|
+
rescue StandardError, ScriptError => e
|
15
|
+
raise EvaluationError.new(e.message)
|
16
|
+
end
|
17
|
+
RUBY
|
18
|
+
rescue EvaluationError => e
|
19
|
+
# This way we could obtain the original backtrace and error
|
20
|
+
# If we just let instance_eval raise an error, the backtrace
|
21
|
+
# won't contain the actual line where it's giving an error.
|
22
|
+
raise e.cause
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def milestone
|
28
|
+
@milestone ||=
|
29
|
+
resource[:milestone] && Milestone.new(resource[:milestone], net)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require 'date'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module Gitlab
|
6
|
+
module Triage
|
7
|
+
module Resource
|
8
|
+
class Milestone < Base
|
9
|
+
FIELDS = %i[
|
10
|
+
id
|
11
|
+
iid
|
12
|
+
project_id
|
13
|
+
group_id
|
14
|
+
title
|
15
|
+
description
|
16
|
+
state
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
DATE_FIELDS = %i[
|
20
|
+
due_date
|
21
|
+
start_date
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
TIME_FIELDS = %i[
|
25
|
+
updated_at
|
26
|
+
created_at
|
27
|
+
].freeze
|
28
|
+
|
29
|
+
FIELDS.each do |field|
|
30
|
+
define_method(field) do
|
31
|
+
resource[field]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
DATE_FIELDS.each do |field|
|
36
|
+
define_method(field) do
|
37
|
+
value = resource[field]
|
38
|
+
|
39
|
+
Date.parse(value) if value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
TIME_FIELDS.each do |field|
|
44
|
+
define_method(field) do
|
45
|
+
value = resource[field]
|
46
|
+
|
47
|
+
Time.parse(value) if value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def succ
|
52
|
+
index = current_index
|
53
|
+
|
54
|
+
all_active_with_start_date[index.succ] if index
|
55
|
+
end
|
56
|
+
|
57
|
+
def active?
|
58
|
+
state == 'active'
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def current_index
|
64
|
+
all_active_with_start_date
|
65
|
+
.index { |milestone| milestone.id == id }
|
66
|
+
end
|
67
|
+
|
68
|
+
def all_active_with_start_date
|
69
|
+
@all_active_with_start_date ||=
|
70
|
+
all_active.select(&:start_date).sort_by(&:start_date)
|
71
|
+
end
|
72
|
+
|
73
|
+
def all_active
|
74
|
+
@all_active ||=
|
75
|
+
network
|
76
|
+
.query_api_cached(net[:token], url(state: 'active'))
|
77
|
+
.map { |milestone| self.class.new(milestone, net) }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
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: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitLab
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08-
|
11
|
+
date: 2018-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '3.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: webmock
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.4'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.4'
|
97
111
|
description:
|
98
112
|
email:
|
99
113
|
- remy@rymai.me
|
@@ -117,6 +131,9 @@ files:
|
|
117
131
|
- bin/gitlab-triage
|
118
132
|
- gitlab-triage.gemspec
|
119
133
|
- lib/gitlab/triage.rb
|
134
|
+
- lib/gitlab/triage/api_query_builders/base_query_param_builder.rb
|
135
|
+
- lib/gitlab/triage/api_query_builders/multi_query_param_builder.rb
|
136
|
+
- lib/gitlab/triage/api_query_builders/single_query_param_builder.rb
|
120
137
|
- lib/gitlab/triage/command_builders/base_command_builder.rb
|
121
138
|
- lib/gitlab/triage/command_builders/cc_command_builder.rb
|
122
139
|
- lib/gitlab/triage/command_builders/comment_body_builder.rb
|
@@ -125,22 +142,26 @@ files:
|
|
125
142
|
- lib/gitlab/triage/command_builders/remove_label_command_builder.rb
|
126
143
|
- lib/gitlab/triage/command_builders/status_command_builder.rb
|
127
144
|
- lib/gitlab/triage/engine.rb
|
128
|
-
- lib/gitlab/triage/
|
129
|
-
- lib/gitlab/triage/
|
130
|
-
- lib/gitlab/triage/
|
131
|
-
- lib/gitlab/triage/
|
132
|
-
- lib/gitlab/triage/
|
133
|
-
- lib/gitlab/triage/
|
134
|
-
- lib/gitlab/triage/
|
135
|
-
- lib/gitlab/triage/
|
136
|
-
- lib/gitlab/triage/
|
137
|
-
- lib/gitlab/triage/
|
138
|
-
- lib/gitlab/triage/
|
139
|
-
- lib/gitlab/triage/
|
145
|
+
- lib/gitlab/triage/expand_condition.rb
|
146
|
+
- lib/gitlab/triage/expand_condition/sequence.rb
|
147
|
+
- lib/gitlab/triage/expand_condition/sequence/expansion.rb
|
148
|
+
- lib/gitlab/triage/filters/assignee_member_conditions_filter.rb
|
149
|
+
- lib/gitlab/triage/filters/author_member_conditions_filter.rb
|
150
|
+
- lib/gitlab/triage/filters/base_conditions_filter.rb
|
151
|
+
- lib/gitlab/triage/filters/date_conditions_filter.rb
|
152
|
+
- lib/gitlab/triage/filters/forbidden_labels_conditions_filter.rb
|
153
|
+
- lib/gitlab/triage/filters/member_conditions_filter.rb
|
154
|
+
- lib/gitlab/triage/filters/name_conditions_filter.rb
|
155
|
+
- lib/gitlab/triage/filters/no_additional_labels_conditions_filter.rb
|
156
|
+
- lib/gitlab/triage/filters/ruby_conditions_filter.rb
|
157
|
+
- lib/gitlab/triage/filters/votes_conditions_filter.rb
|
140
158
|
- lib/gitlab/triage/network.rb
|
141
159
|
- lib/gitlab/triage/network_adapters/base_adapter.rb
|
142
160
|
- lib/gitlab/triage/network_adapters/httparty_adapter.rb
|
143
161
|
- lib/gitlab/triage/network_adapters/test_adapter.rb
|
162
|
+
- lib/gitlab/triage/resource/base.rb
|
163
|
+
- lib/gitlab/triage/resource/context.rb
|
164
|
+
- lib/gitlab/triage/resource/milestone.rb
|
144
165
|
- lib/gitlab/triage/retryable.rb
|
145
166
|
- lib/gitlab/triage/ui.rb
|
146
167
|
- lib/gitlab/triage/url_builders/url_builder.rb
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module Gitlab
|
2
|
-
module Triage
|
3
|
-
module FilterBuilders
|
4
|
-
class BaseFilterBuilder
|
5
|
-
attr_reader :filter_name, :filter_contents
|
6
|
-
|
7
|
-
def initialize(filter_name, filter_contents)
|
8
|
-
@filter_name = filter_name
|
9
|
-
@filter_contents = filter_contents
|
10
|
-
end
|
11
|
-
|
12
|
-
def build_filter
|
13
|
-
"&#{filter_name}=#{filter_content}"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require_relative 'base_filter_builder'
|
2
|
-
|
3
|
-
module Gitlab
|
4
|
-
module Triage
|
5
|
-
module FilterBuilders
|
6
|
-
class MultiFilterBuilder < BaseFilterBuilder
|
7
|
-
attr_reader :separator
|
8
|
-
|
9
|
-
def initialize(filter_name, filter_contents, separator)
|
10
|
-
@separator = separator
|
11
|
-
super(filter_name, filter_contents)
|
12
|
-
end
|
13
|
-
|
14
|
-
def filter_content
|
15
|
-
filter_contents.join(separator)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|