plan_my_stuff 0.8.0 → 0.10.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/CHANGELOG.md +31 -0
- data/CONFIGURATION.md +351 -0
- data/app/views/plan_my_stuff/issues/show.html.erb +1 -1
- data/app/views/plan_my_stuff/partials/_flash.html.erb +0 -1
- data/lib/generators/plan_my_stuff/install/templates/initializer.rb +1 -12
- data/lib/plan_my_stuff/base_project.rb +5 -176
- data/lib/plan_my_stuff/base_project_extractions/graphql_hydration.rb +184 -0
- data/lib/plan_my_stuff/base_project_item.rb +1 -0
- data/lib/plan_my_stuff/comment.rb +5 -3
- data/lib/plan_my_stuff/configuration.rb +3 -16
- data/lib/plan_my_stuff/issue.rb +15 -1082
- data/lib/plan_my_stuff/issue_extractions/approvals.rb +370 -0
- data/lib/plan_my_stuff/issue_extractions/links.rb +525 -0
- data/lib/plan_my_stuff/issue_extractions/viewers.rb +75 -0
- data/lib/plan_my_stuff/issue_extractions/waiting.rb +148 -0
- data/lib/plan_my_stuff/label.rb +4 -4
- data/lib/plan_my_stuff/version.rb +1 -1
- data/lib/tasks/plan_my_stuff.rake +2 -2
- metadata +8 -2
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlanMyStuff
|
|
4
|
+
module BaseProjectExtractions
|
|
5
|
+
module GraphqlHydration
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
# Builds a summary Project from a list query node. Dispatches to TestingProject when the readme metadata has
|
|
9
|
+
# kind: "testing".
|
|
10
|
+
#
|
|
11
|
+
# @param node [Hash]
|
|
12
|
+
#
|
|
13
|
+
# @return [PlanMyStuff::BaseProject]
|
|
14
|
+
#
|
|
15
|
+
def build_summary(node)
|
|
16
|
+
raw_readme = node[:readme] || ''
|
|
17
|
+
parsed_meta = PlanMyStuff::MetadataParser.parse(raw_readme)
|
|
18
|
+
klass = dispatch_project_class(parsed_meta[:metadata])
|
|
19
|
+
project = klass.new
|
|
20
|
+
project.__send__(:hydrate_summary, node, raw_readme: raw_readme, parsed_meta: parsed_meta)
|
|
21
|
+
project
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Builds a detailed Project from a find query response. Dispatches to TestingProject when the readme metadata
|
|
25
|
+
# has kind: "testing".
|
|
26
|
+
#
|
|
27
|
+
# @param graphql_project [Hash]
|
|
28
|
+
# @param items [Array<Hash>]
|
|
29
|
+
# @param next_cursor [String, nil]
|
|
30
|
+
# @param has_next_page [Boolean, nil]
|
|
31
|
+
#
|
|
32
|
+
# @return [PlanMyStuff::BaseProject]
|
|
33
|
+
#
|
|
34
|
+
def build_detail(graphql_project, items:, next_cursor: nil, has_next_page: nil)
|
|
35
|
+
raw_readme = graphql_project[:readme] || ''
|
|
36
|
+
parsed_meta = PlanMyStuff::MetadataParser.parse(raw_readme)
|
|
37
|
+
klass = dispatch_project_class(parsed_meta[:metadata])
|
|
38
|
+
project = klass.new
|
|
39
|
+
project.__send__(
|
|
40
|
+
:hydrate_detail,
|
|
41
|
+
graphql_project,
|
|
42
|
+
items: items,
|
|
43
|
+
next_cursor: next_cursor,
|
|
44
|
+
has_next_page: has_next_page,
|
|
45
|
+
)
|
|
46
|
+
project
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @param org [String]
|
|
50
|
+
# @param number [Integer]
|
|
51
|
+
#
|
|
52
|
+
# @return [PlanMyStuff::BaseProject]
|
|
53
|
+
#
|
|
54
|
+
def find_auto_paginated(org, number)
|
|
55
|
+
all_items = []
|
|
56
|
+
cursor = nil
|
|
57
|
+
raw_project = nil
|
|
58
|
+
page = nil
|
|
59
|
+
|
|
60
|
+
loop do
|
|
61
|
+
page = fetch_project_page(org, number, cursor)
|
|
62
|
+
raw_project ||= page[:raw]
|
|
63
|
+
all_items.concat(page[:items])
|
|
64
|
+
|
|
65
|
+
break if !page[:has_next_page] || all_items.length >= PlanMyStuff::BaseProject::MAX_AUTO_PAGINATE_ITEMS
|
|
66
|
+
|
|
67
|
+
cursor = page[:next_cursor]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
build_detail(
|
|
71
|
+
raw_project,
|
|
72
|
+
items: all_items,
|
|
73
|
+
next_cursor: page[:next_cursor],
|
|
74
|
+
has_next_page: page[:has_next_page],
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @param org [String]
|
|
79
|
+
# @param number [Integer]
|
|
80
|
+
# @param cursor [String, nil]
|
|
81
|
+
#
|
|
82
|
+
# @return [PlanMyStuff::BaseProject]
|
|
83
|
+
#
|
|
84
|
+
def find_with_cursor(org, number, cursor:)
|
|
85
|
+
page = fetch_project_page(org, number, cursor)
|
|
86
|
+
build_detail(
|
|
87
|
+
page[:raw],
|
|
88
|
+
items: page[:items],
|
|
89
|
+
next_cursor: page[:next_cursor],
|
|
90
|
+
has_next_page: page[:has_next_page],
|
|
91
|
+
)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Fetches a single page of project data. Returns a lightweight hash for pagination loop consumption (not a
|
|
95
|
+
# Project instance).
|
|
96
|
+
#
|
|
97
|
+
# @param org [String]
|
|
98
|
+
# @param number [Integer]
|
|
99
|
+
# @param cursor [String, nil]
|
|
100
|
+
#
|
|
101
|
+
# @return [Hash] with :raw, :items, :next_cursor, :has_next_page
|
|
102
|
+
#
|
|
103
|
+
def fetch_project_page(org, number, cursor)
|
|
104
|
+
variables = { org: org, number: number }
|
|
105
|
+
variables[:cursor] = cursor if cursor
|
|
106
|
+
|
|
107
|
+
data = PlanMyStuff.client.graphql(
|
|
108
|
+
PlanMyStuff::GraphQL::Queries::FIND_PROJECT,
|
|
109
|
+
variables: variables,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
raw_project = data.dig(:organization, :projectV2)
|
|
113
|
+
page_info = raw_project.dig(:items, :pageInfo) || {}
|
|
114
|
+
items_data = raw_project.dig(:items, :nodes) || []
|
|
115
|
+
|
|
116
|
+
{
|
|
117
|
+
raw: raw_project,
|
|
118
|
+
items: items_data.map { |item| parse_project_item(item) },
|
|
119
|
+
next_cursor: page_info[:endCursor],
|
|
120
|
+
has_next_page: page_info[:hasNextPage],
|
|
121
|
+
}
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# @param item [Hash] raw GraphQL project item node
|
|
125
|
+
#
|
|
126
|
+
# @return [Hash]
|
|
127
|
+
#
|
|
128
|
+
def parse_project_item(item)
|
|
129
|
+
content = item[:content] || {}
|
|
130
|
+
field_values = item.dig(:fieldValues, :nodes) || []
|
|
131
|
+
repo_name = content.dig(:repository, :nameWithOwner)
|
|
132
|
+
|
|
133
|
+
{
|
|
134
|
+
id: item[:id],
|
|
135
|
+
type: item[:type],
|
|
136
|
+
content_node_id: content[:id],
|
|
137
|
+
title: content[:title],
|
|
138
|
+
body: content[:body],
|
|
139
|
+
number: content[:number],
|
|
140
|
+
url: content[:url],
|
|
141
|
+
state: content[:state],
|
|
142
|
+
repo: repo_name.present? ? PlanMyStuff::Repo.resolve!(repo_name) : nil,
|
|
143
|
+
status: extract_item_status(field_values),
|
|
144
|
+
field_values: parse_field_values(field_values),
|
|
145
|
+
updated_at: item[:updatedAt],
|
|
146
|
+
github_response: item,
|
|
147
|
+
}
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# @param field_values [Array<Hash>]
|
|
151
|
+
#
|
|
152
|
+
# @return [String, nil]
|
|
153
|
+
#
|
|
154
|
+
def extract_item_status(field_values)
|
|
155
|
+
status_value = field_values.find { |fv| fv.dig(:field, :name) == 'Status' }
|
|
156
|
+
|
|
157
|
+
status_value&.dig(:name)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# @param field_values [Array<Hash>]
|
|
161
|
+
#
|
|
162
|
+
# @return [Hash]
|
|
163
|
+
#
|
|
164
|
+
def parse_field_values(field_values)
|
|
165
|
+
result = {}
|
|
166
|
+
|
|
167
|
+
field_values.each do |fv|
|
|
168
|
+
field_name = fv.dig(:field, :name)
|
|
169
|
+
next unless field_name
|
|
170
|
+
|
|
171
|
+
value = fv[:name] || fv[:text]
|
|
172
|
+
users_node = fv[:users]
|
|
173
|
+
if users_node
|
|
174
|
+
value = (users_node[:nodes] || []).map { |u| u[:login] }
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
result[field_name] = value
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
result
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -32,6 +32,8 @@ module PlanMyStuff
|
|
|
32
32
|
class << self
|
|
33
33
|
# Creates a comment on a GitHub issue with PMS metadata and a visible header.
|
|
34
34
|
#
|
|
35
|
+
# @raise [PlanMyStuff::LockedIssueError] if the parent issue is locked
|
|
36
|
+
#
|
|
35
37
|
# @param issue [PlanMyStuff::Issue] parent issue
|
|
36
38
|
# @param body [String]
|
|
37
39
|
# @param user [Object, Integer] user object or user_id
|
|
@@ -41,8 +43,6 @@ module PlanMyStuff
|
|
|
41
43
|
# @param waiting_on_reply [Boolean] when true and the author is a support user, marks the issue as waiting on
|
|
42
44
|
# an end-user reply. Ignored for non-support authors.
|
|
43
45
|
#
|
|
44
|
-
# @raise [PlanMyStuff::LockedIssueError] if the parent issue is locked
|
|
45
|
-
#
|
|
46
46
|
# @return [PlanMyStuff::Comment]
|
|
47
47
|
#
|
|
48
48
|
def create!(
|
|
@@ -279,7 +279,9 @@ module PlanMyStuff
|
|
|
279
279
|
)
|
|
280
280
|
hydrate_from_comment(created)
|
|
281
281
|
else
|
|
282
|
-
|
|
282
|
+
update_attrs = { user: user, body: body }
|
|
283
|
+
update_attrs[:visibility] = visibility if visibility_changed?
|
|
284
|
+
update!(**update_attrs)
|
|
283
285
|
end
|
|
284
286
|
|
|
285
287
|
self
|
|
@@ -84,19 +84,6 @@ module PlanMyStuff
|
|
|
84
84
|
#
|
|
85
85
|
attr_accessor :markdown_options
|
|
86
86
|
|
|
87
|
-
# Proc returning boolean, or nil (always send). When it returns false the request is deferred to a background job
|
|
88
|
-
# instead of hitting GitHub.
|
|
89
|
-
#
|
|
90
|
-
# @return [Proc, nil]
|
|
91
|
-
#
|
|
92
|
-
attr_accessor :should_send_request
|
|
93
|
-
|
|
94
|
-
# Map of action type to job class name for deferred requests. Keys: :create_ticket, :post_comment, :update_status.
|
|
95
|
-
#
|
|
96
|
-
# @return [Hash{Symbol => String}]
|
|
97
|
-
#
|
|
98
|
-
attr_accessor :job_classes
|
|
99
|
-
|
|
100
87
|
# Fallback actor for notification events when a caller does not pass +user:+. Set to a proc/lambda that returns the
|
|
101
88
|
# current request user.
|
|
102
89
|
#
|
|
@@ -344,11 +331,12 @@ module PlanMyStuff
|
|
|
344
331
|
#
|
|
345
332
|
attr_accessor :sns_verifier_error
|
|
346
333
|
|
|
347
|
-
# Named repo configs. Set via config.repos[:element] = 'BrandsInsurance/Element'
|
|
334
|
+
# Named repo configs. Set via config.repos[:element] = 'BrandsInsurance/Element', or assign a whole hash with
|
|
335
|
+
# config.repos = { element: 'BrandsInsurance/Element', underwriter: 'BrandsInsurance/Underwriter' }.
|
|
348
336
|
#
|
|
349
337
|
# @return [Hash{Symbol => String}]
|
|
350
338
|
#
|
|
351
|
-
|
|
339
|
+
attr_accessor :repos
|
|
352
340
|
|
|
353
341
|
# @return [Configuration]
|
|
354
342
|
def initialize
|
|
@@ -359,7 +347,6 @@ module PlanMyStuff
|
|
|
359
347
|
@support_method = :support?
|
|
360
348
|
@markdown_renderer = :commonmarker
|
|
361
349
|
@markdown_options = {}
|
|
362
|
-
@job_classes = {}
|
|
363
350
|
@custom_fields = {}
|
|
364
351
|
@issue_custom_fields = {}
|
|
365
352
|
@comment_custom_fields = {}
|