payping-gitlab-triage 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +19 -0
  3. data/.gitignore +15 -0
  4. data/.gitlab/CODEOWNERS +2 -0
  5. data/.gitlab/changelog_config.yml +13 -0
  6. data/.gitlab/issue_templates/Default.md +13 -0
  7. data/.gitlab/merge_request_templates/Default.md +11 -0
  8. data/.gitlab/merge_request_templates/Release.md +13 -0
  9. data/.gitlab-ci.yml +146 -0
  10. data/.rubocop.yml +21 -0
  11. data/.rubocop_todo.yml +145 -0
  12. data/.ruby-version +1 -0
  13. data/.tool-versions +1 -0
  14. data/.yardopts +4 -0
  15. data/CONTRIBUTING.md +31 -0
  16. data/Dangerfile +5 -0
  17. data/Gemfile +15 -0
  18. data/Guardfile +70 -0
  19. data/LICENSE.md +25 -0
  20. data/README.md +1480 -0
  21. data/Rakefile +6 -0
  22. data/bin/gitlab-triage +19 -0
  23. data/gitlab-triage.gemspec +41 -0
  24. data/lib/gitlab/triage/action/base.rb +14 -0
  25. data/lib/gitlab/triage/action/comment.rb +104 -0
  26. data/lib/gitlab/triage/action/comment_on_summary.rb +83 -0
  27. data/lib/gitlab/triage/action/delete.rb +56 -0
  28. data/lib/gitlab/triage/action/issue.rb +64 -0
  29. data/lib/gitlab/triage/action/summarize.rb +82 -0
  30. data/lib/gitlab/triage/action.rb +36 -0
  31. data/lib/gitlab/triage/api_query_builders/base_query_param_builder.rb +27 -0
  32. data/lib/gitlab/triage/api_query_builders/date_query_param_builder.rb +42 -0
  33. data/lib/gitlab/triage/api_query_builders/multi_query_param_builder.rb +28 -0
  34. data/lib/gitlab/triage/api_query_builders/single_query_param_builder.rb +13 -0
  35. data/lib/gitlab/triage/command_builders/base_command_builder.rb +40 -0
  36. data/lib/gitlab/triage/command_builders/cc_command_builder.rb +19 -0
  37. data/lib/gitlab/triage/command_builders/comment_command_builder.rb +19 -0
  38. data/lib/gitlab/triage/command_builders/label_command_builder.rb +40 -0
  39. data/lib/gitlab/triage/command_builders/move_command_builder.rb +19 -0
  40. data/lib/gitlab/triage/command_builders/remove_label_command_builder.rb +15 -0
  41. data/lib/gitlab/triage/command_builders/status_command_builder.rb +23 -0
  42. data/lib/gitlab/triage/command_builders/text_content_builder.rb +138 -0
  43. data/lib/gitlab/triage/engine.rb +635 -0
  44. data/lib/gitlab/triage/entity_builders/issue_builder.rb +54 -0
  45. data/lib/gitlab/triage/entity_builders/summary_builder.rb +82 -0
  46. data/lib/gitlab/triage/errors/network.rb +11 -0
  47. data/lib/gitlab/triage/errors.rb +1 -0
  48. data/lib/gitlab/triage/expand_condition/expansion.rb +203 -0
  49. data/lib/gitlab/triage/expand_condition/list.rb +25 -0
  50. data/lib/gitlab/triage/expand_condition/sequence.rb +25 -0
  51. data/lib/gitlab/triage/expand_condition.rb +23 -0
  52. data/lib/gitlab/triage/filters/assignee_member_conditions_filter.rb +13 -0
  53. data/lib/gitlab/triage/filters/author_member_conditions_filter.rb +13 -0
  54. data/lib/gitlab/triage/filters/base_conditions_filter.rb +58 -0
  55. data/lib/gitlab/triage/filters/branch_date_filter.rb +73 -0
  56. data/lib/gitlab/triage/filters/branch_protected_filter.rb +26 -0
  57. data/lib/gitlab/triage/filters/discussions_conditions_filter.rb +58 -0
  58. data/lib/gitlab/triage/filters/issue_date_conditions_filter.rb +78 -0
  59. data/lib/gitlab/triage/filters/member_conditions_filter.rb +84 -0
  60. data/lib/gitlab/triage/filters/merge_request_date_conditions_filter.rb +13 -0
  61. data/lib/gitlab/triage/filters/name_conditions_filter.rb +26 -0
  62. data/lib/gitlab/triage/filters/no_additional_labels_conditions_filter.rb +30 -0
  63. data/lib/gitlab/triage/filters/ruby_conditions_filter.rb +33 -0
  64. data/lib/gitlab/triage/filters/votes_conditions_filter.rb +54 -0
  65. data/lib/gitlab/triage/graphql_network.rb +81 -0
  66. data/lib/gitlab/triage/graphql_queries/query_builder.rb +158 -0
  67. data/lib/gitlab/triage/graphql_queries/query_param_builders/base_param_builder.rb +30 -0
  68. data/lib/gitlab/triage/graphql_queries/query_param_builders/date_param_builder.rb +35 -0
  69. data/lib/gitlab/triage/graphql_queries/query_param_builders/labels_param_builder.rb +18 -0
  70. data/lib/gitlab/triage/limiters/base_limiter.rb +35 -0
  71. data/lib/gitlab/triage/limiters/date_field_limiter.rb +45 -0
  72. data/lib/gitlab/triage/network.rb +39 -0
  73. data/lib/gitlab/triage/network_adapters/base_adapter.rb +17 -0
  74. data/lib/gitlab/triage/network_adapters/graphql_adapter.rb +92 -0
  75. data/lib/gitlab/triage/network_adapters/httparty_adapter.rb +116 -0
  76. data/lib/gitlab/triage/network_adapters/test_adapter.rb +39 -0
  77. data/lib/gitlab/triage/option_parser.rb +105 -0
  78. data/lib/gitlab/triage/options.rb +30 -0
  79. data/lib/gitlab/triage/param_builders/date_param_builder.rb +64 -0
  80. data/lib/gitlab/triage/policies/base_policy.rb +80 -0
  81. data/lib/gitlab/triage/policies/rule_policy.rb +36 -0
  82. data/lib/gitlab/triage/policies/summary_policy.rb +29 -0
  83. data/lib/gitlab/triage/policies_resources/rule_resources.rb +11 -0
  84. data/lib/gitlab/triage/policies_resources/summary_resources.rb +11 -0
  85. data/lib/gitlab/triage/resource/base.rb +102 -0
  86. data/lib/gitlab/triage/resource/branch.rb +13 -0
  87. data/lib/gitlab/triage/resource/context.rb +47 -0
  88. data/lib/gitlab/triage/resource/epic.rb +20 -0
  89. data/lib/gitlab/triage/resource/instance_version.rb +35 -0
  90. data/lib/gitlab/triage/resource/issue.rb +52 -0
  91. data/lib/gitlab/triage/resource/label.rb +56 -0
  92. data/lib/gitlab/triage/resource/label_event.rb +48 -0
  93. data/lib/gitlab/triage/resource/linked_issue.rb +15 -0
  94. data/lib/gitlab/triage/resource/merge_request.rb +23 -0
  95. data/lib/gitlab/triage/resource/milestone.rb +98 -0
  96. data/lib/gitlab/triage/resource/shared/issuable.rb +119 -0
  97. data/lib/gitlab/triage/rest_api_network.rb +125 -0
  98. data/lib/gitlab/triage/retryable.rb +33 -0
  99. data/lib/gitlab/triage/ui.rb +23 -0
  100. data/lib/gitlab/triage/url_builders/url_builder.rb +54 -0
  101. data/lib/gitlab/triage/utils.rb +13 -0
  102. data/lib/gitlab/triage/validators/limiter_validator.rb +21 -0
  103. data/lib/gitlab/triage/validators/params_validator.rb +43 -0
  104. data/lib/gitlab/triage/version.rb +7 -0
  105. data/lib/gitlab/triage.rb +6 -0
  106. data/support/.gitlab-ci.example.yml +22 -0
  107. data/support/.triage-policies.example.yml +51 -0
  108. metadata +280 -0
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/bin/gitlab-triage ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require_relative '../lib/gitlab/triage/option_parser'
5
+ require_relative '../lib/gitlab/triage/engine'
6
+ require_relative '../lib/gitlab/triage/ui'
7
+
8
+ options = Gitlab::Triage::OptionParser.parse(ARGV)
9
+ options.policies_files << '.triage-policies.yml' if options.policies_files.empty?
10
+
11
+ options.policies_files.each do |policies_file|
12
+ policies = HashWithIndifferentAccess.new(YAML.load_file(policies_file))
13
+
14
+ policy_engine = Gitlab::Triage::Engine
15
+ .new(policies: policies, options: options)
16
+
17
+ puts Gitlab::Triage::UI.header("Executing policies from #{policies_file}.", char: '*')
18
+ policy_engine.perform
19
+ end
@@ -0,0 +1,41 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'gitlab/triage/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'payping-gitlab-triage'
7
+ spec.version = Gitlab::Triage::VERSION
8
+ spec.authors = ['GitLab']
9
+ spec.email = ['gitlab_rubygems@gitlab.com']
10
+
11
+ spec.summary = 'GitLab triage automation project.'
12
+ spec.homepage = 'https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage'
13
+ spec.license = 'MIT'
14
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
15
+
16
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
17
+
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = "https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage"
20
+ spec.metadata["changelog_uri"] = "https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage/-/releases"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match?(%r{^(docs?|spec|tmp)/}) }
26
+ end
27
+ spec.bindir = 'bin'
28
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
29
+ spec.require_paths = ['lib']
30
+
31
+ spec.add_dependency 'activesupport', '>= 5.1'
32
+ spec.add_dependency 'globalid', '~> 0.4'
33
+ spec.add_dependency 'graphql-client', '~> 0.16'
34
+ spec.add_dependency 'httparty', '~> 0.17'
35
+
36
+ spec.add_development_dependency 'bundler'
37
+ spec.add_development_dependency 'gitlab-styles', '~> 2.8'
38
+ spec.add_development_dependency 'rake', '~> 10.2'
39
+ spec.add_development_dependency 'rspec', '~> 3.8'
40
+ spec.add_development_dependency 'webmock', '~> 3.4'
41
+ end
@@ -0,0 +1,14 @@
1
+ module Gitlab
2
+ module Triage
3
+ module Action
4
+ class Base
5
+ attr_reader :policy, :network
6
+
7
+ def initialize(policy:, network:)
8
+ @policy = policy
9
+ @network = network
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative '../command_builders/text_content_builder'
5
+ require_relative '../command_builders/comment_command_builder'
6
+ require_relative '../command_builders/label_command_builder'
7
+ require_relative '../command_builders/remove_label_command_builder'
8
+ require_relative '../command_builders/cc_command_builder'
9
+ require_relative '../command_builders/status_command_builder'
10
+ require_relative '../command_builders/move_command_builder'
11
+
12
+ module Gitlab
13
+ module Triage
14
+ module Action
15
+ class Comment < Base
16
+ class Dry < Comment
17
+ def act
18
+ puts "The following comments would be posted for the rule **#{policy.name}**:\n\n"
19
+
20
+ super
21
+ end
22
+
23
+ private
24
+
25
+ def perform(resource, comment)
26
+ puts "# #{resource[:web_url]}\n```\n#{comment}\n```\n"
27
+ end
28
+ end
29
+
30
+ def act
31
+ if policy.type == 'branches'
32
+ puts Gitlab::Triage::UI.warn "Comment actions are not available for branches. They will NOT be performed\n\n"
33
+ return
34
+ end
35
+
36
+ policy.resources.each do |resource|
37
+ comment = build_comment(resource).strip
38
+
39
+ perform(resource, comment) unless comment.empty?
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def build_comment(resource)
46
+ CommandBuilders::CommentCommandBuilder.new(
47
+ [
48
+ CommandBuilders::TextContentBuilder.new(policy.actions[:comment], resource: resource, network: network).build_command,
49
+ CommandBuilders::LabelCommandBuilder.new(policy.actions[:labels], resource: resource, network: network).build_command,
50
+ CommandBuilders::RemoveLabelCommandBuilder.new(policy.actions[:remove_labels], resource: resource, network: network).build_command,
51
+ CommandBuilders::CcCommandBuilder.new(policy.actions[:mention]).build_command,
52
+ CommandBuilders::MoveCommandBuilder.new(policy.actions[:move]).build_command,
53
+ CommandBuilders::StatusCommandBuilder.new(policy.actions[:status]).build_command
54
+ ]
55
+ ).build_command
56
+ end
57
+
58
+ def perform(resource, comment)
59
+ network.post_api(
60
+ build_post_url(resource),
61
+ body: comment)
62
+ end
63
+
64
+ def build_post_url(resource)
65
+ url_builder_opts = {
66
+ network_options: network.options,
67
+ source: policy.source,
68
+ source_id: resource[policy.source_id_sym],
69
+ resource_type: policy.type,
70
+ resource_id: resource_id(resource),
71
+ sub_resource_type: sub_resource_type
72
+ }
73
+
74
+ # POST /(groups|projects)/:id/(epics|issues|merge_requests)/:iid/notes
75
+ post_url = UrlBuilders::UrlBuilder.new(url_builder_opts).build
76
+
77
+ puts Gitlab::Triage::UI.debug "post_url: #{post_url}" if network.options.debug
78
+
79
+ post_url
80
+ end
81
+
82
+ def sub_resource_type
83
+ case type = policy.actions[:comment_type]
84
+ when 'comment', nil # nil is default
85
+ 'notes'
86
+ when 'thread'
87
+ 'discussions'
88
+ else
89
+ raise ArgumentError, "Unknown comment type: #{type}"
90
+ end
91
+ end
92
+
93
+ def resource_id(resource)
94
+ case policy.type
95
+ when 'epics'
96
+ resource['id']
97
+ else
98
+ resource['iid']
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative '../command_builders/text_content_builder'
5
+ require_relative '../command_builders/comment_command_builder'
6
+ require_relative '../command_builders/label_command_builder'
7
+ require_relative '../command_builders/remove_label_command_builder'
8
+ require_relative '../command_builders/cc_command_builder'
9
+ require_relative '../command_builders/status_command_builder'
10
+ require_relative '../command_builders/move_command_builder'
11
+
12
+ module Gitlab
13
+ module Triage
14
+ module Action
15
+ class CommentOnSummary < Base
16
+ class Dry < CommentOnSummary
17
+ def act
18
+ puts "The following comments would be posted for the rule **#{policy.name}**:\n\n"
19
+
20
+ super
21
+ end
22
+
23
+ private
24
+
25
+ def perform(comment)
26
+ puts "# #{summary[:web_url]}\n```\n#{comment}\n```\n"
27
+ end
28
+ end
29
+
30
+ attr_reader :summary
31
+
32
+ def initialize(policy:, network:)
33
+ super(policy: policy, network: network)
34
+ @summary = policy.summary
35
+ end
36
+
37
+ def act
38
+ policy.resources.each do |resource|
39
+ comment = build_comment(resource).strip
40
+
41
+ perform(comment) unless comment.empty?
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def build_comment(resource)
48
+ CommandBuilders::TextContentBuilder.new(policy.actions[:comment_on_summary], resource: resource, network: network).build_command
49
+ end
50
+
51
+ def perform(comment)
52
+ network.post_api(build_post_url, body: comment)
53
+ end
54
+
55
+ def build_post_url
56
+ # POST /projects/:id/issues/:issue_iid/notes
57
+ post_url = UrlBuilders::UrlBuilder.new(
58
+ network_options: network.options,
59
+ source_id: summary['project_id'],
60
+ resource_type: policy.type,
61
+ resource_id: summary['iid'],
62
+ sub_resource_type: sub_resource_type
63
+ ).build
64
+
65
+ puts Gitlab::Triage::UI.debug "post_url: #{post_url}" if network.options.debug
66
+
67
+ post_url
68
+ end
69
+
70
+ def sub_resource_type
71
+ case type = policy.actions[:comment_type]
72
+ when 'comment', nil # nil is default
73
+ 'notes'
74
+ when 'thread'
75
+ 'discussions'
76
+ else
77
+ raise ArgumentError, "Unknown comment type: #{type}"
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Gitlab
6
+ module Triage
7
+ module Action
8
+ class Delete < Base
9
+ class Dry < Delete
10
+ def act
11
+ puts "The following resources will be deleted by the rule **#{policy.name}**:\n\n"
12
+ super
13
+ end
14
+
15
+ private
16
+
17
+ def perform(resource)
18
+ puts "DELETE resource with type: #{resource[:type]} and id: #{resource_id(resource)}"
19
+ end
20
+ end
21
+
22
+ def act
23
+ return unless policy.type&.to_sym == :branches
24
+
25
+ policy.resources.each do |resource|
26
+ perform(resource)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def perform(resource)
33
+ network.delete_api(build_delete_url(resource))
34
+ end
35
+
36
+ def build_delete_url(resource)
37
+ delete_url = UrlBuilders::UrlBuilder.new(
38
+ source: policy.source,
39
+ source_id: network.options.source_id,
40
+ resource_type: policy.type,
41
+ resource_id: resource_id(resource),
42
+ network_options: network.options
43
+ ).build
44
+
45
+ puts Gitlab::Triage::UI.debug "delete_url: #{delete_url}" if network.options.debug
46
+
47
+ delete_url
48
+ end
49
+
50
+ def resource_id(resource)
51
+ resource[:name]
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Gitlab
6
+ module Triage
7
+ module Action
8
+ class Issue < Base
9
+ class Dry < Issue
10
+ def act
11
+ puts "The following issues would be created for the rule **#{policy.name}**:\n\n"
12
+
13
+ super
14
+ end
15
+
16
+ private
17
+
18
+ def perform(resource, issue)
19
+ puts ">>>"
20
+ puts "* Project: #{issue.destination || resource[policy.source_id_sym]}"
21
+ puts "* Title: #{issue.title}"
22
+ puts "* Description: #{issue.description}"
23
+ puts ">>>"
24
+ end
25
+ end
26
+
27
+ def act
28
+ policy.resources.each do |resource|
29
+ issue = policy.build_issue(resource)
30
+
31
+ perform(resource, issue) if issue.valid?
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def perform(resource, issue)
38
+ network.post_api(build_post_url(resource, issue), post_issue_body(issue))
39
+ end
40
+
41
+ def build_post_url(resource, issue)
42
+ # POST /projects/:id/issues
43
+ # https://docs.gitlab.com/ee/api/issues.html#new-issue
44
+ post_url = UrlBuilders::UrlBuilder.new(
45
+ network_options: network.options,
46
+ source_id: issue.destination || resource[policy.source_id_sym],
47
+ resource_type: 'issues'
48
+ ).build
49
+
50
+ puts Gitlab::Triage::UI.debug "post_issue_url: #{post_url}" if network.options.debug
51
+
52
+ post_url
53
+ end
54
+
55
+ def post_issue_body(issue)
56
+ {
57
+ title: issue.title,
58
+ description: issue.description
59
+ }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Gitlab
6
+ module Triage
7
+ module Action
8
+ class Summarize < Base
9
+ class Dry < Summarize
10
+ private
11
+
12
+ def perform
13
+ policy.summary = {
14
+ web_url: '[the-created-issue-url]',
15
+ project_id: 'some-id',
16
+ iid: 'some-iid'
17
+ }.with_indifferent_access
18
+
19
+ if group_summary_without_destination?
20
+ puts Gitlab::Triage::UI.warn("No issue will be created: No summary destination specified when source is 'groups'.")
21
+ return
22
+ end
23
+
24
+ puts "The following issue would be created in project `#{destination}` for the rule **#{policy.name}**:\n\n"
25
+ puts ">>>"
26
+ puts "* Title: #{issue.title}"
27
+ puts "* Description: #{issue.description}"
28
+ puts ">>>"
29
+ end
30
+ end
31
+
32
+ def act
33
+ perform if issue.valid?
34
+ end
35
+
36
+ private
37
+
38
+ def perform
39
+ if group_summary_without_destination?
40
+ puts Gitlab::Triage::UI.warn("Issue was not created: No summary destination specified when source is 'groups'.")
41
+ return
42
+ end
43
+
44
+ policy.summary = network.post_api(post_issue_url, post_issue_body)
45
+ end
46
+
47
+ def issue
48
+ @issue ||= policy.build_summary
49
+ end
50
+
51
+ def destination
52
+ issue.destination || network.options.source_id
53
+ end
54
+
55
+ def group_summary_without_destination?
56
+ network.options.source == :groups && !issue.destination
57
+ end
58
+
59
+ def post_issue_url
60
+ # POST /projects/:id/issues
61
+ # https://docs.gitlab.com/ee/api/issues.html#new-issue
62
+ post_url = UrlBuilders::UrlBuilder.new(
63
+ network_options: network.options,
64
+ source_id: destination,
65
+ resource_type: 'issues'
66
+ ).build
67
+
68
+ puts Gitlab::Triage::UI.debug "post_issue_url: #{post_url}" if network.options.debug
69
+
70
+ post_url
71
+ end
72
+
73
+ def post_issue_body
74
+ {
75
+ title: issue.title,
76
+ description: issue.description
77
+ }
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'action/summarize'
2
+ require_relative 'action/comment'
3
+ require_relative 'action/comment_on_summary'
4
+ require_relative 'action/issue'
5
+ require_relative 'action/delete'
6
+
7
+ module Gitlab
8
+ module Triage
9
+ module Action
10
+ def self.process(policy:, **args)
11
+ policy.validate!
12
+
13
+ [
14
+ [Summarize, policy.summarize?],
15
+ [Comment, policy.comment?],
16
+ [CommentOnSummary, policy.comment_on_summary?],
17
+ [Issue, policy.issue?],
18
+ [Delete, policy.delete?]
19
+ ].each do |action, active|
20
+ act(action: action, policy: policy, **args) if active
21
+ end
22
+ end
23
+
24
+ def self.act(action:, dry:, **args)
25
+ klass =
26
+ if dry
27
+ action.const_get(:Dry)
28
+ else
29
+ action
30
+ end
31
+
32
+ klass.new(**args).act
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ module Gitlab
2
+ module Triage
3
+ module APIQueryBuilders
4
+ class BaseQueryParamBuilder
5
+ attr_reader :param_name, :param_contents, :allowed_values
6
+
7
+ def initialize(param_name, param_contents, allowed_values: nil)
8
+ @param_name = param_name
9
+ @param_contents = param_contents
10
+ @allowed_values = allowed_values
11
+
12
+ validate_allowed_values! if allowed_values
13
+ end
14
+
15
+ def build_param
16
+ "&#{param_name}=#{param_content.strip}"
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
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,42 @@
1
+ require_relative '../param_builders/date_param_builder'
2
+ require_relative 'base_query_param_builder'
3
+
4
+ module Gitlab
5
+ module Triage
6
+ module APIQueryBuilders
7
+ class DateQueryParamBuilder < BaseQueryParamBuilder
8
+ ATTRIBUTES = %w[updated_at created_at].freeze
9
+
10
+ def self.applicable?(condition)
11
+ ATTRIBUTES.include?(condition[:attribute].to_s) &&
12
+ condition[:filter_in_ruby] != true
13
+ end
14
+
15
+ def initialize(condition_hash)
16
+ date_param_builder = ParamBuilders::DateParamBuilder.new(ATTRIBUTES, condition_hash)
17
+
18
+ super(build_param_name(condition_hash), date_param_builder.param_content)
19
+ end
20
+
21
+ def param_content
22
+ param_contents
23
+ end
24
+
25
+ private
26
+
27
+ def build_param_name(condition_hash)
28
+ prefix = condition_hash[:attribute].to_s.sub(/_at\z/, '')
29
+ suffix =
30
+ case condition_hash[:condition].to_sym
31
+ when :older_than
32
+ 'before'
33
+ when :newer_than
34
+ 'after'
35
+ end
36
+
37
+ "#{prefix}_#{suffix}"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,28 @@
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, allowed_values: nil)
10
+ @separator = separator
11
+ super(param_name, Array(param_contents), allowed_values: allowed_values)
12
+ end
13
+
14
+ def param_content
15
+ param_contents.map(&:strip).join(separator)
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
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'base_query_param_builder'
2
+
3
+ module Gitlab
4
+ module Triage
5
+ module APIQueryBuilders
6
+ class SingleQueryParamBuilder < BaseQueryParamBuilder
7
+ def param_content
8
+ param_contents
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,40 @@
1
+ module Gitlab
2
+ module Triage
3
+ module CommandBuilders
4
+ class BaseCommandBuilder
5
+ def initialize(items, resource: nil, network: nil)
6
+ @items = Array.wrap(items)
7
+ @items.delete('')
8
+ @resource = resource&.with_indifferent_access
9
+ @network = network
10
+ end
11
+
12
+ def build_command
13
+ if items.any?
14
+ [slash_command_string, content_string].compact.join(separator)
15
+ else
16
+ ""
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :items, :resource, :network
23
+
24
+ def separator
25
+ ' '
26
+ end
27
+
28
+ def slash_command_string
29
+ nil
30
+ end
31
+
32
+ def content_string
33
+ items.map do |item|
34
+ format_item(item)
35
+ end.join(separator)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'base_command_builder'
2
+
3
+ module Gitlab
4
+ module Triage
5
+ module CommandBuilders
6
+ class CcCommandBuilder < BaseCommandBuilder
7
+ private
8
+
9
+ def slash_command_string
10
+ "/cc"
11
+ end
12
+
13
+ def format_item(item)
14
+ "@#{item}"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end