Class: Rubyists::Linear::Issue

Inherits:
Object
  • Object
show all
Extended by:
ClassMethods
Includes:
SemanticLogger::Loggable
Defined in:
lib/linear/models/issue.rb,
lib/linear/models/issue/class_methods.rb

Overview

The Issue class represents a Linear issue.

Defined Under Namespace

Modules: ClassMethods

Constant Summary collapse

BASIC_FILTER =
{ completedAt: { null: true }, canceledAt: { null: true } }.freeze
Base =
fragment('BaseIssue', 'Issue') do
  id
  identifier
  title
  branchName
  description
  createdAt
  updatedAt
end

Instance Method Summary collapse

Methods included from ClassMethods

base_fragment, create, find_all, full_fragment

Instance Method Details

#add_comment(comment) ⇒ Object



56
57
58
59
60
61
62
63
64
# File 'lib/linear/models/issue.rb', line 56

def add_comment(comment)
  id_for_this = identifier
  comment_frag = comment_fragment
  m = mutation { commentCreate(input: { issueId: id_for_this, body: comment }) { comment { ___ comment_frag } } }

  query_data = Api.query(m)
  query_data.dig(:commentCreate, :comment)
  self
end

#assign!(user) ⇒ Object

Raises:



90
91
92
93
94
95
96
97
98
99
# File 'lib/linear/models/issue.rb', line 90

def assign!(user)
  this_id = identifier
  m = mutation { issueUpdate(id: this_id, input: { assigneeId: user.id }) { issue { ___ Issue.full_fragment } } }
  query_data = Api.query(m)
  updated = query_data.dig(:issueUpdate, :issue)
  raise SmellsBad, "Unknown response for issue update: #{data} (should have :issueUpdate key)" if updated.nil?

  @data = @updated_data = updated
  self
end

#attach_to_project(project) ⇒ Object



50
51
52
# File 'lib/linear/models/issue.rb', line 50

def attach_to_project(project)
  update!({ projectId: project.id })
end

#close!(state: nil, trash: false) ⇒ Object

rubocop:disable Metrics/MethodLength



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/linear/models/issue.rb', line 73

def close!(state: nil, trash: false) # rubocop:disable Metrics/MethodLength
  logger.warn "Using first completed state found: #{completed_states.first}" if state.nil?
  state ||= completed_states.first
  query_data = Api.query close_mutation(state, trash:)
  unless query_data.respond_to?(:dig)
    raise SmellsBad, "Unknown response (#{query_data || "NULL"}) updating #{self} to #{state}, trash: #{trash}"
  end

  updated = query_data.dig(:issueUpdate, :issue)
  if updated.nil?
    raise SmellsBad, "Unknown response for issue close: #{query_data} (should have :issueUpdate key)"
  end

  @data = @updated_data = updated
  self
end

#close_mutation(close_state, trash: false) ⇒ Object



66
67
68
69
70
71
# File 'lib/linear/models/issue.rb', line 66

def close_mutation(close_state, trash: false)
  id_for_this = identifier
  input = { stateId: close_state.id }
  input[:trashed] = true if trash
  mutation { issueUpdate(id: id_for_this, input:) { issue { ___ Issue.full_fragment } } }
end

#comment_fragmentObject



31
32
33
34
35
36
37
# File 'lib/linear/models/issue.rb', line 31

def comment_fragment
  @comment_fragment ||= fragment('Comment', 'Comment') do
    id
    body
    url
  end
end

#display(options) ⇒ Object



147
148
149
# File 'lib/linear/models/issue.rb', line 147

def display(options)
  printf "%s\n", (options[:full] ? full : to_s)
end

#display_commentsObject



127
128
129
130
131
132
133
134
135
136
# File 'lib/linear/models/issue.rb', line 127

def display_comments
  return '' unless comments.respond_to?(:map)

  displays = comments&.map do |c|
    user = c.user.name
    date = DateTime.parse(c.createdAt).strftime('%Y-%m-%d at %H:%M')
    "--- #{user} on #{date} ---\n#{TTY::Markdown.parse(c.body)}"
  end
  displays&.join("\n")
end

#fullObject



138
139
140
141
142
143
144
145
# File 'lib/linear/models/issue.rb', line 138

def full
  sep = '-' * to_s.length
  format("%<to_s>s\n%<sep>s\n%<description>s\n%<comments>s",
         sep:,
         to_s:,
         description: parsed_description,
         comments: display_comments)
end

#inspectionObject



105
106
107
# File 'lib/linear/models/issue.rb', line 105

def inspection
  format('id: "%<identifier>s" title: "%<title>s"', identifier:, title:)
end

#parsed_descriptionObject



116
117
118
119
120
121
122
123
124
125
# File 'lib/linear/models/issue.rb', line 116

def parsed_description
  return TTY::Markdown.parse(description) if description && !description.empty?

  TTY::Markdown.parse(['# No Description For this issue??',
                       'Issues really need description',
                       "## What's up with that?"].join("\n"))
rescue StandardError => e
  logger.error 'Error parsing description', e
  "Description was unparsable: #{description}\n"
end

#to_sObject



109
110
111
112
113
114
# File 'lib/linear/models/issue.rb', line 109

def to_s
  basic = format('%<id>-12s %<title>s', id: identifier, title:)
  return basic unless (name = data.dig(:assignee, :name))

  format('%<basic>s (%<name>s)', basic:, name:)
end

#update!(input) ⇒ Object

Raises:



39
40
41
42
43
44
45
46
47
48
# File 'lib/linear/models/issue.rb', line 39

def update!(input)
  id_for_this = identifier
  m = mutation { issueUpdate(id: id_for_this, input:) { issue { ___ Issue.full_fragment } } }
  query_data = Api.query(m)
  updated = query_data.dig(:issueUpdate, :issue)
  raise SmellsBad, "Unknown response for issue update: #{data} (should have :issueUpdate key)" if updated.nil?

  @data = @updated_data = updated
  self
end

#workflow_statesObject



101
102
103
# File 'lib/linear/models/issue.rb', line 101

def workflow_states
  @workflow_states ||= team.workflow_states
end