payping-gitlab-triage 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,17 @@
1
+ require_relative '../version'
2
+
3
+ module Gitlab
4
+ module Triage
5
+ module NetworkAdapters
6
+ class BaseAdapter
7
+ USER_AGENT = "GitLab Triage #{Gitlab::Triage::VERSION}".freeze
8
+
9
+ attr_reader :options
10
+
11
+ def initialize(options)
12
+ @options = options
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,92 @@
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
+ headers = response.extensions.fetch('headers', {})
21
+
22
+ graphql_response = {
23
+ ratelimit_remaining: headers['ratelimit-remaining'].to_i,
24
+ ratelimit_reset_at: Time.at(headers['ratelimit-reset'].to_i)
25
+ }
26
+
27
+ return graphql_response.merge(results: {}) if parsed_response.nil?
28
+ return graphql_response.merge(results: parsed_response.map(&:to_h)) if parsed_response.is_a?(Client::List)
29
+ return graphql_response.merge(results: parsed_response.to_h) unless parsed_response.nodes?
30
+
31
+ graphql_response.merge(
32
+ more_pages: parsed_response.page_info.has_next_page,
33
+ end_cursor: parsed_response.page_info.end_cursor,
34
+ results: parsed_response.nodes.map(&:to_h)
35
+ )
36
+ end
37
+
38
+ delegate :parse, to: :client
39
+
40
+ private
41
+
42
+ def parse_response(response, resource_path)
43
+ resource_path.reduce(response.data) { |data, resource| data&.send(resource) }
44
+ end
45
+
46
+ def raise_on_error!(response)
47
+ return if response.errors.blank?
48
+
49
+ puts Gitlab::Triage::UI.debug response.inspect if options.debug
50
+
51
+ raise "There was an error: #{response.errors.messages.to_json}"
52
+ end
53
+
54
+ def http_client
55
+ Client::HTTP.new("#{options.host_url}/api/graphql") do
56
+ def execute(document:, operation_name: nil, variables: {}, context: {}) # rubocop:disable Lint/NestedMethodDefinition
57
+ body = {}
58
+ body['query'] = document.to_query_string
59
+ body['variables'] = variables if variables.any?
60
+ body['operationName'] = operation_name if operation_name
61
+
62
+ response = HTTParty.post(
63
+ uri,
64
+ body: body.to_json,
65
+ headers: {
66
+ 'User-Agent' => USER_AGENT,
67
+ 'Content-type' => 'application/json',
68
+ 'PRIVATE-TOKEN' => context[:token]
69
+ }
70
+ )
71
+
72
+ case response.code
73
+ when 200, 400
74
+ JSON.parse(response.body).merge('extensions' => { 'headers' => response.headers })
75
+ else
76
+ { 'errors' => [{ 'message' => "#{response.code} #{response.message}" }] }
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def schema
83
+ @schema ||= Client.load_schema(http_client)
84
+ end
85
+
86
+ def client
87
+ @client ||= Client.new(schema: schema, execute: http_client).tap { |client| client.allow_dynamic_queries = true }
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,116 @@
1
+ require 'httparty'
2
+
3
+ require_relative 'base_adapter'
4
+ require_relative '../ui'
5
+ require_relative '../errors'
6
+
7
+ module Gitlab
8
+ module Triage
9
+ module NetworkAdapters
10
+ class HttpartyAdapter < BaseAdapter
11
+ def get(token, url)
12
+ response = HTTParty.get(
13
+ url,
14
+ headers: {
15
+ 'User-Agent' => USER_AGENT,
16
+ 'Content-type' => 'application/json',
17
+ 'PRIVATE-TOKEN' => token
18
+ }
19
+ )
20
+
21
+ raise_on_unauthorized_error!(response)
22
+ raise_on_internal_server_error!(response)
23
+ raise_on_too_many_requests!(response)
24
+
25
+ {
26
+ more_pages: (response.headers["x-next-page"].to_s != ""),
27
+ next_page_url: next_page_url(url, response),
28
+ results: response.parsed_response,
29
+ ratelimit_remaining: response.headers["ratelimit-remaining"].to_i,
30
+ ratelimit_reset_at: Time.at(response.headers["ratelimit-reset"].to_i)
31
+ }
32
+ end
33
+
34
+ def post(token, url, body)
35
+ response = HTTParty.post(
36
+ url,
37
+ body: body.to_json,
38
+ headers: {
39
+ 'User-Agent' => "GitLab Triage #{Gitlab::Triage::VERSION}",
40
+ 'Content-type' => 'application/json',
41
+ 'PRIVATE-TOKEN' => token
42
+ }
43
+ )
44
+
45
+ raise_on_unauthorized_error!(response)
46
+ raise_on_internal_server_error!(response)
47
+ raise_on_too_many_requests!(response)
48
+
49
+ {
50
+ results: response.parsed_response,
51
+ ratelimit_remaining: response.headers["ratelimit-remaining"].to_i,
52
+ ratelimit_reset_at: Time.at(response.headers["ratelimit-reset"].to_i)
53
+ }
54
+ end
55
+
56
+ def delete(token, url)
57
+ response = HTTParty.delete(
58
+ url,
59
+ headers: {
60
+ 'User-Agent' => USER_AGENT,
61
+ 'PRIVATE-TOKEN' => token
62
+ }
63
+ )
64
+
65
+ raise_on_unauthorized_error!(response)
66
+ raise_on_internal_server_error!(response)
67
+ raise_on_too_many_requests!(response)
68
+
69
+ {
70
+ results: response.parsed_response,
71
+ ratelimit_remaining: response.headers["ratelimit-remaining"].to_i,
72
+ ratelimit_reset_at: Time.at(response.headers["ratelimit-reset"].to_i)
73
+ }
74
+ end
75
+
76
+ private
77
+
78
+ def raise_on_unauthorized_error!(response)
79
+ return unless response.response.is_a?(Net::HTTPUnauthorized)
80
+
81
+ puts Gitlab::Triage::UI.debug response.inspect if options.debug
82
+
83
+ raise 'The provided token is unauthorized!'
84
+ end
85
+
86
+ def raise_on_internal_server_error!(response)
87
+ return unless response.response.is_a?(Net::HTTPInternalServerError)
88
+
89
+ puts Gitlab::Triage::UI.debug response.inspect if options.debug
90
+
91
+ raise Errors::Network::InternalServerError, 'Internal server error encountered!'
92
+ end
93
+
94
+ def raise_on_too_many_requests!(response)
95
+ return unless response.response.is_a?(Net::HTTPTooManyRequests)
96
+
97
+ puts Gitlab::Triage::UI.debug response.inspect if options.debug
98
+
99
+ raise Errors::Network::TooManyRequests, 'Too many requests made!'
100
+ end
101
+
102
+ def next_page_url(url, response)
103
+ return unless response.headers['x-next-page'].present?
104
+
105
+ next_page = "&page=#{response.headers['x-next-page']}"
106
+
107
+ if url.include?('&page')
108
+ url.gsub(/&page=\d+/, next_page)
109
+ else
110
+ url + next_page
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,39 @@
1
+ require 'httparty'
2
+
3
+ require_relative 'base_adapter'
4
+
5
+ module Gitlab
6
+ module Triage
7
+ module NetworkAdapters
8
+ class TestAdapter < BaseAdapter
9
+ def get(_token, url)
10
+ results =
11
+ case url
12
+ when %r{\Ahttps://gitlab.com/v4/issues?per_page=100}
13
+ [
14
+ { id: 1, title: 'First issue' }
15
+ ]
16
+ else
17
+ []
18
+ end
19
+
20
+ {
21
+ more_pages: false,
22
+ next_page_url: nil,
23
+ results: results,
24
+ ratelimit_remaining: 600,
25
+ ratelimit_reset_at: Time.now
26
+ }
27
+ end
28
+
29
+ def post(token, url, body)
30
+ {
31
+ results: {},
32
+ ratelimit_remaining: 600,
33
+ ratelimit_reset_at: Time.now
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal
2
+
3
+ require 'optparse'
4
+ require 'fileutils'
5
+ require_relative 'options'
6
+
7
+ module Gitlab
8
+ module Triage
9
+ class OptionParser
10
+ class << self
11
+ # rubocop:disable Metrics/AbcSize
12
+ def parse(argv)
13
+ options = Options.new
14
+ options.host_url = 'https://gitlab.com'
15
+
16
+ parser = ::OptionParser.new do |opts|
17
+ opts.banner = "Usage: gitlab-triage [options]\n\n"
18
+
19
+ opts.on('-n', '--dry-run', "Don't actually update anything, just print") do |value|
20
+ options.dry_run = value
21
+ end
22
+
23
+ opts.on('-f', '--policies-file [string]', String, 'A valid policies YML file') do |value|
24
+ options.policies_files << value
25
+ end
26
+
27
+ opts.on('--all-projects', 'Process all projects the token has access to') do |value|
28
+ options.all = value
29
+ end
30
+
31
+ opts.on('-s', '--source [type]', [:projects, :groups], 'The source type between [ projects or groups ], default value: projects') do |value|
32
+ options.source = value
33
+ end
34
+
35
+ opts.on('-i', '--source-id [string]', String, 'Source ID or path') do |value|
36
+ options.source_id = value
37
+ end
38
+
39
+ opts.on('-p', '--project-id [string]', String, '[Deprecated] A project ID or path, please use `--source-id`') do |value|
40
+ puts Gitlab::Triage::UI.warn("The option `--project-id` has been deprecated, please use `--source-id` instead")
41
+ puts
42
+ options.source = 'projects'
43
+ options.source_id = value
44
+ end
45
+
46
+ opts.on('--resource-reference [string]', String, 'Resource short-reference, e.g. #42, !33, or &99') do |value|
47
+ options.resource_reference = value
48
+ end
49
+
50
+ opts.on('-t', '--token [string]', String, 'A valid API token') do |value|
51
+ options.token = value
52
+ end
53
+
54
+ opts.on('-H', '--host-url [string]', String, 'A valid host url') do |value|
55
+ options.host_url = value
56
+ end
57
+
58
+ opts.on('-r', '--require [string]', String, 'Require a file before performing') do |value|
59
+ options.require_files << value
60
+ end
61
+
62
+ opts.on('-d', '--debug', 'Print debug information') do |value|
63
+ options.debug = value
64
+ end
65
+
66
+ opts.on('-h', '--help', 'Print help message') do
67
+ $stdout.puts opts
68
+ exit # rubocop:disable Rails/Exit
69
+ end
70
+
71
+ opts.on('-v', '--version', 'Print version') do
72
+ require_relative 'version'
73
+ $stdout.puts Gitlab::Triage::VERSION
74
+ exit # rubocop:disable Rails/Exit
75
+ end
76
+
77
+ opts.on('--init', 'Initialize the project with a policy file') do
78
+ example_path =
79
+ File.expand_path('../../../support/.triage-policies.example.yml', __dir__)
80
+
81
+ FileUtils.cp(example_path, '.triage-policies.yml')
82
+ exit # rubocop:disable Rails/Exit
83
+ end
84
+
85
+ opts.on('--init-ci', 'Initialize the project with a .gitlab-ci.yml file') do
86
+ example_path =
87
+ File.expand_path('../../../support/.gitlab-ci.example.yml', __dir__)
88
+
89
+ FileUtils.cp(example_path, '.gitlab-ci.yml')
90
+ exit # rubocop:disable Rails/Exit
91
+ end
92
+ end
93
+
94
+ parser.parse!(argv)
95
+
96
+ options.source = nil if options.all
97
+ options.token ||= ''
98
+
99
+ options
100
+ end
101
+ # rubocop:enable Metrics/AbcSize
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,30 @@
1
+ module Gitlab
2
+ module Triage
3
+ Options = Struct.new(
4
+ :dry_run,
5
+ :policies_files,
6
+ :resources,
7
+ :all,
8
+ :source,
9
+ :source_id,
10
+ :resource_reference,
11
+ :token,
12
+ :debug,
13
+ :host_url,
14
+ :require_files,
15
+ :api_version
16
+ ) do
17
+ def initialize(*args)
18
+ super
19
+
20
+ # Defaults
21
+ self.host_url ||= 'https://gitlab.com'
22
+ self.api_version ||= 'v4'
23
+ self.all ||= false
24
+ self.source ||= 'projects'
25
+ self.require_files ||= []
26
+ self.policies_files ||= Set.new
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,64 @@
1
+ require_relative '../validators/params_validator'
2
+
3
+ module Gitlab
4
+ module Triage
5
+ module ParamBuilders
6
+ class DateParamBuilder
7
+ CONDITIONS = %w[older_than newer_than].freeze
8
+ TIME_BASED_INTERVALS = %w[minutes hours].freeze
9
+ DATE_BASED_INTERVALS = %w[days weeks months years].freeze
10
+ INTERVAL_TYPES = TIME_BASED_INTERVALS + DATE_BASED_INTERVALS
11
+
12
+ def initialize(allowed_attributes, condition_hash)
13
+ @allowed_attributes = allowed_attributes
14
+ @attribute = condition_hash[:attribute].to_s
15
+ @interval_condition = condition_hash[:condition].to_sym
16
+ @interval_type = condition_hash[:interval_type]
17
+ @interval = condition_hash[:interval]
18
+
19
+ validate_condition(condition_hash)
20
+ end
21
+
22
+ def param_content
23
+ if TIME_BASED_INTERVALS.include?(interval_type)
24
+ interval.public_send(interval_type).ago.to_datetime # rubocop:disable GitlabSecurity/PublicSend
25
+ else
26
+ interval.public_send(interval_type).ago.to_date # rubocop:disable GitlabSecurity/PublicSend
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :allowed_attributes, :attribute, :interval_condition, :interval_type, :interval
33
+
34
+ def validate_condition(condition)
35
+ ParamsValidator.new(filter_parameters, condition).validate!
36
+ end
37
+
38
+ def filter_parameters
39
+ [
40
+ {
41
+ name: :attribute,
42
+ type: String,
43
+ values: allowed_attributes
44
+ },
45
+ {
46
+ name: :condition,
47
+ type: String,
48
+ values: CONDITIONS
49
+ },
50
+ {
51
+ name: :interval_type,
52
+ type: String,
53
+ values: INTERVAL_TYPES
54
+ },
55
+ {
56
+ name: :interval,
57
+ type: Numeric
58
+ }
59
+ ]
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module Triage
5
+ module Policies
6
+ class BasePolicy
7
+ InvalidPolicyError = Class.new(StandardError)
8
+
9
+ attr_reader :type, :policy_spec, :resources, :network
10
+ attr_accessor :summary
11
+
12
+ def initialize(type, policy_spec, resources, network)
13
+ @type = type
14
+ @policy_spec = policy_spec
15
+ @resources = resources
16
+ @network = network
17
+ end
18
+
19
+ def validate!
20
+ raise InvalidPolicyError, 'Policies that comment_on_summary must include summarize action' if comment_on_summary? && !summarize?
21
+ end
22
+
23
+ def name
24
+ @name ||= (policy_spec[:name] || "#{type}-#{object_id}")
25
+ end
26
+
27
+ def source
28
+ case type
29
+ when 'epics'
30
+ 'groups'
31
+ else
32
+ 'projects'
33
+ end
34
+ end
35
+
36
+ def source_id_sym
37
+ case type
38
+ when 'epics'
39
+ :group_id
40
+ else
41
+ :project_id
42
+ end
43
+ end
44
+
45
+ def actions
46
+ @actions ||= policy_spec.fetch(:actions) { {} }
47
+ end
48
+
49
+ def summarize?
50
+ actions.key?(:summarize)
51
+ end
52
+
53
+ def comment_on_summary?
54
+ actions.key?(:comment_on_summary)
55
+ end
56
+
57
+ def comment?
58
+ # The actual keys are strings
59
+ (actions.keys.map(&:to_sym) - [:summarize, :comment_on_summary, :delete, :issue]).any?
60
+ end
61
+
62
+ def issue?
63
+ actions.key?(:issue)
64
+ end
65
+
66
+ def delete?
67
+ actions.key?(:delete) && actions[:delete]
68
+ end
69
+
70
+ def build_issue
71
+ raise NotImplementedError
72
+ end
73
+
74
+ def build_summary
75
+ raise NotImplementedError
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_policy'
4
+ require_relative '../entity_builders/issue_builder'
5
+ require_relative '../entity_builders/summary_builder'
6
+
7
+ module Gitlab
8
+ module Triage
9
+ module Policies
10
+ class RulePolicy < BasePolicy
11
+ # Build a summary from a single rule policy
12
+ def build_summary
13
+ action = actions.fetch(:summarize, {})
14
+
15
+ EntityBuilders::SummaryBuilder.new(
16
+ type: type,
17
+ policy_spec: policy_spec,
18
+ action: action,
19
+ resources: resources,
20
+ network: network)
21
+ end
22
+
23
+ def build_issue(resource)
24
+ action = actions.fetch(:issue, {})
25
+
26
+ EntityBuilders::IssueBuilder.new(
27
+ type: type,
28
+ policy_spec: policy_spec,
29
+ action: action,
30
+ resource: resource,
31
+ network: network)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_policy'
4
+ require_relative '../entity_builders/summary_builder'
5
+
6
+ module Gitlab
7
+ module Triage
8
+ module Policies
9
+ class SummaryPolicy < BasePolicy
10
+ # Build a summary from several rules policies
11
+ def build_summary
12
+ action = actions[:summarize]
13
+ issues = resources.map do |inner_policy_spec, inner_resources|
14
+ Policies::RulePolicy.new(
15
+ type, inner_policy_spec, inner_resources, network)
16
+ .build_summary
17
+ end
18
+
19
+ EntityBuilders::SummaryBuilder.new(
20
+ type: type,
21
+ action: action,
22
+ resources: issues.select(&:any_resources?),
23
+ network: network,
24
+ separator: "\n\n")
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+
5
+ module Gitlab
6
+ module Triage
7
+ module PoliciesResources
8
+ RuleResources = Class.new(SimpleDelegator)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+
5
+ module Gitlab
6
+ module Triage
7
+ module PoliciesResources
8
+ SummaryResources = Class.new(SimpleDelegator)
9
+ end
10
+ end
11
+ end