Commit 72f5e1fc authored by Lee Tickett's avatar Lee Tickett

Expose merge request timelogs via GraphQL

Changelog: added
parent 833b78d2
......@@ -144,7 +144,7 @@ module Types
resolver: Resolvers::GroupLabelsResolver
field :timelogs, ::Types::TimelogType.connection_type, null: false,
description: 'Time logged on issues in the group and its subgroups.',
description: 'Time logged on issues and merge requests in the group and its subgroups.',
extras: [:lookahead],
complexity: 5,
resolver: ::Resolvers::TimelogResolver
......
......@@ -26,6 +26,11 @@ module Types
null: true,
description: 'The issue that logged time was added to.'
field :merge_request,
Types::MergeRequestType,
null: true,
description: 'The merge request that logged time was added to.'
field :note,
Types::Notes::NoteType,
null: true,
......
......@@ -15,6 +15,6 @@ module HasTimelogsReport
private
def timelogs_for(start_time, end_time)
Timelog.between_times(start_time, end_time).for_issues_in_group(self)
Timelog.between_times(start_time, end_time).in_group(self)
end
end
......@@ -14,12 +14,8 @@ class Timelog < ApplicationRecord
belongs_to :user
belongs_to :note
scope :for_issues_in_group, -> (group) do
joins(:issue).where(
'EXISTS (?)',
Project.select(1).where(namespace: group.self_and_descendants)
.where('issues.project_id = projects.id')
)
scope :in_group, -> (group) do
joins(:project).where(projects: { namespace: group.self_and_descendants })
end
scope :between_times, -> (start_time, end_time) do
......
---
title: Expose merge request timelogs via GraphQL
merge_request: 57322
author: Lee Tickett @leetickett
type: added
......@@ -9001,7 +9001,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
##### `Group.timelogs`
Time logged on issues in the group and its subgroups.
Time logged on issues and merge requests in the group and its subgroups.
Returns [`TimelogConnection!`](#timelogconnection).
......@@ -12492,6 +12492,7 @@ Represents a historically accurate report about the timebox.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="timelogissue"></a>`issue` | [`Issue`](#issue) | The issue that logged time was added to. |
| <a id="timelogmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | The merge request that logged time was added to. |
| <a id="timelognote"></a>`note` | [`Note`](#note) | The note where the quick action to add the logged time was executed. |
| <a id="timelogspentat"></a>`spentAt` | [`Time`](#time) | Timestamp of when the time tracked was spent at. |
| <a id="timelogtimespent"></a>`timeSpent` | [`Int!`](#int) | The time spent displayed in seconds. |
......
......@@ -99,3 +99,8 @@ With this option enabled, `75h` is displayed instead of `1w 4d 3h`.
## Other interesting links
- [Time Tracking solutions page](https://about.gitlab.com/solutions/time-tracking/)
- Time Tracking GraphQL references:
- [Connection](../../api/graphql/reference/index.md#timelogconnection)
- [Edge](../../api/graphql/reference/index.md#timelogedge)
- [Fields](../../api/graphql/reference/index.md#timelog)
- [Group Timelogs](../../api/graphql/reference/index.md#grouptimelogs)
......@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe GitlabSchema.types['Timelog'] do
let(:fields) { %i[spent_at time_spent user issue note] }
let(:fields) { %i[spent_at time_spent user issue merge_request note] }
it { expect(described_class.graphql_name).to eq('Timelog') }
it { expect(described_class).to have_graphql_fields(fields) }
......@@ -25,6 +25,14 @@ RSpec.describe GitlabSchema.types['Timelog'] do
end
end
describe 'merge_request field' do
subject { described_class.fields['mergeRequest'] }
it 'returns merge_request' do
is_expected.to have_graphql_type(Types::MergeRequestType)
end
end
describe 'note field' do
subject { described_class.fields['note'] }
......
......@@ -3,16 +3,20 @@
require 'spec_helper'
RSpec.describe HasTimelogsReport do
let(:user) { create(:user) }
let_it_be(:user) { create(:user) }
let(:group) { create(:group) }
let(:issue) { create(:issue, project: create(:project, :public, group: group)) }
let(:project) { create(:project, :public, group: group) }
let(:issue1) { create(:issue, project: project) }
let(:merge_request1) { create(:merge_request, source_project: project) }
describe '#timelogs' do
let!(:timelog1) { create_timelog(15.days.ago) }
let!(:timelog2) { create_timelog(10.days.ago) }
let!(:timelog3) { create_timelog(5.days.ago) }
let(:start_time) { 20.days.ago }
let(:end_time) { 8.days.ago }
let_it_be(:start_time) { 20.days.ago }
let_it_be(:end_time) { 8.days.ago }
let!(:timelog1) { create_timelog(15.days.ago, issue: issue1) }
let!(:timelog2) { create_timelog(10.days.ago, merge_request: merge_request1) }
let!(:timelog3) { create_timelog(5.days.ago, issue: issue1) }
before do
group.add_developer(user)
......@@ -45,7 +49,7 @@ RSpec.describe HasTimelogsReport do
end
end
def create_timelog(time)
create(:timelog, issue: issue, user: user, spent_at: time)
def create_timelog(time, issue: nil, merge_request: nil)
create(:timelog, issue: issue, merge_request: merge_request, user: user, spent_at: time)
end
end
......@@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe Timelog do
subject { create(:timelog) }
let(:issue) { create(:issue) }
let(:merge_request) { create(:merge_request) }
let_it_be(:issue) { create(:issue) }
let_it_be(:merge_request) { create(:merge_request) }
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:issue).touch(true) }
......@@ -54,27 +54,34 @@ RSpec.describe Timelog do
end
describe 'scopes' do
describe 'for_issues_in_group' do
it 'return timelogs created for group issues' do
group = create(:group)
subgroup = create(:group, parent: group)
create(:issue_timelog)
timelog1 = create(:issue_timelog, issue: create(:issue, project: create(:project, group: group)))
timelog2 = create(:issue_timelog, issue: create(:issue, project: create(:project, group: subgroup)))
expect(described_class.for_issues_in_group(group)).to contain_exactly(timelog1, timelog2)
let_it_be(:group) { create(:group) }
let_it_be(:group_project) { create(:project, :empty_repo, group: group) }
let_it_be(:group_issue) { create(:issue, project: group_project) }
let_it_be(:group_merge_request) { create(:merge_request, source_project: group_project) }
let_it_be(:subgroup) { create(:group, parent: group) }
let_it_be(:subgroup_project) { create(:project, :empty_repo, group: subgroup) }
let_it_be(:subgroup_issue) { create(:issue, project: subgroup_project) }
let_it_be(:subgroup_merge_request) { create(:merge_request, source_project: subgroup_project) }
let_it_be(:timelog) { create(:issue_timelog, spent_at: 65.days.ago) }
let_it_be(:timelog1) { create(:issue_timelog, spent_at: 15.days.ago, issue: group_issue) }
let_it_be(:timelog2) { create(:issue_timelog, spent_at: 5.days.ago, issue: subgroup_issue) }
let_it_be(:timelog3) { create(:merge_request_timelog, spent_at: 65.days.ago) }
let_it_be(:timelog4) { create(:merge_request_timelog, spent_at: 15.days.ago, merge_request: group_merge_request) }
let_it_be(:timelog5) { create(:merge_request_timelog, spent_at: 5.days.ago, merge_request: subgroup_merge_request) }
describe 'in_group' do
it 'return timelogs created for group issues and merge requests' do
expect(described_class.in_group(group)).to contain_exactly(timelog1, timelog2, timelog4, timelog5)
end
end
describe 'between_times' do
it 'returns collection of timelogs within given times' do
create(:issue_timelog, spent_at: 65.days.ago)
timelog1 = create(:issue_timelog, spent_at: 15.days.ago)
timelog2 = create(:issue_timelog, spent_at: 5.days.ago)
timelogs = described_class.between_times(20.days.ago, 1.day.ago)
timelogs = described_class.between_times(20.days.ago, 10.days.ago)
expect(timelogs).to contain_exactly(timelog1, timelog2)
expect(timelogs).to contain_exactly(timelog1, timelog4)
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment