tempest_time 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f0b28efca7d86aa211972f46715abbc9280b68b9ef899964601d31b22175240
4
- data.tar.gz: 9f244e11657b42344c22275331bde8538a247117a4c098519355f6e766bdf89d
3
+ metadata.gz: 0f7ea036a16a00b115c43380947acca1d3ed7d1b8b3e6f15ad61aec1306291a0
4
+ data.tar.gz: 2f3b83981753b1f81b75a33b5399421f967fad33898e7c5e66f25bbb270d1e3e
5
5
  SHA512:
6
- metadata.gz: 314dc7529baf6ff390c05d6429da13244015ecf6d89d840bab1063a53cddeb067fd0275a036d63320617b9de527bdfa896c409682a33c51cc0ec6313b19e0d8e
7
- data.tar.gz: 1de292852bc3af692ed108442edf035d4adae2f49a686bb12986d66413f76170d4ee13708dfb84a4e8cad124580270d47c0d76480b397ae0f92f684ba3bbd1bf
6
+ metadata.gz: b27a2c26d4dd0c4e908001649eb59c514f4a6cdf9ea8b03d2c55826e46caf5e56af1101fab6f8d7383821c577e8ab699142bd4d0e21322b792e85eeef6087a59
7
+ data.tar.gz: e522e503832c6dd7e4704c0fd2e76815e68f75f4dd6a3af0725b14a7fa82ae34dc492bc1df6a2b232f78ada460dabbb0a4c1c3c9a6301f41998fa91a70373bd0
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JiraAPI
4
+ module Models
5
+ class User
6
+ def initialize(user)
7
+ @user_data = user
8
+ end
9
+
10
+ def name
11
+ @name ||= user_data['displayName']
12
+ end
13
+
14
+ def username
15
+ @username ||= user_data['name']
16
+ end
17
+
18
+ def email
19
+ @email ||= user_data['emailAddress']
20
+ end
21
+
22
+ def account_id
23
+ @account_id ||= user_data['accountId']
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :user_data
29
+ end
30
+ end
31
+ end
@@ -31,7 +31,7 @@ module JiraAPI
31
31
  Response
32
32
  end
33
33
 
34
- def user
34
+ def auth_user
35
35
  credentials.fetch(:email, nil)
36
36
  end
37
37
 
@@ -4,11 +4,11 @@ require_relative '../responses/get_user_issues'
4
4
  module JiraAPI
5
5
  module Requests
6
6
  class GetUserIssues < JiraAPI::Request
7
- attr_reader :requested_user
7
+ attr_reader :user
8
8
 
9
- def initialize(requested_user: nil)
9
+ def initialize(user:)
10
10
  super
11
- @requested_user = requested_user || username
11
+ @user = user
12
12
  end
13
13
 
14
14
  private
@@ -23,7 +23,7 @@ module JiraAPI
23
23
 
24
24
  def query_params
25
25
  {
26
- 'jql' => "assignee=#{requested_user} AND resolution is EMPTY",
26
+ 'jql' => "assignee=#{user.username} AND resolution is EMPTY",
27
27
  'maxResults' => 100,
28
28
  }
29
29
  end
@@ -0,0 +1,33 @@
1
+ require_relative '../request'
2
+ require_relative '../responses/search_users'
3
+
4
+ module JiraAPI
5
+ module Requests
6
+ class SearchUsers < JiraAPI::Request
7
+ def initialize(query: nil)
8
+ super
9
+ @query = query
10
+ end
11
+
12
+ private
13
+
14
+ def request_method
15
+ 'get'
16
+ end
17
+
18
+ def request_path
19
+ "/user/search"
20
+ end
21
+
22
+ def query_params
23
+ {
24
+ 'query' => @query,
25
+ }
26
+ end
27
+
28
+ def response_klass
29
+ JiraAPI::Responses::SearchUsers
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,9 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../response'
4
+ require_relative '../models/user'
4
5
 
5
6
  module JiraAPI
6
7
  module Responses
7
- class GetCurrentUser < JiraAPI::Response; end
8
+ class GetCurrentUser < JiraAPI::Response
9
+ def user
10
+ @user ||= JiraAPI::Models::User.new(raw_response)
11
+ end
12
+ end
8
13
  end
9
14
  end
@@ -0,0 +1,12 @@
1
+ require_relative '../response'
2
+ require_relative '../models/user'
3
+
4
+ module JiraAPI
5
+ module Responses
6
+ class SearchUsers < JiraAPI::Response
7
+ def users
8
+ @users ||= raw_response.parsed_response.map { |user| JiraAPI::Models::User.new(user) }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -61,7 +61,7 @@ module TempestTime
61
61
  authorization_klass.new.credentials
62
62
  end
63
63
 
64
- def user
64
+ def auth_user
65
65
  credentials.fetch(:user, nil)
66
66
  end
67
67
 
@@ -82,7 +82,7 @@ module TempestTime
82
82
  end
83
83
 
84
84
  def basic_auth
85
- { username: user, password: token }
85
+ { username: auth_user, password: token }
86
86
  end
87
87
  end
88
88
  end
@@ -5,7 +5,7 @@ module TempoAPI
5
5
  private
6
6
 
7
7
  def url
8
- 'https://api.tempo.io/2'
8
+ 'https://api.tempo.io/core/3'
9
9
  end
10
10
 
11
11
  def user
@@ -15,6 +15,7 @@ module TempoAPI
15
15
  body: request_body.empty? ? request_body : request_body.to_json,
16
16
  query: query_params
17
17
  )
18
+
18
19
  response
19
20
  end
20
21
 
@@ -12,11 +12,12 @@ module TempoAPI
12
12
  @message = options[:message]
13
13
  @date = options[:date] ? options[:date] : Date.today
14
14
  @billable = options[:billable]
15
+ @user = options[:user]
15
16
  end
16
17
 
17
18
  private
18
19
 
19
- attr_reader :issue, :remaining, :seconds, :message, :date, :billable
20
+ attr_reader :issue, :remaining, :seconds, :message, :date, :billable, :user
20
21
 
21
22
  def request_method
22
23
  'post'
@@ -38,7 +39,7 @@ module TempoAPI
38
39
  "remainingEstimateSeconds": remaining,
39
40
  "startDate": date.strftime('%Y-%m-%d'),
40
41
  "startTime": '12:00:00',
41
- "authorUsername": user,
42
+ "authorAccountId": user.account_id,
42
43
  "description": message,
43
44
  }
44
45
  end
@@ -6,23 +6,23 @@ module TempoAPI
6
6
  class GetUserSchedule < TempoAPI::Request
7
7
  attr_reader :start_date, :end_date
8
8
 
9
- def initialize(start_date:, end_date:, requested_user:)
9
+ def initialize(start_date:, end_date:, user:)
10
10
  super
11
11
  @start_date = start_date
12
12
  @end_date = end_date || start_date
13
- @requested_user = requested_user || user
13
+ @user = user
14
14
  end
15
15
 
16
16
  private
17
17
 
18
- attr_reader :requested_user
18
+ attr_reader :user
19
19
 
20
20
  def request_method
21
21
  'get'
22
22
  end
23
23
 
24
24
  def request_path
25
- "/user-schedule/#{requested_user}"
25
+ "/user-schedule/#{user.account_id}"
26
26
  end
27
27
 
28
28
  def query_params
@@ -8,16 +8,16 @@ module TempoAPI
8
8
  class ListWorklogs < TempoAPI::Request
9
9
  attr_reader :start_date, :end_date
10
10
 
11
- def initialize(start_date, end_date: nil, requested_user: nil)
11
+ def initialize(start_date, end_date: nil, user: nil)
12
12
  super
13
13
  @start_date = start_date
14
14
  @end_date = end_date || start_date
15
- @requested_user = requested_user || user
15
+ @user = user
16
16
  end
17
17
 
18
18
  private
19
19
 
20
- attr_reader :requested_user
20
+ attr_reader :user
21
21
 
22
22
  def response_klass
23
23
  TempoAPI::Responses::ListWorklogs
@@ -28,7 +28,7 @@ module TempoAPI
28
28
  end
29
29
 
30
30
  def request_path
31
- "/worklogs/user/#{requested_user}"
31
+ "/worklogs/user/#{user.account_id}"
32
32
  end
33
33
 
34
34
  def query_params
@@ -7,10 +7,11 @@ module TempoAPI
7
7
  class SubmitTimesheet < TempoAPI::Request
8
8
  include TempestTime::Helpers::TimeHelper
9
9
 
10
- attr_reader :reviewer, :dates
10
+ attr_reader :submitter, :reviewer, :dates
11
11
 
12
- def initialize(reviewer, dates)
12
+ def initialize(submitter:, reviewer:, dates:)
13
13
  super
14
+ @submitter = submitter
14
15
  @reviewer = reviewer
15
16
  @dates = dates
16
17
  end
@@ -22,7 +23,7 @@ module TempoAPI
22
23
  end
23
24
 
24
25
  def request_path
25
- "/timesheet-approvals/user/#{user}/submit"
26
+ "/timesheet-approvals/user/#{submitter.account_id}/submit"
26
27
  end
27
28
 
28
29
  def response_klass
@@ -38,7 +39,7 @@ module TempoAPI
38
39
 
39
40
  def request_body
40
41
  {
41
- reviewerUsername: reviewer,
42
+ reviewerAccountId: reviewer.account_id,
42
43
  }
43
44
  end
44
45
  end
@@ -6,7 +6,7 @@ module TempoAPI
6
6
  private
7
7
 
8
8
  def success_message
9
- "Timesheet submitted successfully to #{request.reviewer}!"
9
+ "Timesheet submitted successfully to #{request.reviewer.name}!"
10
10
  end
11
11
  end
12
12
  end
@@ -5,6 +5,8 @@ require 'forwardable'
5
5
  require_relative 'helpers/time_helper'
6
6
  require_relative 'helpers/formatting_helper'
7
7
  require_relative 'helpers/git_helper'
8
+ require_relative 'api/jira_api/requests/get_current_user'
9
+ require_relative 'api/jira_api/requests/search_users'
8
10
 
9
11
  module TempestTime
10
12
  class Command
@@ -90,5 +92,31 @@ module TempestTime
90
92
  s.error(pastel.red(response.message))
91
93
  end
92
94
  end
95
+
96
+ def find_user(query)
97
+ return current_user unless query
98
+
99
+ users = JiraAPI::Requests::SearchUsers.new(query: query).send_request.users
100
+ case users.count
101
+ when 0
102
+ abort(
103
+ pastel.red('User not found!') + ' ' \
104
+ 'Please check your query and try again.'
105
+ )
106
+ when 1
107
+ return users.first
108
+ else
109
+ require 'tty-prompt'
110
+ TTY::Prompt.new.select(
111
+ pastel.yellow('Multiple users match your query. Please select a user.'),
112
+ users.map { |user| { "#{user.name}: #{user.email}" => user } },
113
+ per_page: 10
114
+ )
115
+ end
116
+ end
117
+
118
+ def current_user
119
+ @current_user ||= JiraAPI::Requests::GetCurrentUser.new.send_request.user
120
+ end
93
121
  end
94
122
  end
@@ -8,10 +8,10 @@ module TempestTime
8
8
  namespace :issue
9
9
 
10
10
  desc 'list', 'List unresolved issues.'
11
- option :date, type: :string
12
- def list(user = nil)
11
+ option :user, aliases: '-u', type: :string
12
+ def list
13
13
  require_relative 'issue/list'
14
- TempestTime::Commands::Issue::List.new(user).execute
14
+ TempestTime::Commands::Issue::List.new(options).execute
15
15
  end
16
16
 
17
17
  desc 'open', 'Open an issue in your browser. (Default: current branch)'
@@ -7,13 +7,13 @@ module TempestTime
7
7
  module Commands
8
8
  class Issue
9
9
  class List < TempestTime::Command
10
- def initialize(user)
11
- @user = user || current_user
10
+ def initialize(options)
11
+ @user = find_user(options[:user])
12
12
  end
13
13
 
14
14
  def execute!
15
- request = JiraAPI::Requests::GetUserIssues.new(requested_user: @user)
16
- message = "Getting issues for #{request.requested_user}"
15
+ request = JiraAPI::Requests::GetUserIssues.new(user: @user)
16
+ message = "Getting issues for #{pastel.yellow(@user.name)}"
17
17
  response = with_spinner(message) do |spinner|
18
18
  request.send_request.tap { spinner.stop }
19
19
  end
@@ -38,17 +38,12 @@ module TempestTime
38
38
  table.new(
39
39
  %w(Status Issue Summary),
40
40
  issues.map { |issue| row(issue) }
41
- ).render(:ascii, padding: [0, 1], column_widths: [15, 10, 30])
41
+ ).render(:ascii, padding: [0, 1], column_widths: [15, 15, 45])
42
42
  end
43
43
 
44
44
  def row(issue)
45
45
  [issue.status, issue.key, issue.summary]
46
46
  end
47
-
48
- def current_user
49
- require_relative '../../settings/authorization'
50
- TempestTime::Settings::Authorization.new.fetch('username')
51
- end
52
47
  end
53
48
  end
54
49
  end
@@ -7,7 +7,7 @@ module TempestTime
7
7
  module Commands
8
8
  class List < TempestTime::Command
9
9
  def initialize(options)
10
- @user = options[:user]
10
+ @user = find_user(options[:user])
11
11
  @dates = options[:date] ? [Date.parse(options[:date])] : nil
12
12
  end
13
13
 
@@ -18,11 +18,14 @@ module TempestTime
18
18
  days_after: 13
19
19
  ).sort
20
20
  @dates.each do |date|
21
- with_spinner("Retrieving logs for #{pastel.yellow(formatted_date(date))}...") do |spin|
21
+ with_spinner(
22
+ "Retrieving logs for #{pastel.yellow(@user.name)} "\
23
+ "on #{pastel.yellow(formatted_date(date))}..."
24
+ ) do |spin|
22
25
  @response = TempoAPI::Requests::ListWorklogs.new(
23
26
  date,
24
27
  end_date: nil,
25
- requested_user: @user
28
+ user: @user
26
29
  ).send_request
27
30
  spin.stop(pastel.green('Done!'))
28
31
  prompt.say(render_table)
@@ -45,7 +48,7 @@ module TempestTime
45
48
  t.render(
46
49
  :ascii,
47
50
  padding: [0, 1],
48
- column_widths: [7, 10, 15, 30],
51
+ column_widths: [7, 15, 15, 30],
49
52
  multiline: true
50
53
  )
51
54
  end
@@ -20,7 +20,7 @@ module TempestTime
20
20
 
21
21
  def execute!
22
22
  if @users.empty? && @team.nil?
23
- @users = user_prompt
23
+ @users = user_prompt.map { |user| find_user(user) }
24
24
  end
25
25
 
26
26
  @users.push(@teams.members(@team)) if @team
@@ -86,7 +86,7 @@ module TempestTime
86
86
  @required_seconds ||= TempoAPI::Requests::GetUserSchedule.new(
87
87
  start_date: start_date,
88
88
  end_date: end_date,
89
- requested_user: @users.first
89
+ user: @users.first
90
90
  ).send_request.required_seconds
91
91
  end
92
92
 
@@ -95,7 +95,7 @@ module TempestTime
95
95
  worklogs = TempoAPI::Requests::ListWorklogs.new(
96
96
  start_date,
97
97
  end_date: end_date,
98
- requested_user: user
98
+ user: user
99
99
  ).send_request.worklogs
100
100
 
101
101
  TempestTime::Models::Report.new(
@@ -143,7 +143,7 @@ module TempestTime
143
143
 
144
144
  def row(data)
145
145
  row = [
146
- data.user,
146
+ (data.user.is_a?(String) ? data.user : data.user.name),
147
147
  right_align(percentage(data.total_compliance_percentage)),
148
148
  right_align(percentage(data.utilization_percentage)),
149
149
  ]
@@ -72,10 +72,12 @@ module TempestTime
72
72
  end
73
73
 
74
74
  def check_for_validity
75
- jira = JiraAPI::Requests::GetCurrentUser.new
76
- tempo = TempoAPI::Requests::ListWorklogs.new(Date.today)
77
- raise StandardError unless jira.send_request.success?
78
- raise StandardError unless tempo.send_request.success?
75
+ raise StandardError unless JiraAPI::Requests::GetCurrentUser.new.send_request.user
76
+ raise StandardError unless tempo_token_is_valid?
77
+ end
78
+
79
+ def tempo_token_is_valid?
80
+ TempoAPI::Requests::ListWorklogs.new(Date.today).send_request.success?
79
81
  end
80
82
 
81
83
  def email
@@ -11,15 +11,19 @@ module TempestTime
11
11
  end
12
12
 
13
13
  def execute!
14
- reviewer = prompt.ask('Who should review this timesheet? (username)')
14
+ reviewer = find_user(prompt.ask('Who should review this timesheet?'))
15
15
  dates = week_dates(week_prompt('Select a week to submit.'))
16
16
 
17
- message = 'Submit the selected timesheet to ' + pastel.green(reviewer) + '?'
17
+ message = 'Submit the selected timesheet to ' + pastel.green(reviewer.name) + '?'
18
18
  abort unless prompt.yes?(message)
19
19
  abort unless prompt.yes?('Are you sure? No edits can be made once submitted!')
20
20
 
21
21
  with_success_fail_spinner('Submitting your timesheet...') do
22
- TempoAPI::Requests::SubmitTimesheet.new(reviewer, dates).send_request
22
+ TempoAPI::Requests::SubmitTimesheet.new(
23
+ submitter: current_user,
24
+ reviewer: reviewer,
25
+ dates: dates
26
+ ).send_request
23
27
  end
24
28
  end
25
29
  end
@@ -7,13 +7,14 @@ require_relative '../api/tempo_api/requests/create_worklog'
7
7
  module TempestTime
8
8
  module Commands
9
9
  class Track < TempestTime::Command
10
- attr_reader :time, :issues, :options
10
+ attr_reader :time, :issues, :options, :user
11
11
 
12
12
  def initialize(time, issues, options)
13
13
  @options = options
14
14
  @issues = issues.any? ? issues.map(&:upcase) : [automatic_issue]
15
15
  @time = parsed_time(time) / @issues.count
16
16
  @dates = options[:date] ? [Date.parse(options[:date])] : nil
17
+ @user = current_user
17
18
  end
18
19
 
19
20
  def execute!
@@ -24,7 +25,7 @@ module TempestTime
24
25
  end
25
26
  dates.each do |date|
26
27
  issues.each do |issue|
27
- track_time(time, options.merge({ issue: issue, date: date }))
28
+ track_time(time, options.merge({ issue: issue, date: date, user: user }))
28
29
  end
29
30
  end
30
31
  end
@@ -61,13 +62,12 @@ module TempestTime
61
62
  end
62
63
 
63
64
  def remaining_estimate(issue, time)
64
- request = JiraAPI::Requests::GetIssue.new(issue)
65
- request.send_request
66
- if request.response.failure?
67
- abort("There was an issue getting this Jira issue.\n"\
65
+ response = JiraAPI::Requests::GetIssue.new(issue).send_request
66
+ if response.failure?
67
+ abort("There was a problem getting this Jira issue.\n"\
68
68
  'Please check the issue number and your credentials.')
69
69
  end
70
- remaining = request.response.issue.remaining_estimate || 0
70
+ remaining = response.issue.remaining_estimate || 0
71
71
  remaining > time ? remaining - time : 0
72
72
  end
73
73
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TempestTime
4
- VERSION = '1.2.1'
4
+ VERSION = '2.0.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tempest_time
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Devan Hurst
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-05 00:00:00.000000000 Z
11
+ date: 2019-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -247,14 +247,17 @@ files:
247
247
  - lib/tempest_time/api/authorization.rb
248
248
  - lib/tempest_time/api/jira_api/authorization.rb
249
249
  - lib/tempest_time/api/jira_api/models/issue.rb
250
+ - lib/tempest_time/api/jira_api/models/user.rb
250
251
  - lib/tempest_time/api/jira_api/request.rb
251
252
  - lib/tempest_time/api/jira_api/requests/get_current_user.rb
252
253
  - lib/tempest_time/api/jira_api/requests/get_issue.rb
253
254
  - lib/tempest_time/api/jira_api/requests/get_user_issues.rb
255
+ - lib/tempest_time/api/jira_api/requests/search_users.rb
254
256
  - lib/tempest_time/api/jira_api/response.rb
255
257
  - lib/tempest_time/api/jira_api/responses/get_current_user.rb
256
258
  - lib/tempest_time/api/jira_api/responses/get_issue.rb
257
259
  - lib/tempest_time/api/jira_api/responses/get_user_issues.rb
260
+ - lib/tempest_time/api/jira_api/responses/search_users.rb
258
261
  - lib/tempest_time/api/request.rb
259
262
  - lib/tempest_time/api/response.rb
260
263
  - lib/tempest_time/api/tempo_api/authorization.rb