plan_my_stuff 0.23.1 → 0.24.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 +15 -0
- data/lib/plan_my_stuff/issue.rb +150 -8
- data/lib/plan_my_stuff/version.rb +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1cf33c92cfa763f5ace87a1419cf8c7369f27e8adc10d1c092c044b99d151a4a
|
|
4
|
+
data.tar.gz: 3b2a482534b9f9094d550385c064f50f4dc37db3c2a6361e4aa899d4b4dc5713
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ef36dbcfb77d4c3763d2165993c50fb9f4f7e8b0729a1efc1d63fb9155abb039bf9db92fb59a49d3302ebfec024bafc19dd15b54be84a2d7919040405a10803f
|
|
7
|
+
data.tar.gz: beae8f4926a95e4a47786c4ffefc886300f255466d7e08a7e607f444e91883e920b3bf60d666d33b539eed2dac7a1241f64cd23ae42ebe292a8a113ce150de8d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.24.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `PlanMyStuff::Issue.list` and `PlanMyStuff::Issue.count` now accept `issue_type:` (String / Symbol) and
|
|
8
|
+
`issue_fields:` (Hash keyed by display name) filter kwargs. `issue_type:` resolves symbol nicknames through
|
|
9
|
+
`ISSUE_TYPE_NICKNAMES` and `config.issue_types`; Arrays raise `ArgumentError` since GitHub's REST `type` param
|
|
10
|
+
and Search `type:` qualifier each accept only one value. `issue_fields:` accepts scalar (equality) or `Range`
|
|
11
|
+
(date / numeric bounds -- inclusive
|
|
12
|
+
`..` -> `>=`/`<=`, exclusive `...` -> `>=`/`<`, beginless / endless ranges drop the unbounded side) values and ANDs
|
|
13
|
+
multiple constraints together. Composes with the existing `priority_list:` filter into the same
|
|
14
|
+
`issue_field_values` (REST) / `field.<slug>:` (Search) qualifier list. Search-API calls flip
|
|
15
|
+
`advanced_search=true` when any field qualifier is present. `issue_fields:` raises
|
|
16
|
+
`IssueFieldsNotEnabledError` when `config.issue_fields_enabled` is `false` (closes #54).
|
|
17
|
+
|
|
3
18
|
## 0.23.1
|
|
4
19
|
|
|
5
20
|
### Added
|
data/lib/plan_my_stuff/issue.rb
CHANGED
|
@@ -296,11 +296,29 @@ module PlanMyStuff
|
|
|
296
296
|
|
|
297
297
|
# Lists GitHub issues with optional filters and pagination.
|
|
298
298
|
#
|
|
299
|
-
#
|
|
299
|
+
# +issue_fields:+ is a Hash keyed by GitHub Issue Field display name (String / Symbol). Each value is either
|
|
300
|
+
# a scalar (equality match -- Date / Time are emitted as ISO 8601, everything else as +to_s+) or a +Range+ for
|
|
301
|
+
# numeric / date bounds:
|
|
302
|
+
#
|
|
303
|
+
# - +Date.parse('2026-01-01')..Date.today+ -> +start-date:>=2026-01-01,start-date:<=2026-05-21+
|
|
304
|
+
# - +Date.parse('2026-01-01')...Date.today+ -> +start-date:>=2026-01-01,start-date:<2026-05-21+ (exclusive end)
|
|
305
|
+
# - +..Date.today+ (beginless) / +Date.parse('2026-01-01')..+ (endless) drop the unbounded side
|
|
306
|
+
#
|
|
307
|
+
# Multiple field constraints AND together. Composes with the existing +priority_list:+ filter: both feed the
|
|
308
|
+
# same +issue_field_values+ query param.
|
|
309
|
+
#
|
|
310
|
+
# @raise [ArgumentError] when +priority_list: false+ is passed, or when +issue_type:+ is an Array (GitHub's
|
|
311
|
+
# REST +type+ param only accepts a single value)
|
|
312
|
+
# @raise [PlanMyStuff::IssueFieldsNotEnabledError] when +issue_fields:+ is passed and
|
|
313
|
+
# +config.issue_fields_enabled+ is +false+
|
|
300
314
|
#
|
|
301
315
|
# @param repo [Symbol, String, nil] defaults to config.default_repo
|
|
302
316
|
# @param state [Symbol] :open, :closed, or :all
|
|
303
317
|
# @param labels [Array<String>]
|
|
318
|
+
# @param issue_type [String, Symbol, nil] a single GitHub issue type name. Symbols resolve through
|
|
319
|
+
# +ISSUE_TYPE_NICKNAMES+ then +config.issue_types+ for org-specific renames.
|
|
320
|
+
# @param issue_fields [Hash{String,Symbol => Object,Range,nil}, nil] GitHub Issue Field equality / range
|
|
321
|
+
# filters. See description for the value shapes the gem accepts.
|
|
304
322
|
# @param priority_list [Boolean, nil] when +true+, restricts to issues whose +Priority List+ issue field is
|
|
305
323
|
# +Yes+ (server-side filter via the +issue_field_values+ query param). +false+ raises +ArgumentError+ -- GitHub
|
|
306
324
|
# has no negation qualifier. Silently dropped when +config.issue_fields_enabled+ is +false+.
|
|
@@ -309,19 +327,38 @@ module PlanMyStuff
|
|
|
309
327
|
#
|
|
310
328
|
# @return [Array<PlanMyStuff::Issue>]
|
|
311
329
|
#
|
|
312
|
-
def list(
|
|
330
|
+
def list(
|
|
331
|
+
repo: nil,
|
|
332
|
+
state: :open,
|
|
333
|
+
labels: [],
|
|
334
|
+
issue_type: nil,
|
|
335
|
+
issue_fields: nil,
|
|
336
|
+
priority_list: nil,
|
|
337
|
+
page: 1,
|
|
338
|
+
per_page: 25
|
|
339
|
+
)
|
|
313
340
|
if priority_list == false
|
|
314
341
|
raise(ArgumentError, 'priority_list: false is not supported (no GitHub negation qualifier)')
|
|
315
342
|
end
|
|
343
|
+
if issue_fields.present? && !PlanMyStuff.configuration.issue_fields_enabled
|
|
344
|
+
raise(PlanMyStuff::IssueFieldsNotEnabledError)
|
|
345
|
+
end
|
|
316
346
|
|
|
317
347
|
client = PlanMyStuff.client
|
|
318
348
|
resolved_repo = client.resolve_repo!(repo)
|
|
319
349
|
|
|
320
350
|
params = { state: state.to_s, page: page, per_page: per_page }
|
|
321
351
|
params[:labels] = labels.sort.join(',') if labels.present?
|
|
352
|
+
|
|
353
|
+
resolved_type = resolve_issue_types_filter(issue_type)
|
|
354
|
+
params[:type] = resolved_type if resolved_type.present?
|
|
355
|
+
|
|
356
|
+
field_pairs = []
|
|
357
|
+
field_pairs.concat(build_issue_field_filter_pairs(issue_fields)) if issue_fields.present?
|
|
322
358
|
if priority_list && PlanMyStuff.configuration.issue_fields_enabled
|
|
323
|
-
|
|
359
|
+
field_pairs << 'priority-list:Yes'
|
|
324
360
|
end
|
|
361
|
+
params[:issue_field_values] = field_pairs.join(',') if field_pairs.present?
|
|
325
362
|
|
|
326
363
|
github_issues = client.rest(:list_issues, resolved_repo, **params)
|
|
327
364
|
filtered = github_issues.reject { |gi| gi.respond_to?(:pull_request) && gi.pull_request }
|
|
@@ -347,11 +384,18 @@ module PlanMyStuff
|
|
|
347
384
|
# - The Search API has its own rate limit (30 req/min authenticated) separate from
|
|
348
385
|
# the core REST API.
|
|
349
386
|
#
|
|
350
|
-
# @raise [ArgumentError] when +priority_list: false+ is passed
|
|
387
|
+
# @raise [ArgumentError] when +priority_list: false+ is passed, or when +issue_type:+ is an Array
|
|
388
|
+
# @raise [PlanMyStuff::IssueFieldsNotEnabledError] when +issue_fields:+ is passed and
|
|
389
|
+
# +config.issue_fields_enabled+ is +false+
|
|
351
390
|
#
|
|
352
391
|
# @param repo [Symbol, String, nil] defaults to config.default_repo
|
|
353
392
|
# @param state [Symbol] :open, :closed, or :all
|
|
354
393
|
# @param labels [Array<String>]
|
|
394
|
+
# @param issue_type [String, Symbol, nil] a single GitHub issue type name. Symbols resolve through
|
|
395
|
+
# +ISSUE_TYPE_NICKNAMES+ then +config.issue_types+.
|
|
396
|
+
# @param issue_fields [Hash{String,Symbol => Object,Range,nil}, nil] GitHub Issue Field equality / range
|
|
397
|
+
# filters. See +.list+ for the value shapes the gem accepts. Each pair is emitted as a
|
|
398
|
+
# +field.<slug>:<value>+ Search qualifier and triggers +advanced_search=true+.
|
|
355
399
|
# @param priority_list [Boolean, nil] when +true+, restricts to issues whose +Priority List+ issue field is
|
|
356
400
|
# +Yes+ (server-side filter via the +field.priority-list:Yes+ Search qualifier). +false+ raises
|
|
357
401
|
# +ArgumentError+ -- GitHub has no negation qualifier. Silently dropped when
|
|
@@ -359,10 +403,13 @@ module PlanMyStuff
|
|
|
359
403
|
#
|
|
360
404
|
# @return [Integer]
|
|
361
405
|
#
|
|
362
|
-
def count(repo: nil, state: :open, labels: [], priority_list: nil)
|
|
406
|
+
def count(repo: nil, state: :open, labels: [], issue_type: nil, issue_fields: nil, priority_list: nil)
|
|
363
407
|
if priority_list == false
|
|
364
408
|
raise(ArgumentError, 'priority_list: false is not supported (no GitHub negation qualifier)')
|
|
365
409
|
end
|
|
410
|
+
if issue_fields.present? && !PlanMyStuff.configuration.issue_fields_enabled
|
|
411
|
+
raise(PlanMyStuff::IssueFieldsNotEnabledError)
|
|
412
|
+
end
|
|
366
413
|
|
|
367
414
|
client = PlanMyStuff.client
|
|
368
415
|
resolved_repo = client.resolve_repo!(repo)
|
|
@@ -374,11 +421,19 @@ module PlanMyStuff
|
|
|
374
421
|
qualifiers += labels_to_use.map do |label|
|
|
375
422
|
"label:\"#{label}\""
|
|
376
423
|
end
|
|
377
|
-
|
|
424
|
+
|
|
425
|
+
resolved_type = resolve_issue_types_filter(issue_type)
|
|
426
|
+
qualifiers << "type:#{resolved_type}" if resolved_type.present?
|
|
427
|
+
|
|
428
|
+
field_pairs = []
|
|
429
|
+
field_pairs.concat(build_issue_field_filter_pairs(issue_fields)) if issue_fields.present?
|
|
378
430
|
if priority_list && PlanMyStuff.configuration.issue_fields_enabled
|
|
379
|
-
|
|
380
|
-
search_options[:advanced_search] = true
|
|
431
|
+
field_pairs << 'priority-list:Yes'
|
|
381
432
|
end
|
|
433
|
+
qualifiers += field_pairs.map { |pair| "field.#{pair}" }
|
|
434
|
+
|
|
435
|
+
search_options = { per_page: 1 }
|
|
436
|
+
search_options[:advanced_search] = true if field_pairs.present?
|
|
382
437
|
client.rest(:search_issues, qualifiers.join(' '), **search_options).total_count
|
|
383
438
|
end
|
|
384
439
|
|
|
@@ -528,6 +583,93 @@ module PlanMyStuff
|
|
|
528
583
|
PlanMyStuff.configuration.issue_types[canonical] || canonical
|
|
529
584
|
end
|
|
530
585
|
|
|
586
|
+
# Resolves an +issue_type:+ filter kwarg (used by +.list+ / +.count+) into a single canonical type name.
|
|
587
|
+
# Accepts a scalar (String / Symbol); +nil+ returns +nil+ so callers can skip the filter entirely. Runs
|
|
588
|
+
# through +resolve_issue_type!+ so symbol nicknames and +config.issue_types+ overrides apply consistently
|
|
589
|
+
# with +create!+ / +update!+. Arrays raise +ArgumentError+ -- GitHub's REST +type+ param and Search
|
|
590
|
+
# +type:+ qualifier each accept a single value at a time.
|
|
591
|
+
#
|
|
592
|
+
# @raise [ArgumentError] when +value+ is an Array
|
|
593
|
+
#
|
|
594
|
+
# @param value [String, Symbol, nil]
|
|
595
|
+
#
|
|
596
|
+
# @return [String, nil]
|
|
597
|
+
#
|
|
598
|
+
def resolve_issue_types_filter(value)
|
|
599
|
+
return if value.nil?
|
|
600
|
+
if value.is_a?(Array)
|
|
601
|
+
raise(ArgumentError, 'issue_type: must be a single String / Symbol; GitHub does not accept multiple types')
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
resolve_issue_type!(value)
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
# Slugifies a field name into the kebab-case form GitHub expects in +issue_field_values+ and the Search API's
|
|
608
|
+
# +field.<slug>:+ qualifier (e.g. +"Priority List"+ / +:priority_list+ -> +"priority-list"+). Lowercases and
|
|
609
|
+
# collapses runs of whitespace or underscores into a single hyphen.
|
|
610
|
+
#
|
|
611
|
+
# @param name [String, Symbol]
|
|
612
|
+
#
|
|
613
|
+
# @return [String]
|
|
614
|
+
#
|
|
615
|
+
def field_filter_slug(name)
|
|
616
|
+
name.to_s.downcase.gsub(/[\s_]+/, '-')
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
# Coerces an issue-field filter value to the literal string GitHub expects in the query (Date / Time -> ISO
|
|
620
|
+
# 8601, Numeric / scalar -> +to_s+). Range bounds are handled separately by
|
|
621
|
+
# +build_issue_field_filter_pairs+.
|
|
622
|
+
#
|
|
623
|
+
# @param value [Object]
|
|
624
|
+
#
|
|
625
|
+
# @return [String]
|
|
626
|
+
#
|
|
627
|
+
def format_field_filter_value(value)
|
|
628
|
+
case value
|
|
629
|
+
when Date, Time then value.iso8601
|
|
630
|
+
else value.to_s
|
|
631
|
+
end
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
# Expands an +issue_fields:+ kwarg hash into the flat +Array<String>+ of +slug:value+ pairs that both REST
|
|
635
|
+
# (+issue_field_values=...+) and Search (+field.slug:value...+) consume.
|
|
636
|
+
#
|
|
637
|
+
# Scalars become a single equality pair. +Range+ values expand into one or two comparison pairs:
|
|
638
|
+
# +>=+/+<=+ for inclusive bounds, +<+ for an exclusive end (+...+). Beginless / endless ranges emit only
|
|
639
|
+
# the bounded side. +nil+ values are skipped.
|
|
640
|
+
#
|
|
641
|
+
# @param hash [Hash{String,Symbol => Object,Range,nil}]
|
|
642
|
+
#
|
|
643
|
+
# @return [Array<String>]
|
|
644
|
+
#
|
|
645
|
+
def build_issue_field_filter_pairs(hash)
|
|
646
|
+
hash.flat_map do |name, value|
|
|
647
|
+
slug = field_filter_slug(name)
|
|
648
|
+
case value
|
|
649
|
+
when nil then []
|
|
650
|
+
when Range then range_field_filter_pairs(slug, value)
|
|
651
|
+
else ["#{slug}:#{format_field_filter_value(value)}"]
|
|
652
|
+
end
|
|
653
|
+
end
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
# Expands a +Range+ value into the +slug:>=begin+ / +slug:<=end+ (or +<end+ for +...+) pair(s) GitHub expects.
|
|
657
|
+
#
|
|
658
|
+
# @param slug [String]
|
|
659
|
+
# @param range [Range]
|
|
660
|
+
#
|
|
661
|
+
# @return [Array<String>]
|
|
662
|
+
#
|
|
663
|
+
def range_field_filter_pairs(slug, range)
|
|
664
|
+
pairs = []
|
|
665
|
+
pairs << "#{slug}:>=#{format_field_filter_value(range.begin)}" if range.begin.present?
|
|
666
|
+
if range.end.present?
|
|
667
|
+
end_op = range.exclude_end? ? '<' : '<='
|
|
668
|
+
pairs << "#{slug}:#{end_op}#{format_field_filter_value(range.end)}"
|
|
669
|
+
end
|
|
670
|
+
pairs
|
|
671
|
+
end
|
|
672
|
+
|
|
531
673
|
# @raise [PlanMyStuff::APIError] when the GitHub API call fails
|
|
532
674
|
#
|
|
533
675
|
# @return [Hash]
|