From ec7db295d230ba1cec0c75ae199d60bd8f89c1d6 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 13 Oct 2016 17:11:28 +0200 Subject: Started refactoring stuff to add events to cycle analytics - Refactored cycle analytics class to extract DB logic - Reuse logic in new events fetcher - Started adding cycle analytics events class and spec (still not functional) --- app/models/cycle_analytics.rb | 49 +----------------- lib/gitlab/cycle_analytics/events.rb | 15 ++++++ lib/gitlab/cycle_analytics/events_fetcher.rb | 27 ++++++++++ lib/gitlab/cycle_analytics/metrics_fetcher.rb | 72 ++++++++++++++++++++++++++ spec/lib/gitlab/cycle_analytics/events_spec.rb | 26 ++++++++++ 5 files changed, 141 insertions(+), 48 deletions(-) create mode 100644 lib/gitlab/cycle_analytics/events.rb create mode 100644 lib/gitlab/cycle_analytics/events_fetcher.rb create mode 100644 lib/gitlab/cycle_analytics/metrics_fetcher.rb create mode 100644 spec/lib/gitlab/cycle_analytics/events_spec.rb diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb index 8ed4a56b19b..30ca3273a2c 100644 --- a/app/models/cycle_analytics.rb +++ b/app/models/cycle_analytics.rb @@ -1,8 +1,5 @@ class CycleAnalytics - include Gitlab::Database::Median - include Gitlab::Database::DateTime - - DEPLOYMENT_METRIC_STAGES = %i[production staging] + include Gitlab::CycleAnalytics::MetricsFetcher def initialize(project, from:) @project = project @@ -56,48 +53,4 @@ class CycleAnalytics Issue.arel_table[:created_at], MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) end - - private - - def calculate_metric(name, start_time_attrs, end_time_attrs) - cte_table = Arel::Table.new("cte_table_for_#{name}") - - # Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time). - # Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time). - # We compute the (end_time - start_time) interval, and give it an alias based on the current - # cycle analytics stage. - interval_query = Arel::Nodes::As.new( - cte_table, - subtract_datetimes(base_query_for(name), end_time_attrs, start_time_attrs, name.to_s)) - - median_datetime(cte_table, interval_query, name) - end - - # Join table with a row for every pair (where the merge request - # closes the given issue) with issue and merge request metrics included. The metrics - # are loaded with an inner join, so issues / merge requests without metrics are - # automatically excluded. - def base_query_for(name) - arel_table = MergeRequestsClosingIssues.arel_table - - # Load issues - query = arel_table.join(Issue.arel_table).on(Issue.arel_table[:id].eq(arel_table[:issue_id])). - join(Issue::Metrics.arel_table).on(Issue.arel_table[:id].eq(Issue::Metrics.arel_table[:issue_id])). - where(Issue.arel_table[:project_id].eq(@project.id)). - where(Issue.arel_table[:deleted_at].eq(nil)). - where(Issue.arel_table[:created_at].gteq(@from)) - - # Load merge_requests - query = query.join(MergeRequest.arel_table, Arel::Nodes::OuterJoin). - on(MergeRequest.arel_table[:id].eq(arel_table[:merge_request_id])). - join(MergeRequest::Metrics.arel_table). - on(MergeRequest.arel_table[:id].eq(MergeRequest::Metrics.arel_table[:merge_request_id])) - - if DEPLOYMENT_METRIC_STAGES.include?(name) - # Limit to merge requests that have been deployed to production after `@from` - query.where(MergeRequest::Metrics.arel_table[:first_deployed_to_production_at].gteq(@from)) - end - - query - end end diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb new file mode 100644 index 00000000000..0cf3067d48e --- /dev/null +++ b/lib/gitlab/cycle_analytics/events.rb @@ -0,0 +1,15 @@ +module Gitlab + module CycleAnalytics + class Events + def initialize(project:, from:) + @project = project + @from = from + @fetcher = EventsFetcher.new(project: project, from: from) + end + + def issue_events + @fetcher.fetch_issues + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb new file mode 100644 index 00000000000..9e62eef706c --- /dev/null +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -0,0 +1,27 @@ +module Gitlab + module CycleAnalytics + class EventsFetcher + include MetricsFetcher + + def initialize(project:, from:) + @project = project + @from = from + end + + def fetch_issues + cte_table = Arel::Table.new("cte_table_for_issue") + + interval_query = Arel::Nodes::As.new( + cte_table, + subtract_datetimes(base_query_for(:issue), *attributes, 'issue')) + + #TODO ActiveRecord::Base.connection.execute(interval_query) + end + + def attributes + [issue_metrics_table[:first_associated_with_milestone_at], + issue_metrics_table[:first_added_to_board_at]] + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb new file mode 100644 index 00000000000..51b4963cf08 --- /dev/null +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -0,0 +1,72 @@ +module Gitlab + module CycleAnalytics + module MetricsFetcher + include Gitlab::Database::Median + include Gitlab::Database::DateTime + + DEPLOYMENT_METRIC_STAGES = %i[production staging] + + private + + def calculate_metric(name, start_time_attrs, end_time_attrs) + cte_table = Arel::Table.new("cte_table_for_#{name}") + + # Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time). + # Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time). + # We compute the (end_time - start_time) interval, and give it an alias based on the current + # cycle analytics stage. + interval_query = Arel::Nodes::As.new( + cte_table, + subtract_datetimes(base_query_for(name), end_time_attrs, start_time_attrs, name.to_s)) + + median_datetime(cte_table, interval_query, name) + end + + # Join table with a row for every pair (where the merge request + # closes the given issue) with issue and merge request metrics included. The metrics + # are loaded with an inner join, so issues / merge requests without metrics are + # automatically excluded. + def base_query_for(name) + # Load issues + query = issue_metrics_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id])). + join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id])). + where(issue_table[:project_id].eq(@project.id)). + where(issue_table[:deleted_at].eq(nil)). + where(issue_table[:created_at].gteq(@from)) + + # Load merge_requests + query = query.join(merge_request_table, Arel::Nodes::OuterJoin). + on(merge_request_table[:id].eq(mr_closing_issues_table[:merge_request_id])). + join(merge_request_metrics_table). + on(merge_request_table[:id].eq(merge_request_metrics_table[:merge_request_id])) + + if DEPLOYMENT_METRIC_STAGES.include?(name) + # Limit to merge requests that have been deployed to production after `@from` + query.where(merge_request_metrics_table[:first_deployed_to_production_at].gteq(@from)) + end + + query + end + + def merge_request_metrics_table + MergeRequest::Metrics.arel_table + end + + def merge_request_table + MergeRequest.arel_table + end + + def mr_closing_issues_table + MergeRequestsClosingIssues.arel_table + end + + def issue_table + Issue.arel_table + end + + def issue_metrics_table + Issue::Metrics.arel_table + end + end + end +end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb new file mode 100644 index 00000000000..b68cc16f4e6 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe Gitlab::CycleAnalytics::Events do + let(:project) { create(:project) } + let(:from_date) { 10.days.ago } + let(:user) { create(:user, :admin) } + + subject { described_class.new(project: project, from: from_date) } + + before do + setup(context) + end + + describe '#issue' do + let!(:context) { create(:issue, project: project) } + + xit 'does something' do + expect(subject.issue_events).to eq([]) + end + end + + def setup(context) + create(:milestone, project: project) + create_merge_request_closing_issue(context) + end +end -- cgit v1.2.1 From 470e39d64f02def538bb6aa3e51bcf406fd2e480 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 14 Oct 2016 17:33:21 +0200 Subject: WIP - refactored some arel queries --- lib/gitlab/cycle_analytics/events_fetcher.rb | 17 ++++++++++++----- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 2 +- lib/gitlab/database/date_time.rb | 26 +++++++++++++++----------- spec/lib/gitlab/cycle_analytics/events_spec.rb | 5 +++-- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 9e62eef706c..ceed5823371 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -11,14 +11,21 @@ module Gitlab def fetch_issues cte_table = Arel::Table.new("cte_table_for_issue") - interval_query = Arel::Nodes::As.new( - cte_table, - subtract_datetimes(base_query_for(:issue), *attributes, 'issue')) + # Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time). + # Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time). + # We compute the (end_time - start_time) interval, and give it an alias based on the current + # cycle analytics stage. - #TODO ActiveRecord::Base.connection.execute(interval_query) + base_query = base_query_for(:issue) + + diff_fn = subtract_datetimes_diff(base_query, issue_table[:created_at], metric_attributes) + + query = base_query.project(diff_fn.as('issue_diff')) + + ActiveRecord::Base.connection.execute(query.to_sql) end - def attributes + def metric_attributes [issue_metrics_table[:first_associated_with_milestone_at], issue_metrics_table[:first_added_to_board_at]] end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index 51b4963cf08..97b55656d79 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -28,7 +28,7 @@ module Gitlab # automatically excluded. def base_query_for(name) # Load issues - query = issue_metrics_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id])). + query = mr_closing_issues_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id])). join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id])). where(issue_table[:project_id].eq(@project.id)). where(issue_table[:deleted_at].eq(nil)). diff --git a/lib/gitlab/database/date_time.rb b/lib/gitlab/database/date_time.rb index b6a89f715fd..e2524886de2 100644 --- a/lib/gitlab/database/date_time.rb +++ b/lib/gitlab/database/date_time.rb @@ -8,20 +8,24 @@ module Gitlab # Note: For MySQL, the interval is returned in seconds. # For PostgreSQL, the interval is returned as an INTERVAL type. def subtract_datetimes(query_so_far, end_time_attrs, start_time_attrs, as) - diff_fn = if Gitlab::Database.postgresql? - Arel::Nodes::Subtraction.new( - Arel::Nodes::NamedFunction.new("COALESCE", Array.wrap(end_time_attrs)), - Arel::Nodes::NamedFunction.new("COALESCE", Array.wrap(start_time_attrs))) - elsif Gitlab::Database.mysql? - Arel::Nodes::NamedFunction.new( - "TIMESTAMPDIFF", - [Arel.sql('second'), - Arel::Nodes::NamedFunction.new("COALESCE", Array.wrap(start_time_attrs)), - Arel::Nodes::NamedFunction.new("COALESCE", Array.wrap(end_time_attrs))]) - end + diff_fn = subtract_datetimes_diff(query_so_far, end_time_attrs, start_time_attrs) query_so_far.project(diff_fn.as(as)) end + + def subtract_datetimes_diff(query_so_far, end_time_attrs, start_time_attrs) + if Gitlab::Database.postgresql? + Arel::Nodes::Subtraction.new( + Arel::Nodes::NamedFunction.new("COALESCE", Array.wrap(end_time_attrs)), + Arel::Nodes::NamedFunction.new("COALESCE", Array.wrap(start_time_attrs))) + elsif Gitlab::Database.mysql? + Arel::Nodes::NamedFunction.new( + "TIMESTAMPDIFF", + [Arel.sql('second'), + Arel::Nodes::NamedFunction.new("COALESCE", Array.wrap(start_time_attrs)), + Arel::Nodes::NamedFunction.new("COALESCE", Array.wrap(end_time_attrs))]) + end + end end end end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index b68cc16f4e6..64387ef10d3 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -15,12 +15,13 @@ describe Gitlab::CycleAnalytics::Events do let!(:context) { create(:issue, project: project) } xit 'does something' do - expect(subject.issue_events).to eq([]) + expect(subject.issue_events).to eq([context]) end end def setup(context) - create(:milestone, project: project) + milestone = create(:milestone, project: project) + context.update(milestone: milestone) create_merge_request_closing_issue(context) end end -- cgit v1.2.1 From 847d2796cfeda0347aae0649fed16250f6188ca9 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 17 Oct 2016 14:57:23 +0200 Subject: fixed spec and SQL query --- lib/gitlab/cycle_analytics/events_fetcher.rb | 22 ++++++++++++---------- spec/lib/gitlab/cycle_analytics/events_spec.rb | 20 ++++++++++++++++++-- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index ceed5823371..eb3cf9f2476 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -9,26 +9,28 @@ module Gitlab end def fetch_issues - cte_table = Arel::Table.new("cte_table_for_issue") - - # Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time). - # Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time). - # We compute the (end_time - start_time) interval, and give it an alias based on the current - # cycle analytics stage. - base_query = base_query_for(:issue) - diff_fn = subtract_datetimes_diff(base_query, issue_table[:created_at], metric_attributes) - query = base_query.project(diff_fn.as('issue_diff')) + query = base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])). + project(diff_fn.as('issue_diff'), *issue_projections). + order(issue_table[:created_at].desc) - ActiveRecord::Base.connection.execute(query.to_sql) + ActiveRecord::Base.connection.execute(query.to_sql).first end def metric_attributes [issue_metrics_table[:first_associated_with_milestone_at], issue_metrics_table[:first_added_to_board_at]] end + + def issue_projections + [issue_table[:title], issue_table[:iid], issue_table[:created_at], User.arel_table[:name]] + end + + def user_table + User.arel_table + end end end end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 64387ef10d3..b32c4d6baa0 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -14,8 +14,24 @@ describe Gitlab::CycleAnalytics::Events do describe '#issue' do let!(:context) { create(:issue, project: project) } - xit 'does something' do - expect(subject.issue_events).to eq([context]) + it 'has an issue diff' do + expect(subject.issue_events['issue_diff']).to eq("-00:00:00.339259") + end + + it 'has a title' do + expect(subject.issue_events['title']).to eq(context.title) + end + + it 'has an iid' do + expect(subject.issue_events['iid']).to eq(context.iid) + end + + it 'has a created_at timestamp' do + expect(subject.issue_events['created_at']).to eq(context.created_at) + end + + it "has the author's name" do + expect(subject.issue_events['name']).to eq(context.author.name) end end -- cgit v1.2.1 From c545968ece739e616047c519dbc66e4e428cdd8a Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 17 Oct 2016 18:03:17 +0200 Subject: fix timestamp diff and spec --- lib/gitlab/cycle_analytics/events.rb | 6 +++++- lib/gitlab/cycle_analytics/events_fetcher.rb | 8 ++++++-- spec/lib/gitlab/cycle_analytics/events_spec.rb | 8 ++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 0cf3067d48e..04cd599f092 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -1,6 +1,8 @@ module Gitlab module CycleAnalytics class Events + include ActionView::Helpers::DateHelper + def initialize(project:, from:) @project = project @from = from @@ -8,7 +10,9 @@ module Gitlab end def issue_events - @fetcher.fetch_issues + @fetcher.fetch_issues.each do |event| + event['issue_diff'] = distance_of_time_in_words(event['issue_diff'].to_f) + end end end end diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index eb3cf9f2476..14405f1356a 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -13,10 +13,10 @@ module Gitlab diff_fn = subtract_datetimes_diff(base_query, issue_table[:created_at], metric_attributes) query = base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])). - project(diff_fn.as('issue_diff'), *issue_projections). + project(extract_epoch(diff_fn).as('issue_diff'), *issue_projections). order(issue_table[:created_at].desc) - ActiveRecord::Base.connection.execute(query.to_sql).first + ActiveRecord::Base.connection.execute(query.to_sql) end def metric_attributes @@ -31,6 +31,10 @@ module Gitlab def user_table User.arel_table end + + def extract_epoch(arel_attribute) + Arel.sql(%Q{EXTRACT(EPOCH FROM (#{arel_attribute.to_sql}))}) + end end end end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index b32c4d6baa0..543259f7cc2 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -12,10 +12,10 @@ describe Gitlab::CycleAnalytics::Events do end describe '#issue' do - let!(:context) { create(:issue, project: project) } + let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } it 'has an issue diff' do - expect(subject.issue_events['issue_diff']).to eq("-00:00:00.339259") + expect(subject.issue_events['issue_diff']).to eq('2 days ago') end it 'has a title' do @@ -23,11 +23,11 @@ describe Gitlab::CycleAnalytics::Events do end it 'has an iid' do - expect(subject.issue_events['iid']).to eq(context.iid) + expect(subject.issue_events['iid']).to eq(context.iid.to_s) end it 'has a created_at timestamp' do - expect(subject.issue_events['created_at']).to eq(context.created_at) + expect(subject.issue_events['created_at']).to eq('2 days ago') end it "has the author's name" do -- cgit v1.2.1 From 1d6068a1c3c906e0e82f0dce884e59aa0da76506 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 18 Oct 2016 10:06:42 +0200 Subject: fix specs - issue events working --- lib/gitlab/cycle_analytics/events.rb | 7 ++++++- lib/gitlab/cycle_analytics/events_fetcher.rb | 2 +- spec/lib/gitlab/cycle_analytics/events_spec.rb | 10 +++++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 04cd599f092..d7248b28527 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -11,9 +11,14 @@ module Gitlab def issue_events @fetcher.fetch_issues.each do |event| - event['issue_diff'] = distance_of_time_in_words(event['issue_diff'].to_f) + event['issue_diff'] = interval_in_words(event['issue_diff']) + event['created_at'] = interval_in_words(event['created_at']) end end + + def interval_in_words(diff) + "#{distance_of_time_in_words( diff.to_f)} ago" + end end end end diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 14405f1356a..0920ab73ae1 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -16,7 +16,7 @@ module Gitlab project(extract_epoch(diff_fn).as('issue_diff'), *issue_projections). order(issue_table[:created_at].desc) - ActiveRecord::Base.connection.execute(query.to_sql) + ActiveRecord::Base.connection.execute(query.to_sql).to_a end def metric_attributes diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 543259f7cc2..be62077f01b 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -15,23 +15,23 @@ describe Gitlab::CycleAnalytics::Events do let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } it 'has an issue diff' do - expect(subject.issue_events['issue_diff']).to eq('2 days ago') + expect(subject.issue_events.first['issue_diff']).to eq('2 days ago') end it 'has a title' do - expect(subject.issue_events['title']).to eq(context.title) + expect(subject.issue_events.first['title']).to eq(context.title) end it 'has an iid' do - expect(subject.issue_events['iid']).to eq(context.iid.to_s) + expect(subject.issue_events.first['iid']).to eq(context.iid.to_s) end it 'has a created_at timestamp' do - expect(subject.issue_events['created_at']).to eq('2 days ago') + expect(subject.issue_events.first['created_at']).to end_with('ago') end it "has the author's name" do - expect(subject.issue_events['name']).to eq(context.author.name) + expect(subject.issue_events.first['name']).to eq(context.author.name) end end -- cgit v1.2.1 From 3b5d947730eb1f979bcb9033d63bdceb11fbb8ca Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 19 Oct 2016 12:47:09 +0200 Subject: commit events and spec --- lib/gitlab/cycle_analytics/events.rb | 16 +++++++++++++--- lib/gitlab/cycle_analytics/events_fetcher.rb | 25 +++++++++++++++++++++---- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 4 ++++ spec/lib/gitlab/cycle_analytics/events_spec.rb | 18 +++++++++++++++--- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index d7248b28527..bdd45e72000 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -9,15 +9,25 @@ module Gitlab @fetcher = EventsFetcher.new(project: project, from: from) end + #TODO: backend pagination - specially for commits, etc... + def issue_events - @fetcher.fetch_issues.each do |event| - event['issue_diff'] = interval_in_words(event['issue_diff']) + #TODO figure out what the frontend needs for displaying the avatar + @fetcher.fetch_issue_events.each do |event| + event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) event['created_at'] = interval_in_words(event['created_at']) end end + def plan_events + # TODO sort out 1st referenced commit and parse stuff + @fetcher.fetch_plan_events + end + + private + def interval_in_words(diff) - "#{distance_of_time_in_words( diff.to_f)} ago" + "#{distance_of_time_in_words(diff.to_f)} ago" end end end diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 0920ab73ae1..f676183f08c 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -8,22 +8,39 @@ module Gitlab @from = from end - def fetch_issues + def fetch_issue_events base_query = base_query_for(:issue) - diff_fn = subtract_datetimes_diff(base_query, issue_table[:created_at], metric_attributes) + diff_fn = subtract_datetimes_diff(base_query, issue_table[:created_at], issue_attributes) query = base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])). - project(extract_epoch(diff_fn).as('issue_diff'), *issue_projections). + project(extract_epoch(diff_fn).as('total_time'), *issue_projections). order(issue_table[:created_at].desc) ActiveRecord::Base.connection.execute(query.to_sql).to_a end - def metric_attributes + def fetch_plan_events + base_query = base_query_for(:plan) + diff_fn = subtract_datetimes_diff(base_query, issue_table[:created_at], plan_attributes) + + query = base_query.join(merge_request_diff_table).on(merge_request_diff_table[:merge_request_id].eq(merge_request_table[:id])). + project(merge_request_diff_table[:st_commits].as(:commits), extract_epoch(diff_fn).as('total_time')). + order(issue_table[:created_at].desc) + + ActiveRecord::Base.connection.execute(query.to_sql).to_a + end + + private + + def issue_attributes [issue_metrics_table[:first_associated_with_milestone_at], issue_metrics_table[:first_added_to_board_at]] end + def plan_attributes + issue_attributes + [issue_metrics_table[:first_mentioned_in_commit_at]] + end + def issue_projections [issue_table[:title], issue_table[:iid], issue_table[:created_at], User.arel_table[:name]] end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index 97b55656d79..b5ff4c0ef5a 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -56,6 +56,10 @@ module Gitlab MergeRequest.arel_table end + def merge_request_diff_table + MergeRequestDiff.arel_table + end + def mr_closing_issues_table MergeRequestsClosingIssues.arel_table end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index be62077f01b..ef59e8abc58 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -11,11 +11,11 @@ describe Gitlab::CycleAnalytics::Events do setup(context) end - describe '#issue' do + describe '#issue_events' do let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } - it 'has an issue diff' do - expect(subject.issue_events.first['issue_diff']).to eq('2 days ago') + it 'has the total time' do + expect(subject.issue_events.first['total_time']).to eq('2 days') end it 'has a title' do @@ -35,6 +35,18 @@ describe Gitlab::CycleAnalytics::Events do end end + describe '#plan_events' do + let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } + + xit 'has the first referenced commit' do + expect(subject.plan_events.first['commit']).to eq(project.commit) + end + + it 'has the total time' do + expect(subject.plan_events.first['total_time']).to eq('2 days') + end + end + def setup(context) milestone = create(:milestone, project: project) context.update(milestone: milestone) -- cgit v1.2.1 From 72660d58af13b4a768840307d2870ac08dd088ef Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 19 Oct 2016 15:40:14 +0200 Subject: plan events and spec working. Also added some TODOs to the code. --- lib/gitlab/cycle_analytics/events.rb | 15 +++++++++++++-- lib/gitlab/cycle_analytics/events_fetcher.rb | 8 ++++++-- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 16 ++++++++-------- spec/lib/gitlab/cycle_analytics/events_spec.rb | 4 ++-- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index bdd45e72000..51fa4653634 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -20,12 +20,23 @@ module Gitlab end def plan_events - # TODO sort out 1st referenced commit and parse stuff - @fetcher.fetch_plan_events + @fetcher.fetch_plan_events.each do |event| + event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) + commits = event.delete('commits') + event['commit'] = first_time_reference_commit(commits, event) + end end private + def first_time_reference_commit(commits, event) + st_commit = YAML.load(commits).detect do |commit| + commit['created_at'] == event['first_mentioned_in_commit_at'] + end + + Commit.new(Gitlab::Git::Commit.new(st_commit), @project) + end + def interval_in_words(diff) "#{distance_of_time_in_words(diff.to_f)} ago" end diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index f676183f08c..c25bc6c5f32 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -23,8 +23,8 @@ module Gitlab base_query = base_query_for(:plan) diff_fn = subtract_datetimes_diff(base_query, issue_table[:created_at], plan_attributes) - query = base_query.join(merge_request_diff_table).on(merge_request_diff_table[:merge_request_id].eq(merge_request_table[:id])). - project(merge_request_diff_table[:st_commits].as(:commits), extract_epoch(diff_fn).as('total_time')). + query = base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])). + project(extract_epoch(diff_fn).as('total_time'), *plan_projections). order(issue_table[:created_at].desc) ActiveRecord::Base.connection.execute(query.to_sql).to_a @@ -45,6 +45,10 @@ module Gitlab [issue_table[:title], issue_table[:iid], issue_table[:created_at], User.arel_table[:name]] end + def plan_projections + [mr_diff_table[:st_commits].as('commits'), issue_metrics_table[:first_mentioned_in_commit_at]] + end + def user_table User.arel_table end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index b5ff4c0ef5a..7fcacf652a6 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -35,28 +35,28 @@ module Gitlab where(issue_table[:created_at].gteq(@from)) # Load merge_requests - query = query.join(merge_request_table, Arel::Nodes::OuterJoin). - on(merge_request_table[:id].eq(mr_closing_issues_table[:merge_request_id])). - join(merge_request_metrics_table). - on(merge_request_table[:id].eq(merge_request_metrics_table[:merge_request_id])) + query = query.join(mr_table, Arel::Nodes::OuterJoin). + on(mr_table[:id].eq(mr_closing_issues_table[:merge_request_id])). + join(mr_metrics_table). + on(mr_table[:id].eq(mr_metrics_table[:merge_request_id])) if DEPLOYMENT_METRIC_STAGES.include?(name) # Limit to merge requests that have been deployed to production after `@from` - query.where(merge_request_metrics_table[:first_deployed_to_production_at].gteq(@from)) + query.where(mr_metrics_table[:first_deployed_to_production_at].gteq(@from)) end query end - def merge_request_metrics_table + def mr_metrics_table MergeRequest::Metrics.arel_table end - def merge_request_table + def mr_table MergeRequest.arel_table end - def merge_request_diff_table + def mr_diff_table MergeRequestDiff.arel_table end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index ef59e8abc58..4f8ba465d71 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -38,8 +38,8 @@ describe Gitlab::CycleAnalytics::Events do describe '#plan_events' do let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } - xit 'has the first referenced commit' do - expect(subject.plan_events.first['commit']).to eq(project.commit) + it 'has the first referenced commit' do + expect(subject.plan_events.first['commit'].message).to eq('commit message') end it 'has the total time' do -- cgit v1.2.1 From 1a4ff5d720fa0ec65f925ef381fced3d5f9a040f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 20 Oct 2016 10:08:53 +0200 Subject: Added code events spec and logic. Also fixed SQL issues and refactored the code a bit. --- lib/gitlab/cycle_analytics/events.rb | 11 +++++++-- lib/gitlab/cycle_analytics/events_fetcher.rb | 34 ++++++++++++++++++++++---- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 2 +- lib/gitlab/database/date_time.rb | 4 +-- spec/lib/gitlab/cycle_analytics/events_spec.rb | 30 ++++++++++++++++++++++- 5 files changed, 70 insertions(+), 11 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 51fa4653634..ea8e78d7ab7 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -9,10 +9,10 @@ module Gitlab @fetcher = EventsFetcher.new(project: project, from: from) end - #TODO: backend pagination - specially for commits, etc... + # TODO: backend pagination - specially for commits, etc... def issue_events - #TODO figure out what the frontend needs for displaying the avatar + # TODO figure out what the frontend needs for displaying the avatar @fetcher.fetch_issue_events.each do |event| event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) event['created_at'] = interval_in_words(event['created_at']) @@ -27,6 +27,13 @@ module Gitlab end end + def code_events + @fetcher.fetch_code_events.each do |event| + event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) + event['created_at'] = interval_in_words(event['created_at']) + end + end + private def first_time_reference_commit(commits, event) diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index c25bc6c5f32..cb61cda200d 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -16,18 +16,33 @@ module Gitlab project(extract_epoch(diff_fn).as('total_time'), *issue_projections). order(issue_table[:created_at].desc) - ActiveRecord::Base.connection.execute(query.to_sql).to_a + execute(query) end def fetch_plan_events base_query = base_query_for(:plan) - diff_fn = subtract_datetimes_diff(base_query, issue_table[:created_at], plan_attributes) + diff_fn = subtract_datetimes_diff(base_query, + issue_metrics_table[:first_associated_with_milestone_at], + plan_attributes) query = base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])). project(extract_epoch(diff_fn).as('total_time'), *plan_projections). - order(issue_table[:created_at].desc) + order(issue_metrics_table[:first_associated_with_milestone_at].desc) - ActiveRecord::Base.connection.execute(query.to_sql).to_a + execute(query) + end + + def fetch_code_events + base_query = base_query_for(:code) + diff_fn = subtract_datetimes_diff(base_query, + issue_metrics_table[:first_mentioned_in_commit_at], + issue_table[:created_at]) + + query = base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])). + project(extract_epoch(diff_fn).as('total_time'), *code_projections). + order(mr_table[:created_at].desc) + + execute(query) end private @@ -38,7 +53,8 @@ module Gitlab end def plan_attributes - issue_attributes + [issue_metrics_table[:first_mentioned_in_commit_at]] + [issue_metrics_table[:first_added_to_board_at], + issue_metrics_table[:first_mentioned_in_commit_at]] end def issue_projections @@ -49,6 +65,10 @@ module Gitlab [mr_diff_table[:st_commits].as('commits'), issue_metrics_table[:first_mentioned_in_commit_at]] end + def code_projections + [mr_table[:title], mr_table[:iid], mr_table[:created_at], User.arel_table[:name]] + end + def user_table User.arel_table end @@ -56,6 +76,10 @@ module Gitlab def extract_epoch(arel_attribute) Arel.sql(%Q{EXTRACT(EPOCH FROM (#{arel_attribute.to_sql}))}) end + + def execute(query) + ActiveRecord::Base.connection.execute(query.to_sql).to_a + end end end end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index 7fcacf652a6..62988fbdec1 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -17,7 +17,7 @@ module Gitlab # cycle analytics stage. interval_query = Arel::Nodes::As.new( cte_table, - subtract_datetimes(base_query_for(name), end_time_attrs, start_time_attrs, name.to_s)) + subtract_datetimes(base_query_for(name), start_time_attrs, end_time_attrs, name.to_s)) median_datetime(cte_table, interval_query, name) end diff --git a/lib/gitlab/database/date_time.rb b/lib/gitlab/database/date_time.rb index e2524886de2..38a6fd4276b 100644 --- a/lib/gitlab/database/date_time.rb +++ b/lib/gitlab/database/date_time.rb @@ -7,13 +7,13 @@ module Gitlab # # Note: For MySQL, the interval is returned in seconds. # For PostgreSQL, the interval is returned as an INTERVAL type. - def subtract_datetimes(query_so_far, end_time_attrs, start_time_attrs, as) + def subtract_datetimes(query_so_far, start_time_attrs, end_time_attrs, as) diff_fn = subtract_datetimes_diff(query_so_far, end_time_attrs, start_time_attrs) query_so_far.project(diff_fn.as(as)) end - def subtract_datetimes_diff(query_so_far, end_time_attrs, start_time_attrs) + def subtract_datetimes_diff(query_so_far, start_time_attrs, end_time_attrs) if Gitlab::Database.postgresql? Arel::Nodes::Subtraction.new( Arel::Nodes::NamedFunction.new("COALESCE", Array.wrap(end_time_attrs)), diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 4f8ba465d71..4a329737c7e 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -43,7 +43,35 @@ describe Gitlab::CycleAnalytics::Events do end it 'has the total time' do - expect(subject.plan_events.first['total_time']).to eq('2 days') + expect(subject.plan_events.first['total_time']).to eq('less than a minute') + end + end + + describe '#code_events' do + let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } + + before do + create_commit_referencing_issue(context) + end + + it 'has the total time' do + expect(subject.code_events.first['total_time']).to eq('2 days') + end + + it 'has a title' do + expect(subject.code_events.first['title']).to eq('Awesome merge_request') + end + + it 'has an iid' do + expect(subject.code_events.first['iid']).to eq(context.iid.to_s) + end + + it 'has a created_at timestamp' do + expect(subject.code_events.first['created_at']).to end_with('ago') + end + + it "has the author's name" do + expect(subject.code_events.first['name']).to eq(context.author.name) end end -- cgit v1.2.1 From 52e2729bf44ff3376071c2462679b46e9f67a44e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 20 Oct 2016 13:14:23 +0200 Subject: add pipeline id to merge request metrics table. Also, updated the pipeline worker to populate this field. --- app/models/merge_request.rb | 2 +- app/models/merge_request/metrics.rb | 1 + app/workers/pipeline_metrics_worker.rb | 4 +-- ...353_add_pipeline_id_to_merge_request_metrics.rb | 29 ++++++++++++++++++++++ db/schema.rb | 3 +++ 5 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index d76feb9680e..a3eb815dd34 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -232,7 +232,7 @@ class MergeRequest < ActiveRecord::Base end def diff_head_commit - if persisted? + if persisted?diff_head_commit merge_request_diff.head_commit else source_branch_head diff --git a/app/models/merge_request/metrics.rb b/app/models/merge_request/metrics.rb index 99c49a020c9..7d19943769e 100644 --- a/app/models/merge_request/metrics.rb +++ b/app/models/merge_request/metrics.rb @@ -1,5 +1,6 @@ class MergeRequest::Metrics < ActiveRecord::Base belongs_to :merge_request + belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :ci_commit_id def record! if merge_request.merged? && self.merged_at.blank? diff --git a/app/workers/pipeline_metrics_worker.rb b/app/workers/pipeline_metrics_worker.rb index 34f6ef161fb..cca39761d8f 100644 --- a/app/workers/pipeline_metrics_worker.rb +++ b/app/workers/pipeline_metrics_worker.rb @@ -12,11 +12,11 @@ class PipelineMetricsWorker private def update_metrics_for_active_pipeline(pipeline) - metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: nil) + metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: nil, ci_commit_id: pipeline.id) end def update_metrics_for_succeeded_pipeline(pipeline) - metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: pipeline.finished_at) + metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: pipeline.finished_at, ci_commit_id: pipeline.id) end def metrics(pipeline) diff --git a/db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb b/db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb new file mode 100644 index 00000000000..a47e1ae7f6a --- /dev/null +++ b/db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb @@ -0,0 +1,29 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddPipelineIdToMergeRequestMetrics < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = true + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + DOWNTIME_REASON = 'Adding a foreign key' + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + # disable_ddl_transaction! + + def change + add_reference :merge_request_metrics, :ci_commit, index: true, foreign_key: { on_delete: :cascade } + end +end diff --git a/db/schema.rb b/db/schema.rb index ed4dfc786f6..5baa82e1058 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -634,8 +634,10 @@ ActiveRecord::Schema.define(version: 20161109150329) do t.datetime "merged_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "ci_commit_id" end + add_index "merge_request_metrics", ["ci_commit_id"], name: "index_merge_request_metrics_on_ci_commit_id", using: :btree add_index "merge_request_metrics", ["first_deployed_to_production_at"], name: "index_merge_request_metrics_on_first_deployed_to_production_at", using: :btree add_index "merge_request_metrics", ["merge_request_id"], name: "index_merge_request_metrics", using: :btree @@ -1244,6 +1246,7 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_foreign_key "labels", "namespaces", column: "group_id", on_delete: :cascade add_foreign_key "lists", "boards" add_foreign_key "lists", "labels" + add_foreign_key "merge_request_metrics", "ci_commits", on_delete: :cascade add_foreign_key "merge_request_metrics", "merge_requests", on_delete: :cascade add_foreign_key "merge_requests_closing_issues", "issues", on_delete: :cascade add_foreign_key "merge_requests_closing_issues", "merge_requests", on_delete: :cascade -- cgit v1.2.1 From ebd5ced7eb296ce10160021d8999d21b36b24da9 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 20 Oct 2016 16:20:04 +0200 Subject: Added test events specs and logic. Also fixed some SQL and refactored the pipeline worker spec. --- app/models/merge_request.rb | 2 +- lib/gitlab/cycle_analytics/events.rb | 7 +++++++ lib/gitlab/cycle_analytics/events_fetcher.rb | 14 +++++++++++++- spec/lib/gitlab/cycle_analytics/events_spec.rb | 24 +++++++++++++++++++++++- spec/workers/pipeline_metrics_worker_spec.rb | 18 +++++++++++------- 5 files changed, 55 insertions(+), 10 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index a3eb815dd34..d76feb9680e 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -232,7 +232,7 @@ class MergeRequest < ActiveRecord::Base end def diff_head_commit - if persisted?diff_head_commit + if persisted? merge_request_diff.head_commit else source_branch_head diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index ea8e78d7ab7..739c8772080 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -34,6 +34,13 @@ module Gitlab end end + def test_events + @fetcher.fetch_test_events.each do |event| + event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) + event['pipeline'] = ::Ci::Pipeline.find_by_id(event['ci_commit_id']) # we may not have a pipeline + end + end + private def first_time_reference_commit(commits, event) diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index cb61cda200d..d12161be0d3 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -36,7 +36,7 @@ module Gitlab base_query = base_query_for(:code) diff_fn = subtract_datetimes_diff(base_query, issue_metrics_table[:first_mentioned_in_commit_at], - issue_table[:created_at]) + mr_table[:created_at]) query = base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])). project(extract_epoch(diff_fn).as('total_time'), *code_projections). @@ -45,6 +45,18 @@ module Gitlab execute(query) end + def fetch_test_events + base_query = base_query_for(:code) + diff_fn = subtract_datetimes_diff(base_query, + mr_metrics_table[:latest_build_started_at], + mr_metrics_table[:latest_build_finished_at]) + + query = base_query.project(extract_epoch(diff_fn).as('total_time'), mr_metrics_table[:ci_commit_id]). + order(mr_table[:created_at].desc) + + execute(query) + end + private def issue_attributes diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 4a329737c7e..404d9e6912c 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -55,7 +55,7 @@ describe Gitlab::CycleAnalytics::Events do end it 'has the total time' do - expect(subject.code_events.first['total_time']).to eq('2 days') + expect(subject.code_events.first['total_time']).to eq('less than a minute') end it 'has a title' do @@ -75,6 +75,28 @@ describe Gitlab::CycleAnalytics::Events do end end + describe '#test_events' do + let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } + let(:merge_request) { MergeRequest.first } + let!(:pipeline) { create(:ci_pipeline, + ref: merge_request.source_branch, + sha: merge_request.diff_head_sha, + project: context.project) } + + before do + pipeline.run! + pipeline.succeed! + end + + it 'has the build info as a pipeline' do + expect(subject.test_events.first['pipeline']).to eq(pipeline) + end + + it 'has the total time' do + expect(subject.test_events.first['total_time']).to eq('less than a minute') + end + end + def setup(context) milestone = create(:milestone, project: project) context.update(milestone: milestone) diff --git a/spec/workers/pipeline_metrics_worker_spec.rb b/spec/workers/pipeline_metrics_worker_spec.rb index 2c9e7c2cd02..2d47d93acec 100644 --- a/spec/workers/pipeline_metrics_worker_spec.rb +++ b/spec/workers/pipeline_metrics_worker_spec.rb @@ -15,32 +15,36 @@ describe PipelineMetricsWorker do end describe '#perform' do - subject { described_class.new.perform(pipeline.id) } + before do + described_class.new.perform(pipeline.id) + end context 'when pipeline is running' do let(:status) { 'running' } it 'records the build start time' do - subject - expect(merge_request.reload.metrics.latest_build_started_at).to be_like_time(pipeline.started_at) end it 'clears the build end time' do - subject - expect(merge_request.reload.metrics.latest_build_finished_at).to be_nil end + + it 'records the pipeline' do + expect(merge_request.reload.metrics.pipeline).to eq(pipeline) + end end context 'when pipeline succeeded' do let(:status) { 'success' } it 'records the build end time' do - subject - expect(merge_request.reload.metrics.latest_build_finished_at).to be_like_time(pipeline.finished_at) end + + it 'records the pipeline' do + expect(merge_request.reload.metrics.pipeline).to eq(pipeline) + end end end end -- cgit v1.2.1 From d99cec7f55fb52dcddc380592e2fbb5ffc735f74 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 21 Oct 2016 08:50:27 +0200 Subject: review events - spec and logic --- lib/gitlab/cycle_analytics/events.rb | 19 +++++++++++-------- lib/gitlab/cycle_analytics/events_fetcher.rb | 13 +++++++++++++ spec/lib/gitlab/cycle_analytics/events_spec.rb | 25 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 739c8772080..42ef864c916 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -13,10 +13,7 @@ module Gitlab def issue_events # TODO figure out what the frontend needs for displaying the avatar - @fetcher.fetch_issue_events.each do |event| - event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) - event['created_at'] = interval_in_words(event['created_at']) - end + @fetcher.fetch_issue_events { |event| parse_event(event) } end def plan_events @@ -28,10 +25,7 @@ module Gitlab end def code_events - @fetcher.fetch_code_events.each do |event| - event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) - event['created_at'] = interval_in_words(event['created_at']) - end + @fetcher.fetch_code_events { |event| parse_event(event) } end def test_events @@ -41,6 +35,15 @@ module Gitlab end end + def review_events + @fetcher.fetch_review_events.each { |event| parse_event(event) } + end + + def parse_event(event) + event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) + event['created_at'] = interval_in_words(event['created_at']) + end + private def first_time_reference_commit(commits, event) diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index d12161be0d3..1be68c771cd 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -57,6 +57,19 @@ module Gitlab execute(query) end + def fetch_review_events + base_query = base_query_for(:code) + diff_fn = subtract_datetimes_diff(base_query, + mr_table[:created_at], + mr_metrics_table[:merged_at]) + + query = base_query.join(user_table).on(mr_table[:author_id].eq(user_table[:id])). + project(extract_epoch(diff_fn).as('total_time'), *code_projections). + order(mr_table[:created_at].desc) + + execute(query) + end + private def issue_attributes diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 404d9e6912c..ae1172cd653 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -97,6 +97,31 @@ describe Gitlab::CycleAnalytics::Events do end end + describe '#review_events' do + let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } + + it 'has the total time' do + expect(subject.review_events.first['total_time']).to eq('less than a minute') + end + + it 'has a title' do + expect(subject.review_events.first['title']).to eq('Awesome merge_request') + end + + it 'has an iid' do + expect(subject.review_events.first['iid']).to eq(context.iid.to_s) + end + + it 'has a created_at timestamp' do + expect(subject.review_events.first['created_at']).to end_with('ago') + end + + it "has the author's name" do + expect(subject.review_events.first['name']).to eq(MergeRequest.first.author.name) + end + end + + def setup(context) milestone = create(:milestone, project: project) context.update(milestone: milestone) -- cgit v1.2.1 From f8acc7ea77bb52531b11abea5eabd68a38236cff Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 21 Oct 2016 09:44:04 +0200 Subject: fixing spec failures --- lib/gitlab/cycle_analytics/events.rb | 8 ++++---- lib/gitlab/database/date_time.rb | 2 +- spec/lib/gitlab/cycle_analytics/events_spec.rb | 11 ++++++----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 42ef864c916..a16c15ed3a8 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -13,7 +13,7 @@ module Gitlab def issue_events # TODO figure out what the frontend needs for displaying the avatar - @fetcher.fetch_issue_events { |event| parse_event(event) } + @fetcher.fetch_issue_events.each { |event| parse_event(event) } end def plan_events @@ -25,7 +25,7 @@ module Gitlab end def code_events - @fetcher.fetch_code_events { |event| parse_event(event) } + @fetcher.fetch_code_events.each { |event| parse_event(event) } end def test_events @@ -39,13 +39,13 @@ module Gitlab @fetcher.fetch_review_events.each { |event| parse_event(event) } end + private + def parse_event(event) event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) event['created_at'] = interval_in_words(event['created_at']) end - private - def first_time_reference_commit(commits, event) st_commit = YAML.load(commits).detect do |commit| commit['created_at'] == event['first_mentioned_in_commit_at'] diff --git a/lib/gitlab/database/date_time.rb b/lib/gitlab/database/date_time.rb index 38a6fd4276b..25e56998038 100644 --- a/lib/gitlab/database/date_time.rb +++ b/lib/gitlab/database/date_time.rb @@ -8,7 +8,7 @@ module Gitlab # Note: For MySQL, the interval is returned in seconds. # For PostgreSQL, the interval is returned as an INTERVAL type. def subtract_datetimes(query_so_far, start_time_attrs, end_time_attrs, as) - diff_fn = subtract_datetimes_diff(query_so_far, end_time_attrs, start_time_attrs) + diff_fn = subtract_datetimes_diff(query_so_far, start_time_attrs, end_time_attrs) query_so_far.project(diff_fn.as(as)) end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index ae1172cd653..17db46f0d4a 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -78,10 +78,12 @@ describe Gitlab::CycleAnalytics::Events do describe '#test_events' do let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } let(:merge_request) { MergeRequest.first } - let!(:pipeline) { create(:ci_pipeline, - ref: merge_request.source_branch, - sha: merge_request.diff_head_sha, - project: context.project) } + let!(:pipeline) do + create(:ci_pipeline, + ref: merge_request.source_branch, + sha: merge_request.diff_head_sha, + project: context.project) + end before do pipeline.run! @@ -121,7 +123,6 @@ describe Gitlab::CycleAnalytics::Events do end end - def setup(context) milestone = create(:milestone, project: project) context.update(milestone: milestone) -- cgit v1.2.1 From 1f701cb5e2fc9ea3af98ab0e6e07a7568a0e54dc Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 21 Oct 2016 11:33:37 +0200 Subject: added staging events and spec --- lib/gitlab/cycle_analytics/events.rb | 7 +++++++ lib/gitlab/cycle_analytics/events_fetcher.rb | 16 ++++++++++++++-- spec/lib/gitlab/cycle_analytics/events_spec.rb | 26 ++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index a16c15ed3a8..3c0b9a7ddfa 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -39,6 +39,13 @@ module Gitlab @fetcher.fetch_review_events.each { |event| parse_event(event) } end + def staging_events + @fetcher.fetch_staging_events.each do |event| + event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) + event['pipeline'] = ::Ci::Pipeline.find_by_id(event['ci_commit_id']) # we may not have a pipeline + end + end + private def parse_event(event) diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 1be68c771cd..bddaede53b7 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -46,7 +46,7 @@ module Gitlab end def fetch_test_events - base_query = base_query_for(:code) + base_query = base_query_for(:test) diff_fn = subtract_datetimes_diff(base_query, mr_metrics_table[:latest_build_started_at], mr_metrics_table[:latest_build_finished_at]) @@ -58,7 +58,7 @@ module Gitlab end def fetch_review_events - base_query = base_query_for(:code) + base_query = base_query_for(:review) diff_fn = subtract_datetimes_diff(base_query, mr_table[:created_at], mr_metrics_table[:merged_at]) @@ -70,6 +70,18 @@ module Gitlab execute(query) end + def fetch_staging_events + base_query = base_query_for(:staging) + diff_fn = subtract_datetimes_diff(base_query, + mr_metrics_table[:merged_at], + mr_metrics_table[:first_deployed_to_production_at]) + + query = base_query.project(extract_epoch(diff_fn).as('total_time'), mr_metrics_table[:ci_commit_id]). + order(mr_table[:created_at].desc) + + execute(query) + end + private def issue_attributes diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 17db46f0d4a..0212ce60b92 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -123,6 +123,32 @@ describe Gitlab::CycleAnalytics::Events do end end + describe '#staging_events' do + let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } + let(:merge_request) { MergeRequest.first } + let!(:pipeline) do + create(:ci_pipeline, + ref: merge_request.source_branch, + sha: merge_request.diff_head_sha, + project: context.project) + end + + before do + pipeline.run! + pipeline.succeed! + merge_merge_requests_closing_issue(context) + deploy_master + end + + it 'has the build info as a pipeline' do + expect(subject.staging_events.first['pipeline']).to eq(pipeline) + end + + it 'has the total time' do + expect(subject.staging_events.first['total_time']).to eq('less than a minute') + end + end + def setup(context) milestone = create(:milestone, project: project) context.update(milestone: milestone) -- cgit v1.2.1 From 954e18922e7cacfa4ad39c6c27d34bd94c3038b2 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 21 Oct 2016 11:37:47 +0200 Subject: fix mySQL error --- lib/gitlab/cycle_analytics/events_fetcher.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index bddaede53b7..742ee2f4fb6 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -111,6 +111,8 @@ module Gitlab end def extract_epoch(arel_attribute) + return arel_attribute unless Gitlab::Database.postgresql? + Arel.sql(%Q{EXTRACT(EPOCH FROM (#{arel_attribute.to_sql}))}) end -- cgit v1.2.1 From 275292de4768ab9893d9d786b9fbb72bf173ba3c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 21 Oct 2016 17:01:53 +0200 Subject: Refactor the SQL/query stuff into a dynamic class. Also merged all the config in a single hash. --- lib/gitlab/cycle_analytics/events.rb | 12 +- lib/gitlab/cycle_analytics/events_fetcher.rb | 154 +++++++++----------------- lib/gitlab/cycle_analytics/events_query.rb | 41 +++++++ lib/gitlab/cycle_analytics/metrics_fetcher.rb | 8 ++ 4 files changed, 108 insertions(+), 107 deletions(-) create mode 100644 lib/gitlab/cycle_analytics/events_query.rb diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 3c0b9a7ddfa..3ab544faf5d 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -13,11 +13,11 @@ module Gitlab def issue_events # TODO figure out what the frontend needs for displaying the avatar - @fetcher.fetch_issue_events.each { |event| parse_event(event) } + @fetcher.fetch(stage: :issue).each { |event| parse_event(event) } end def plan_events - @fetcher.fetch_plan_events.each do |event| + @fetcher.fetch(stage: :plan).each do |event| event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) commits = event.delete('commits') event['commit'] = first_time_reference_commit(commits, event) @@ -25,22 +25,22 @@ module Gitlab end def code_events - @fetcher.fetch_code_events.each { |event| parse_event(event) } + @fetcher.fetch(stage: :code).each { |event| parse_event(event) } end def test_events - @fetcher.fetch_test_events.each do |event| + @fetcher.fetch(stage: :test).each do |event| event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) event['pipeline'] = ::Ci::Pipeline.find_by_id(event['ci_commit_id']) # we may not have a pipeline end end def review_events - @fetcher.fetch_review_events.each { |event| parse_event(event) } + @fetcher.fetch(stage: :review).each { |event| parse_event(event) } end def staging_events - @fetcher.fetch_staging_events.each do |event| + @fetcher.fetch(stage: :staging).each do |event| event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) event['pipeline'] = ::Ci::Pipeline.find_by_id(event['ci_commit_id']) # we may not have a pipeline end diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 742ee2f4fb6..75a6779e0e8 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -3,121 +3,73 @@ module Gitlab class EventsFetcher include MetricsFetcher + EVENTS_CONFIG = { + issue: { + start_time_attrs: issue_table[:created_at], + end_time_attrs: [issue_metrics_table[:first_associated_with_milestone_at], + issue_metrics_table[:first_added_to_board_at]], + projections: [issue_table[:title], issue_table[:iid], issue_table[:created_at], user_table[:name]], + project: @project, + from: @from + }, + plan: { + start_time_attrs: issue_metrics_table[:first_associated_with_milestone_at], + end_time_attrs: [issue_metrics_table[:first_added_to_board_at], + issue_metrics_table[:first_mentioned_in_commit_at]], + projections: [mr_diff_table[:st_commits].as('commits'), issue_metrics_table[:first_mentioned_in_commit_at]] + }, + code: { + start_time_attrs: issue_metrics_table[:first_mentioned_in_commit_at], + end_time_attrs: mr_table[:created_at], + projections: [mr_table[:title], mr_table[:iid], mr_table[:created_at], user_table[:name]], + order: mr_table[:created_at] + }, + test: { + start_time_attrs: mr_metrics_table[:latest_build_started_at], + end_time_attrs: mr_metrics_table[:latest_build_finished_at], + projections: mr_metrics_table[:ci_commit_id], + order: mr_table[:created_at] + }, + review: { + start_time_attrs: mr_table[:created_at], + end_time_attrs: mr_metrics_table[:merged_at], + projections: [mr_table[:title], mr_table[:iid], mr_table[:created_at], user_table[:name]] + }, + staging: { + start_time_attrs: mr_metrics_table[:merged_at], + end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], + projections: mr_metrics_table[:ci_commit_id] + } + }.freeze + def initialize(project:, from:) @project = project @from = from + @query = EventsQuery.new(project: project, from: from) end - def fetch_issue_events - base_query = base_query_for(:issue) - diff_fn = subtract_datetimes_diff(base_query, issue_table[:created_at], issue_attributes) - - query = base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])). - project(extract_epoch(diff_fn).as('total_time'), *issue_projections). - order(issue_table[:created_at].desc) - - execute(query) - end - - def fetch_plan_events - base_query = base_query_for(:plan) - diff_fn = subtract_datetimes_diff(base_query, - issue_metrics_table[:first_associated_with_milestone_at], - plan_attributes) - - query = base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])). - project(extract_epoch(diff_fn).as('total_time'), *plan_projections). - order(issue_metrics_table[:first_associated_with_milestone_at].desc) - - execute(query) - end - - def fetch_code_events - base_query = base_query_for(:code) - diff_fn = subtract_datetimes_diff(base_query, - issue_metrics_table[:first_mentioned_in_commit_at], - mr_table[:created_at]) - - query = base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])). - project(extract_epoch(diff_fn).as('total_time'), *code_projections). - order(mr_table[:created_at].desc) - - execute(query) - end - - def fetch_test_events - base_query = base_query_for(:test) - diff_fn = subtract_datetimes_diff(base_query, - mr_metrics_table[:latest_build_started_at], - mr_metrics_table[:latest_build_finished_at]) - - query = base_query.project(extract_epoch(diff_fn).as('total_time'), mr_metrics_table[:ci_commit_id]). - order(mr_table[:created_at].desc) - - execute(query) - end - - def fetch_review_events - base_query = base_query_for(:review) - diff_fn = subtract_datetimes_diff(base_query, - mr_table[:created_at], - mr_metrics_table[:merged_at]) - - query = base_query.join(user_table).on(mr_table[:author_id].eq(user_table[:id])). - project(extract_epoch(diff_fn).as('total_time'), *code_projections). - order(mr_table[:created_at].desc) - - execute(query) - end - - def fetch_staging_events - base_query = base_query_for(:staging) - diff_fn = subtract_datetimes_diff(base_query, - mr_metrics_table[:merged_at], - mr_metrics_table[:first_deployed_to_production_at]) - - query = base_query.project(extract_epoch(diff_fn).as('total_time'), mr_metrics_table[:ci_commit_id]). - order(mr_table[:created_at].desc) - - execute(query) - end - - private - - def issue_attributes - [issue_metrics_table[:first_associated_with_milestone_at], - issue_metrics_table[:first_added_to_board_at]] - end - - def plan_attributes - [issue_metrics_table[:first_added_to_board_at], - issue_metrics_table[:first_mentioned_in_commit_at]] - end - - def issue_projections - [issue_table[:title], issue_table[:iid], issue_table[:created_at], User.arel_table[:name]] - end + def fetch(stage:) + custom_query = "#{stage}_custom_query".to_sym - def plan_projections - [mr_diff_table[:st_commits].as('commits'), issue_metrics_table[:first_mentioned_in_commit_at]] + @query.execute(stage, EVENTS_CONFIG[stage]) do |base_query| + public_send(custom_query, base_query) if self.respond_to?(custom_query) + end end - def code_projections - [mr_table[:title], mr_table[:iid], mr_table[:created_at], User.arel_table[:name]] + def issue_custom_query(base_query) + base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])) end - def user_table - User.arel_table + def plan_custom_query(base_query) + base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) end - def extract_epoch(arel_attribute) - return arel_attribute unless Gitlab::Database.postgresql? - - Arel.sql(%Q{EXTRACT(EPOCH FROM (#{arel_attribute.to_sql}))}) + def code_custom_query(base_query) + base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])) end - def execute(query) - ActiveRecord::Base.connection.execute(query.to_sql).to_a + def review_custom_query(base_query) + base_query.join(user_table).on(mr_table[:author_id].eq(user_table[:id])) end end end diff --git a/lib/gitlab/cycle_analytics/events_query.rb b/lib/gitlab/cycle_analytics/events_query.rb new file mode 100644 index 00000000000..39687305bc3 --- /dev/null +++ b/lib/gitlab/cycle_analytics/events_query.rb @@ -0,0 +1,41 @@ +module Gitlab + module CycleAnalytics + class EventsQuery + include MetricsFetcher + + def initialize(project:, from:) + @project = project + @from = from + end + + def execute(stage, config, &block) + @stage = stage + @config = config + query = build_query(&block) + + ActiveRecord::Base.connection.execute(query.to_sql).to_a + end + + private + + def build_query + base_query = base_query_for(@stage) + diff_fn = subtract_datetimes_diff(@config[:base_query], @config[:start_time_attrs], @config[:end_time_attrs]) + + yield base_query if block_given? + + base_query.project(extract_epoch(diff_fn).as('total_time'), *@config[:projections]).order(order.desc) + end + + def order + @config[:order] || @config[:start_time_attrs] + end + + def extract_epoch(arel_attribute) + return arel_attribute unless Gitlab::Database.postgresql? + + Arel.sql(%Q{EXTRACT(EPOCH FROM (#{arel_attribute.to_sql}))}) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index 62988fbdec1..c1cbedc045b 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -6,6 +6,10 @@ module Gitlab DEPLOYMENT_METRIC_STAGES = %i[production staging] + def self.included(klass) + klass.extend self + end + private def calculate_metric(name, start_time_attrs, end_time_attrs) @@ -71,6 +75,10 @@ module Gitlab def issue_metrics_table Issue::Metrics.arel_table end + + def user_table + User.arel_table + end end end end -- cgit v1.2.1 From 3cdc9af78ebb71e2b91046e9dea8695aa04f6713 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 21 Oct 2016 17:30:38 +0200 Subject: added production events and related spec --- lib/gitlab/cycle_analytics/events.rb | 6 +++++- lib/gitlab/cycle_analytics/events_fetcher.rb | 20 +++++++++--------- spec/lib/gitlab/cycle_analytics/events_spec.rb | 29 ++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 3ab544faf5d..912694fa1ad 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -10,9 +10,9 @@ module Gitlab end # TODO: backend pagination - specially for commits, etc... + # TODO figure out what the frontend needs for displaying the avatar def issue_events - # TODO figure out what the frontend needs for displaying the avatar @fetcher.fetch(stage: :issue).each { |event| parse_event(event) } end @@ -46,6 +46,10 @@ module Gitlab end end + def production_events + @fetcher.fetch(stage: :production).each { |event| parse_event(event) } + end + private def parse_event(event) diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 75a6779e0e8..03b1878dc1c 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -8,9 +8,7 @@ module Gitlab start_time_attrs: issue_table[:created_at], end_time_attrs: [issue_metrics_table[:first_associated_with_milestone_at], issue_metrics_table[:first_added_to_board_at]], - projections: [issue_table[:title], issue_table[:iid], issue_table[:created_at], user_table[:name]], - project: @project, - from: @from + projections: [issue_table[:title], issue_table[:iid], issue_table[:created_at], user_table[:name]] }, plan: { start_time_attrs: issue_metrics_table[:first_associated_with_milestone_at], @@ -39,12 +37,15 @@ module Gitlab start_time_attrs: mr_metrics_table[:merged_at], end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], projections: mr_metrics_table[:ci_commit_id] - } + }, + production: { + start_time_attrs: issue_table[:created_at], + end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], + projections: [issue_table[:title], issue_table[:iid], issue_table[:created_at], user_table[:name]] + }, }.freeze def initialize(project:, from:) - @project = project - @from = from @query = EventsQuery.new(project: project, from: from) end @@ -60,14 +61,13 @@ module Gitlab base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])) end + alias_method :code_custom_query, :issue_custom_query + alias_method :production_custom_query, :issue_custom_query + def plan_custom_query(base_query) base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) end - def code_custom_query(base_query) - base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])) - end - def review_custom_query(base_query) base_query.join(user_table).on(mr_table[:author_id].eq(user_table[:id])) end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 0212ce60b92..410310d3029 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -149,6 +149,35 @@ describe Gitlab::CycleAnalytics::Events do end end + describe '#production_events' do + let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } + + before do + merge_merge_requests_closing_issue(context) + deploy_master + end + + it 'has the total time' do + expect(subject.production_events.first['total_time']).to eq('2 days') + end + + it 'has a title' do + expect(subject.production_events.first['title']).to eq(context.title) + end + + it 'has an iid' do + expect(subject.production_events.first['iid']).to eq(context.iid.to_s) + end + + it 'has a created_at timestamp' do + expect(subject.production_events.first['created_at']).to end_with('ago') + end + + it "has the author's name" do + expect(subject.production_events.first['name']).to eq(context.author.name) + end + end + def setup(context) milestone = create(:milestone, project: project) context.update(milestone: milestone) -- cgit v1.2.1 From d4726112656df24ff7721865a96237d5168466f7 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 24 Oct 2016 15:24:56 +0100 Subject: added cycle analytics events controller and started integration spec --- .../projects/cycle_analytics/events_controller.rb | 34 ++++++++++++++++++++++ config/routes/project.rb | 6 ++++ .../projects/cycle_analytics_events_spec.rb | 24 +++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 app/controllers/projects/cycle_analytics/events_controller.rb create mode 100644 spec/requests/projects/cycle_analytics_events_spec.rb diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb new file mode 100644 index 00000000000..fe0135874b9 --- /dev/null +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -0,0 +1,34 @@ +class Projects::CycleAnalytics::EventsController < Projects::ApplicationController + #before_action :authorize_read_cycle_analytics! + + def issues + respond_to do |format| + format.html + format.json { render json: events.issue_events } + end + end + + private + + # TODO refactor this + def start_date + case events_params[:start_date] + when '30' then + 30.days.ago + when '90' then + 90.days.ago + else + 90.days.ago + end + end + + def events + @events ||= Gitlab::CycleAnalytics::Events.new(project: project, from: start_date) + end + + def events_params + return {} unless params[:events].present? + + { start_date: params[:events][:start_date] } + end +end \ No newline at end of file diff --git a/config/routes/project.rb b/config/routes/project.rb index 9cf8465dca8..ed595c777f5 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -153,6 +153,12 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: resource :cycle_analytics, only: [:show] + namespace :cycle_analytics do + scope :events, controller: '/projects/cycle_analytics/events' do + get :issues + end + end + resources :builds, only: [:index, :show], constraints: { id: /\d+/ } do collection do post :cancel_all diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb new file mode 100644 index 00000000000..064434c127e --- /dev/null +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe 'cycle analytics events' do + let(:user) { create(:user) } + let(:project) { create(:project) } + + describe 'GET /:namespace/:project/cycle_analytics/events/issues' do + before do + project.team << [user, :developer] + + login_as(user) + end + + it 'lists the issue events' do + get namespace_project_cycle_analytics_issues_path(project.namespace, project, format: :json) + + expect(json_response).to eq ([]) + end + end + + def json_response + JSON.parse(response.body) + end +end -- cgit v1.2.1 From 32679232635dbd0d196a91d0788ee1135ff56b43 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 24 Oct 2016 15:49:25 +0100 Subject: added the rest of the stages to the controller and relevant specs --- .../projects/cycle_analytics/events_controller.rb | 36 ++++++++++++++-- config/routes/project.rb | 8 +++- .../projects/cycle_analytics_events_spec.rb | 48 +++++++++++++++++++++- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index fe0135874b9..142cbc344a5 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -1,15 +1,43 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationController #before_action :authorize_read_cycle_analytics! - def issues + def issue + render_events(events.issue_events) + end + + def plan + render_events(events.plan_events) + end + + def code + render_events(events.code_events) + end + + def test + render_events(events.test_events) + end + + def review + render_events(events.review_events) + end + + def staging + render_events(events.staging_events) + end + + def production + render_events(events.production_events) + end + + private + + def render_events(event_list) respond_to do |format| format.html - format.json { render json: events.issue_events } + format.json { render json: { events: event_list } } end end - private - # TODO refactor this def start_date case events_params[:start_date] diff --git a/config/routes/project.rb b/config/routes/project.rb index ed595c777f5..7e0536b464f 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -155,7 +155,13 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: namespace :cycle_analytics do scope :events, controller: '/projects/cycle_analytics/events' do - get :issues + get :issue + get :plan + get :code + get :test + get :review + get :staging + get :production end end diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index 064434c127e..51db9bb07fa 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -3,18 +3,62 @@ require 'spec_helper' describe 'cycle analytics events' do let(:user) { create(:user) } let(:project) { create(:project) } + let!(:issue) { create(:issue, project: project, created_at: 2.days.ago) } describe 'GET /:namespace/:project/cycle_analytics/events/issues' do before do project.team << [user, :developer] + milestone = create(:milestone, project: project) + issue.update(milestone: milestone) + create_merge_request_closing_issue(issue) + + merge_merge_requests_closing_issue(issue) + deploy_master + login_as(user) end it 'lists the issue events' do - get namespace_project_cycle_analytics_issues_path(project.namespace, project, format: :json) + get namespace_project_cycle_analytics_issue_path(project.namespace, project, format: :json) + + expect(json_response['events']).not_to be_empty + end + + it 'lists the plan events' do + get namespace_project_cycle_analytics_plan_path(project.namespace, project, format: :json) + + expect(json_response['events']).not_to be_empty + end + + it 'lists the code events' do + get namespace_project_cycle_analytics_code_path(project.namespace, project, format: :json) + + expect(json_response['events']).not_to be_empty + end + + it 'lists the test events' do + get namespace_project_cycle_analytics_test_path(project.namespace, project, format: :json) + + expect(json_response['events']).not_to be_empty + end + + it 'lists the review events' do + get namespace_project_cycle_analytics_review_path(project.namespace, project, format: :json) + + expect(json_response['events']).not_to be_empty + end + + it 'lists the staging events' do + get namespace_project_cycle_analytics_staging_path(project.namespace, project, format: :json) + + expect(json_response['events']).not_to be_empty + end + + it 'lists the production events' do + get namespace_project_cycle_analytics_production_path(project.namespace, project, format: :json) - expect(json_response).to eq ([]) + expect(json_response['events']).not_to be_empty end end -- cgit v1.2.1 From ca9ae8bf63ff94c68143823046ab2a7466f30b1c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 27 Oct 2016 17:52:42 +0100 Subject: add email to user related queries so it can be used for displaying avatar in the UI --- .../projects/cycle_analytics/events_controller.rb | 16 ++++++++-------- lib/gitlab/cycle_analytics/events.rb | 1 - lib/gitlab/cycle_analytics/events_fetcher.rb | 8 ++++---- spec/lib/gitlab/cycle_analytics/events_spec.rb | 16 ++++++++++++++++ 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 142cbc344a5..49c7303dccc 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -1,5 +1,5 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationController - #before_action :authorize_read_cycle_analytics! + # before_action :authorize_read_cycle_analytics! def issue render_events(events.issue_events) @@ -41,12 +41,12 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll # TODO refactor this def start_date case events_params[:start_date] - when '30' then - 30.days.ago - when '90' then - 90.days.ago - else - 90.days.ago + when '30' then + 30.days.ago + when '90' then + 90.days.ago + else + 90.days.ago end end @@ -59,4 +59,4 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll { start_date: params[:events][:start_date] } end -end \ No newline at end of file +end diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 912694fa1ad..08070c2bac2 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -10,7 +10,6 @@ module Gitlab end # TODO: backend pagination - specially for commits, etc... - # TODO figure out what the frontend needs for displaying the avatar def issue_events @fetcher.fetch(stage: :issue).each { |event| parse_event(event) } diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 03b1878dc1c..2686ac353d6 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -8,7 +8,7 @@ module Gitlab start_time_attrs: issue_table[:created_at], end_time_attrs: [issue_metrics_table[:first_associated_with_milestone_at], issue_metrics_table[:first_added_to_board_at]], - projections: [issue_table[:title], issue_table[:iid], issue_table[:created_at], user_table[:name]] + projections: [issue_table[:title], issue_table[:iid], issue_table[:created_at], user_table[:name], user_table[:email]] }, plan: { start_time_attrs: issue_metrics_table[:first_associated_with_milestone_at], @@ -19,7 +19,7 @@ module Gitlab code: { start_time_attrs: issue_metrics_table[:first_mentioned_in_commit_at], end_time_attrs: mr_table[:created_at], - projections: [mr_table[:title], mr_table[:iid], mr_table[:created_at], user_table[:name]], + projections: [mr_table[:title], mr_table[:iid], mr_table[:created_at], user_table[:name], user_table[:email]], order: mr_table[:created_at] }, test: { @@ -31,7 +31,7 @@ module Gitlab review: { start_time_attrs: mr_table[:created_at], end_time_attrs: mr_metrics_table[:merged_at], - projections: [mr_table[:title], mr_table[:iid], mr_table[:created_at], user_table[:name]] + projections: [mr_table[:title], mr_table[:iid], mr_table[:created_at], user_table[:name], user_table[:email]] }, staging: { start_time_attrs: mr_metrics_table[:merged_at], @@ -41,7 +41,7 @@ module Gitlab production: { start_time_attrs: issue_table[:created_at], end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], - projections: [issue_table[:title], issue_table[:iid], issue_table[:created_at], user_table[:name]] + projections: [issue_table[:title], issue_table[:iid], issue_table[:created_at], user_table[:name], user_table[:email]] }, }.freeze diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 410310d3029..7d544dbdb19 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -30,6 +30,10 @@ describe Gitlab::CycleAnalytics::Events do expect(subject.issue_events.first['created_at']).to end_with('ago') end + it "has the author's email" do + expect(subject.issue_events.first['email']).to eq(context.author.email) + end + it "has the author's name" do expect(subject.issue_events.first['name']).to eq(context.author.name) end @@ -70,6 +74,10 @@ describe Gitlab::CycleAnalytics::Events do expect(subject.code_events.first['created_at']).to end_with('ago') end + it "has the author's email" do + expect(subject.code_events.first['email']).to eq(context.author.email) + end + it "has the author's name" do expect(subject.code_events.first['name']).to eq(context.author.name) end @@ -118,6 +126,10 @@ describe Gitlab::CycleAnalytics::Events do expect(subject.review_events.first['created_at']).to end_with('ago') end + it "has the author's email" do + expect(subject.review_events.first['email']).to eq(MergeRequest.first.author.email) + end + it "has the author's name" do expect(subject.review_events.first['name']).to eq(MergeRequest.first.author.name) end @@ -173,6 +185,10 @@ describe Gitlab::CycleAnalytics::Events do expect(subject.production_events.first['created_at']).to end_with('ago') end + it "has the author's email" do + expect(subject.production_events.first['email']).to eq(context.author.email) + end + it "has the author's name" do expect(subject.production_events.first['name']).to eq(context.author.name) end -- cgit v1.2.1 From f9fd0ff1ca2a559ef7a82337f66e15e0d2d06e16 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 28 Oct 2016 12:46:38 +0100 Subject: Some refactoring - used class instead of hash for the query configuration --- lib/gitlab/cycle_analytics/events_fetcher.rb | 48 ++------------- lib/gitlab/cycle_analytics/events_query.rb | 4 +- lib/gitlab/cycle_analytics/query_config.rb | 89 ++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 46 deletions(-) create mode 100644 lib/gitlab/cycle_analytics/query_config.rb diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 2686ac353d6..0248ebc7193 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -3,48 +3,6 @@ module Gitlab class EventsFetcher include MetricsFetcher - EVENTS_CONFIG = { - issue: { - start_time_attrs: issue_table[:created_at], - end_time_attrs: [issue_metrics_table[:first_associated_with_milestone_at], - issue_metrics_table[:first_added_to_board_at]], - projections: [issue_table[:title], issue_table[:iid], issue_table[:created_at], user_table[:name], user_table[:email]] - }, - plan: { - start_time_attrs: issue_metrics_table[:first_associated_with_milestone_at], - end_time_attrs: [issue_metrics_table[:first_added_to_board_at], - issue_metrics_table[:first_mentioned_in_commit_at]], - projections: [mr_diff_table[:st_commits].as('commits'), issue_metrics_table[:first_mentioned_in_commit_at]] - }, - code: { - start_time_attrs: issue_metrics_table[:first_mentioned_in_commit_at], - end_time_attrs: mr_table[:created_at], - projections: [mr_table[:title], mr_table[:iid], mr_table[:created_at], user_table[:name], user_table[:email]], - order: mr_table[:created_at] - }, - test: { - start_time_attrs: mr_metrics_table[:latest_build_started_at], - end_time_attrs: mr_metrics_table[:latest_build_finished_at], - projections: mr_metrics_table[:ci_commit_id], - order: mr_table[:created_at] - }, - review: { - start_time_attrs: mr_table[:created_at], - end_time_attrs: mr_metrics_table[:merged_at], - projections: [mr_table[:title], mr_table[:iid], mr_table[:created_at], user_table[:name], user_table[:email]] - }, - staging: { - start_time_attrs: mr_metrics_table[:merged_at], - end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], - projections: mr_metrics_table[:ci_commit_id] - }, - production: { - start_time_attrs: issue_table[:created_at], - end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], - projections: [issue_table[:title], issue_table[:iid], issue_table[:created_at], user_table[:name], user_table[:email]] - }, - }.freeze - def initialize(project:, from:) @query = EventsQuery.new(project: project, from: from) end @@ -52,11 +10,13 @@ module Gitlab def fetch(stage:) custom_query = "#{stage}_custom_query".to_sym - @query.execute(stage, EVENTS_CONFIG[stage]) do |base_query| - public_send(custom_query, base_query) if self.respond_to?(custom_query) + @query.execute(stage) do |base_query| + send(custom_query, base_query) if self.respond_to?(custom_query) end end + private + def issue_custom_query(base_query) base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])) end diff --git a/lib/gitlab/cycle_analytics/events_query.rb b/lib/gitlab/cycle_analytics/events_query.rb index 39687305bc3..f78272a4108 100644 --- a/lib/gitlab/cycle_analytics/events_query.rb +++ b/lib/gitlab/cycle_analytics/events_query.rb @@ -8,9 +8,9 @@ module Gitlab @from = from end - def execute(stage, config, &block) + def execute(stage, &block) @stage = stage - @config = config + @config = QueryConfig.get(stage) query = build_query(&block) ActiveRecord::Base.connection.execute(query.to_sql).to_a diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb new file mode 100644 index 00000000000..5689c070eb7 --- /dev/null +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -0,0 +1,89 @@ +module Gitlab + module CycleAnalytics + class QueryConfig + include MetricsFetcher + + def self.get(*args) + new(*args).get + end + + def initialize(stage) + @stage = stage + end + + def get + send(@stage).freeze if self.respond_to?(@stage) + end + + private + + def issue + { start_time_attrs: issue_table[:created_at], + end_time_attrs: [issue_metrics_table[:first_associated_with_milestone_at], + issue_metrics_table[:first_added_to_board_at]], + projections: [issue_table[:title], + issue_table[:iid], + issue_table[:created_at], + user_table[:name], + user_table[:email]] + } + end + + def plan + { start_time_attrs: issue_metrics_table[:first_associated_with_milestone_at], + end_time_attrs: [issue_metrics_table[:first_added_to_board_at], + issue_metrics_table[:first_mentioned_in_commit_at]], + projections: [mr_diff_table[:st_commits].as('commits'), + issue_metrics_table[:first_mentioned_in_commit_at]] + } + end + + def code + { start_time_attrs: issue_metrics_table[:first_mentioned_in_commit_at], + end_time_attrs: mr_table[:created_at], + projections: [mr_table[:title], mr_table[:iid], + mr_table[:created_at], + user_table[:name], + user_table[:email]], + order: mr_table[:created_at] + } + end + + def test + { start_time_attrs: mr_metrics_table[:latest_build_started_at], + end_time_attrs: mr_metrics_table[:latest_build_finished_at], + projections: mr_metrics_table[:ci_commit_id], + order: mr_table[:created_at] + } + end + + def review + { start_time_attrs: mr_table[:created_at], + end_time_attrs: mr_metrics_table[:merged_at], + projections: [mr_table[:title], mr_table[:iid], + mr_table[:created_at], + user_table[:name], + user_table[:email]] + } + end + + def staging + { start_time_attrs: mr_metrics_table[:merged_at], + end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], + projections: mr_metrics_table[:ci_commit_id] + } + end + + def production + { start_time_attrs: issue_table[:created_at], + end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], + projections: [issue_table[:title], + issue_table[:iid], + issue_table[:created_at], + user_table[:name], + user_table[:email]] + } + end + end + end +end -- cgit v1.2.1 From 85c448d76f382538898344b07b2c635c7215ca7b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 28 Oct 2016 16:16:37 +0100 Subject: fix specs --- lib/gitlab/cycle_analytics/events_fetcher.rb | 4 +--- lib/gitlab/cycle_analytics/query_config.rb | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 0248ebc7193..db613a2d5c0 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -11,12 +11,10 @@ module Gitlab custom_query = "#{stage}_custom_query".to_sym @query.execute(stage) do |base_query| - send(custom_query, base_query) if self.respond_to?(custom_query) + public_send(custom_query, base_query) if self.respond_to?(custom_query) end end - private - def issue_custom_query(base_query) base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])) end diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index 5689c070eb7..c096e632724 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -12,11 +12,9 @@ module Gitlab end def get - send(@stage).freeze if self.respond_to?(@stage) + public_send(@stage).freeze if self.respond_to?(@stage) end - private - def issue { start_time_attrs: issue_table[:created_at], end_time_attrs: [issue_metrics_table[:first_associated_with_milestone_at], -- cgit v1.2.1 From 9ead268cfaee7cb23fa00c7cf8bcf536cafab91a Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 2 Nov 2016 16:21:49 +0100 Subject: some more integration scenarios testing order. Also, fixed bug in the staging and test stages. --- lib/gitlab/cycle_analytics/events.rb | 2 - lib/gitlab/cycle_analytics/query_config.rb | 4 +- .../projects/cycle_analytics_events_spec.rb | 55 +++++++++++++++++----- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 08070c2bac2..0ec5aeb03c7 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -9,8 +9,6 @@ module Gitlab @fetcher = EventsFetcher.new(project: project, from: from) end - # TODO: backend pagination - specially for commits, etc... - def issue_events @fetcher.fetch(stage: :issue).each { |event| parse_event(event) } end diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index c096e632724..3852ed7a2ab 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -50,7 +50,7 @@ module Gitlab def test { start_time_attrs: mr_metrics_table[:latest_build_started_at], end_time_attrs: mr_metrics_table[:latest_build_finished_at], - projections: mr_metrics_table[:ci_commit_id], + projections: [mr_metrics_table[:ci_commit_id]], order: mr_table[:created_at] } end @@ -68,7 +68,7 @@ module Gitlab def staging { start_time_attrs: mr_metrics_table[:merged_at], end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], - projections: mr_metrics_table[:ci_commit_id] + projections: [mr_metrics_table[:ci_commit_id]] } end diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index 51db9bb07fa..e2733be4c3a 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -3,66 +3,95 @@ require 'spec_helper' describe 'cycle analytics events' do let(:user) { create(:user) } let(:project) { create(:project) } - let!(:issue) { create(:issue, project: project, created_at: 2.days.ago) } describe 'GET /:namespace/:project/cycle_analytics/events/issues' do before do project.team << [user, :developer] - milestone = create(:milestone, project: project) - issue.update(milestone: milestone) - create_merge_request_closing_issue(issue) - - merge_merge_requests_closing_issue(issue) + 3.times { create_cycle } deploy_master login_as(user) end - it 'lists the issue events' do + it 'lists the issue events in the right order' do get namespace_project_cycle_analytics_issue_path(project.namespace, project, format: :json) expect(json_response['events']).not_to be_empty + + first_issue_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s + + expect(json_response['events'].first['iid']).to eq(first_issue_iid) end - it 'lists the plan events' do + it 'lists the plan events in the right order' do get namespace_project_cycle_analytics_plan_path(project.namespace, project, format: :json) expect(json_response['events']).not_to be_empty + + first_date = DateTime.parse(json_response['events'].first['commit']['authored_date']) + last_date = DateTime.parse(json_response['events'].last['commit']['authored_date']) + + expect(first_date).to be > last_date end - it 'lists the code events' do + it 'lists the code events in the right order' do get namespace_project_cycle_analytics_code_path(project.namespace, project, format: :json) expect(json_response['events']).not_to be_empty + + first_mr_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s + + expect(json_response['events'].first['iid']).to eq(first_mr_iid) end - it 'lists the test events' do + it 'lists the test events in the right order' do get namespace_project_cycle_analytics_test_path(project.namespace, project, format: :json) expect(json_response['events']).not_to be_empty + + # TODO create builds end - it 'lists the review events' do + it 'lists the review events in the right order' do get namespace_project_cycle_analytics_review_path(project.namespace, project, format: :json) expect(json_response['events']).not_to be_empty + + first_mr_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s + + expect(json_response['events'].first['iid']).to eq(first_mr_iid) end - it 'lists the staging events' do + it 'lists the staging events in the right order' do get namespace_project_cycle_analytics_staging_path(project.namespace, project, format: :json) expect(json_response['events']).not_to be_empty + + # TODO create builds end - it 'lists the production events' do + it 'lists the production events in the right order' do get namespace_project_cycle_analytics_production_path(project.namespace, project, format: :json) expect(json_response['events']).not_to be_empty + + first_issue_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s + + expect(json_response['events'].first['iid']).to eq(first_issue_iid) end end def json_response JSON.parse(response.body) end + + def create_cycle + issue = create(:issue, project: project, created_at: 2.days.ago) + milestone = create(:milestone, project: project) + issue.update(milestone: milestone) + + create_merge_request_closing_issue(issue) + merge_merge_requests_closing_issue(issue) + end end -- cgit v1.2.1 From 8bb4750eb3987999b0ee617c29224e468e9147b3 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 3 Nov 2016 11:28:14 +0100 Subject: added pipelines to integration spec --- spec/requests/projects/cycle_analytics_events_spec.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index e2733be4c3a..3ee8795ac4c 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -50,7 +50,8 @@ describe 'cycle analytics events' do expect(json_response['events']).not_to be_empty - # TODO create builds + expect(json_response['events'].first['pipeline']).not_to be_empty + end it 'lists the review events in the right order' do @@ -68,7 +69,7 @@ describe 'cycle analytics events' do expect(json_response['events']).not_to be_empty - # TODO create builds + expect(json_response['events'].first['pipeline']).not_to be_empty end it 'lists the production events in the right order' do @@ -90,8 +91,11 @@ describe 'cycle analytics events' do issue = create(:issue, project: project, created_at: 2.days.ago) milestone = create(:milestone, project: project) issue.update(milestone: milestone) + mr = create_merge_request_closing_issue(issue) + + pipeline = create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha) + pipeline.run - create_merge_request_closing_issue(issue) merge_merge_requests_closing_issue(issue) end end -- cgit v1.2.1 From 8f7266cd4b6a3e65224b55c2b91509f5ac88d837 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 3 Nov 2016 13:06:14 +0100 Subject: added missing fields to issue. Also, added a light url builder to add URLs easily from arel. Updated specs. --- lib/gitlab/cycle_analytics/events.rb | 5 +++ lib/gitlab/cycle_analytics/query_config.rb | 7 +++- lib/gitlab/light_url_builder.rb | 45 ++++++++++++++++++++++ spec/lib/gitlab/cycle_analytics/events_spec.rb | 10 +++-- .../projects/cycle_analytics_events_spec.rb | 1 - 5 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 lib/gitlab/light_url_builder.rb diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 0ec5aeb03c7..66b30250c0f 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -50,8 +50,13 @@ module Gitlab private def parse_event(event) + event['url'] = Gitlab::LightUrlBuilder.build(entity: :issue, project: @project, id: event['id']) event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) event['created_at'] = interval_in_words(event['created_at']) + event['author_profile_url'] = Gitlab::LightUrlBuilder.build(entity: :user, id: event['author_username']) + event['author_avatar_url'] = Gitlab::LightUrlBuilder.build(entity: :user_avatar_url, id: event['author_id']) + + event.except('author_id', 'author_username') end def first_time_reference_commit(commits, event) diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index 3852ed7a2ab..ae3dd43a81b 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -21,9 +21,12 @@ module Gitlab issue_metrics_table[:first_added_to_board_at]], projections: [issue_table[:title], issue_table[:iid], + issue_table[:id], issue_table[:created_at], - user_table[:name], - user_table[:email]] + issue_table[:state], + user_table[:name].as('author_name'), + user_table[:username].as('author_username'), + user_table[:id].as('author_id')] } end diff --git a/lib/gitlab/light_url_builder.rb b/lib/gitlab/light_url_builder.rb new file mode 100644 index 00000000000..9b5d28e731f --- /dev/null +++ b/lib/gitlab/light_url_builder.rb @@ -0,0 +1,45 @@ +module Gitlab + class LightUrlBuilder + include Gitlab::Routing.url_helpers + include GitlabRoutingHelper + include ActionView::RecordIdentifier + + def self.build(*args) + new(*args).url + end + + def initialize(entity:, project: nil, id:, opts: {}) + @entity = entity + @project = project + @id = id + @opts = opts + end + + def url + case @entity + when :issue + issue_url + when :user + user_url(@id) + when :user_avatar_url + user_avatar_url + else + raise NotImplementedError.new("No URL builder defined for #{object.class}") + end + end + + private + + def issue_url + namespace_project_issue_url({ + namespace_id: @project.namespace, + project_id: @project, + id: @id + }.merge!(@opts)) + end + + def user_avatar_url + User.find(@id).avatar_url + end + end +end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 7d544dbdb19..e4535f467ec 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -30,12 +30,16 @@ describe Gitlab::CycleAnalytics::Events do expect(subject.issue_events.first['created_at']).to end_with('ago') end - it "has the author's email" do - expect(subject.issue_events.first['email']).to eq(context.author.email) + it "has the author's URL" do + expect(subject.issue_events.first['author_profile_url']).not_to be_nil + end + + it "has the author's avatar URL" do + expect(subject.issue_events.first['author_avatar_url']).not_to be_nil end it "has the author's name" do - expect(subject.issue_events.first['name']).to eq(context.author.name) + expect(subject.issue_events.first['author_name']).to eq(context.author.name) end end diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index 3ee8795ac4c..9884df3f110 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -51,7 +51,6 @@ describe 'cycle analytics events' do expect(json_response['events']).not_to be_empty expect(json_response['events'].first['pipeline']).not_to be_empty - end it 'lists the review events in the right order' do -- cgit v1.2.1 From 1b5b2eac222cc25bfe7301cdefb69a6635ef0682 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 3 Nov 2016 15:41:46 +0100 Subject: added missing fields to plan events and updated spec --- .../projects/cycle_analytics/events_controller.rb | 21 +++++++++------- lib/gitlab/cycle_analytics/events.rb | 11 ++++++--- lib/gitlab/cycle_analytics/query_config.rb | 3 +-- lib/gitlab/light_url_builder.rb | 20 ++++++++++++---- spec/lib/gitlab/cycle_analytics/events_spec.rb | 28 +++++++++++++++------- 5 files changed, 55 insertions(+), 28 deletions(-) diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 49c7303dccc..86ed01b9ea4 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -1,40 +1,43 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationController + # TODO: fix authorization # before_action :authorize_read_cycle_analytics! + # TODO: refactor +event_hash+ + def issue - render_events(events.issue_events) + render_events(issues: events.issue_events) end def plan - render_events(events.plan_events) + render_events(commits: events.plan_events) end def code - render_events(events.code_events) + render_events(merge_requests: events.code_events) end def test - render_events(events.test_events) + render_events(builds: events.test_events) end def review - render_events(events.review_events) + render_events(merge_requests: events.review_events) end def staging - render_events(events.staging_events) + render_events(builds: events.staging_events) end def production - render_events(events.production_events) + render_events(issues: events.production_events) end private - def render_events(event_list) + def render_events(event_hash) respond_to do |format| format.html - format.json { render json: { events: event_list } } + format.json { render json: event_hash } end end diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 66b30250c0f..88aff762b43 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -16,8 +16,13 @@ module Gitlab def plan_events @fetcher.fetch(stage: :plan).each do |event| event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) - commits = event.delete('commits') - event['commit'] = first_time_reference_commit(commits, event) + commit = first_time_reference_commit(event.delete('commits'), event) + event['title'] = commit.title + event['url'] = Gitlab::LightUrlBuilder.build(entity: :commit_url, project: @project, id: commit.id) + event['sha'] = commit.short_id + event['author_name'] = commit.author.name + event['author_profile_url'] = Gitlab::LightUrlBuilder.build(entity: :user, id: commit.author.username) + event['author_avatar_url'] = Gitlab::LightUrlBuilder.build(entity: :user_avatar_url, id: commit.author.id) end end @@ -56,7 +61,7 @@ module Gitlab event['author_profile_url'] = Gitlab::LightUrlBuilder.build(entity: :user, id: event['author_username']) event['author_avatar_url'] = Gitlab::LightUrlBuilder.build(entity: :user_avatar_url, id: event['author_id']) - event.except('author_id', 'author_username') + event.except!('author_id', 'author_username') end def first_time_reference_commit(commits, event) diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index ae3dd43a81b..a544dbbf556 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -34,8 +34,7 @@ module Gitlab { start_time_attrs: issue_metrics_table[:first_associated_with_milestone_at], end_time_attrs: [issue_metrics_table[:first_added_to_board_at], issue_metrics_table[:first_mentioned_in_commit_at]], - projections: [mr_diff_table[:st_commits].as('commits'), - issue_metrics_table[:first_mentioned_in_commit_at]] + projections: [mr_diff_table[:st_commits].as('commits')] } end diff --git a/lib/gitlab/light_url_builder.rb b/lib/gitlab/light_url_builder.rb index 9b5d28e731f..3cbb53241f2 100644 --- a/lib/gitlab/light_url_builder.rb +++ b/lib/gitlab/light_url_builder.rb @@ -23,8 +23,10 @@ module Gitlab user_url(@id) when :user_avatar_url user_avatar_url + when :commit_url + commit_url else - raise NotImplementedError.new("No URL builder defined for #{object.class}") + raise NotImplementedError.new("No URL builder defined for #{object.class}") end end @@ -32,14 +34,22 @@ module Gitlab def issue_url namespace_project_issue_url({ - namespace_id: @project.namespace, - project_id: @project, - id: @id - }.merge!(@opts)) + namespace_id: @project.namespace, + project_id: @project, + id: @id + }.merge!(@opts)) end def user_avatar_url User.find(@id).avatar_url end + + def commit_url + namespace_project_commit_url({ + namespace_id: @project.namespace, + project_id: @project, + id: @id + }.merge!(@opts)) + end end end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index e4535f467ec..80a66693a37 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -4,6 +4,7 @@ describe Gitlab::CycleAnalytics::Events do let(:project) { create(:project) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } + let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } subject { described_class.new(project: project, from: from_date) } @@ -12,8 +13,6 @@ describe Gitlab::CycleAnalytics::Events do end describe '#issue_events' do - let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } - it 'has the total time' do expect(subject.issue_events.first['total_time']).to eq('2 days') end @@ -44,20 +43,32 @@ describe Gitlab::CycleAnalytics::Events do end describe '#plan_events' do - let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } + it 'has a title' do + expect(subject.plan_events.first['title']).not_to be_nil + end - it 'has the first referenced commit' do - expect(subject.plan_events.first['commit'].message).to eq('commit message') + it 'has a sha short ID' do + expect(subject.plan_events.first['sha']).not_to be_nil end it 'has the total time' do expect(subject.plan_events.first['total_time']).to eq('less than a minute') end + + it "has the author's URL" do + expect(subject.plan_events.first['author_profile_url']).not_to be_nil + end + + it "has the author's avatar URL" do + expect(subject.plan_events.first['author_avatar_url']).not_to be_nil + end + + it "has the author's name" do + expect(subject.plan_events.first['author_name']).not_to be_nil + end end describe '#code_events' do - let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } - before do create_commit_referencing_issue(context) end @@ -88,7 +99,6 @@ describe Gitlab::CycleAnalytics::Events do end describe '#test_events' do - let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } let(:merge_request) { MergeRequest.first } let!(:pipeline) do create(:ci_pipeline, @@ -140,7 +150,6 @@ describe Gitlab::CycleAnalytics::Events do end describe '#staging_events' do - let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } let(:merge_request) { MergeRequest.first } let!(:pipeline) do create(:ci_pipeline, @@ -152,6 +161,7 @@ describe Gitlab::CycleAnalytics::Events do before do pipeline.run! pipeline.succeed! + merge_merge_requests_closing_issue(context) deploy_master end -- cgit v1.2.1 From 11bad33a4206e482ffac42d75a121182a52e0cb0 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 3 Nov 2016 16:31:30 +0100 Subject: added missing fields to code events and updated spec --- lib/gitlab/cycle_analytics/events.rb | 6 +++--- lib/gitlab/cycle_analytics/query_config.rb | 11 +++++++---- lib/gitlab/light_url_builder.rb | 10 ++++++++++ spec/lib/gitlab/cycle_analytics/events_spec.rb | 10 +++++++--- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 88aff762b43..0819f0eba4b 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -27,7 +27,7 @@ module Gitlab end def code_events - @fetcher.fetch(stage: :code).each { |event| parse_event(event) } + @fetcher.fetch(stage: :code).each { |event| parse_event(event, entity: :merge_request) } end def test_events @@ -54,8 +54,8 @@ module Gitlab private - def parse_event(event) - event['url'] = Gitlab::LightUrlBuilder.build(entity: :issue, project: @project, id: event['id']) + def parse_event(event, entity: :issue) + event['url'] = Gitlab::LightUrlBuilder.build(entity: entity, project: @project, id: event['id']) event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) event['created_at'] = interval_in_words(event['created_at']) event['author_profile_url'] = Gitlab::LightUrlBuilder.build(entity: :user, id: event['author_username']) diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index a544dbbf556..b1ebb452659 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -23,7 +23,6 @@ module Gitlab issue_table[:iid], issue_table[:id], issue_table[:created_at], - issue_table[:state], user_table[:name].as('author_name'), user_table[:username].as('author_username'), user_table[:id].as('author_id')] @@ -41,10 +40,14 @@ module Gitlab def code { start_time_attrs: issue_metrics_table[:first_mentioned_in_commit_at], end_time_attrs: mr_table[:created_at], - projections: [mr_table[:title], mr_table[:iid], + projections: [mr_table[:title], + mr_table[:iid], + mr_table[:id], mr_table[:created_at], - user_table[:name], - user_table[:email]], + mr_table[:state], + user_table[:name].as('author_name'), + user_table[:username].as('author_username'), + user_table[:id].as('author_id')], order: mr_table[:created_at] } end diff --git a/lib/gitlab/light_url_builder.rb b/lib/gitlab/light_url_builder.rb index 3cbb53241f2..7df14acfe85 100644 --- a/lib/gitlab/light_url_builder.rb +++ b/lib/gitlab/light_url_builder.rb @@ -25,6 +25,8 @@ module Gitlab user_avatar_url when :commit_url commit_url + when :merge_request + mr_url else raise NotImplementedError.new("No URL builder defined for #{object.class}") end @@ -51,5 +53,13 @@ module Gitlab id: @id }.merge!(@opts)) end + + def mr_url + namespace_project_merge_request_url({ + namespace_id: @project.namespace, + project_id: @project, + id: @id + }.merge!(@opts)) + end end end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 80a66693a37..32e7bd7b12c 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -89,12 +89,16 @@ describe Gitlab::CycleAnalytics::Events do expect(subject.code_events.first['created_at']).to end_with('ago') end - it "has the author's email" do - expect(subject.code_events.first['email']).to eq(context.author.email) + it "has the author's URL" do + expect(subject.code_events.first['author_profile_url']).not_to be_nil + end + + it "has the author's avatar URL" do + expect(subject.code_events.first['author_avatar_url']).not_to be_nil end it "has the author's name" do - expect(subject.code_events.first['name']).to eq(context.author.name) + expect(subject.code_events.first['author_name']).to eq(context.author.name) end end -- cgit v1.2.1 From eccb6a5e920920bda11104ca608e652f84a944bf Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Nov 2016 11:56:24 +0100 Subject: Refactored test events Now test events return the builds instead a list of pipelines to avoid calling pipeline.builds per each and get the info. Also, added missing fields/data, URLs, and fixed specs in events spec. --- lib/gitlab/cycle_analytics/events.rb | 11 ++++++-- lib/gitlab/cycle_analytics/events_fetcher.rb | 4 +++ lib/gitlab/cycle_analytics/metrics_fetcher.rb | 4 +++ lib/gitlab/cycle_analytics/query_config.rb | 2 +- lib/gitlab/light_url_builder.rb | 18 +++++++++++++ spec/lib/gitlab/cycle_analytics/events_spec.rb | 37 +++++++++++++++++++++++--- 6 files changed, 70 insertions(+), 6 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 0819f0eba4b..b39282b1e0f 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -32,8 +32,15 @@ module Gitlab def test_events @fetcher.fetch(stage: :test).each do |event| - event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) - event['pipeline'] = ::Ci::Pipeline.find_by_id(event['ci_commit_id']) # we may not have a pipeline + build = ::Ci::Build.find(event['id']) + event['name'] = build.name + event['url'] = Gitlab::LightUrlBuilder.build(entity: :build_url, project: @project, id: build.id) + event['branch'] = build.ref + event['branch_url'] = Gitlab::LightUrlBuilder.build(entity: :branch_url, project: @project, id: build.ref) + event['sha'] = build.short_sha + event['commit_url'] = Gitlab::LightUrlBuilder.build(entity: :commit_url, project: @project, id: build.sha) + event['date'] = build.started_at + event['total_time'] = build.duration end end diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index db613a2d5c0..069b5873736 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -29,6 +29,10 @@ module Gitlab def review_custom_query(base_query) base_query.join(user_table).on(mr_table[:author_id].eq(user_table[:id])) end + + def test_custom_query(base_query) + base_query.join(build_table).on(mr_metrics_table[:ci_commit_id].eq(build_table[:commit_id])) + end end end end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index c1cbedc045b..11f923cc27d 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -79,6 +79,10 @@ module Gitlab def user_table User.arel_table end + + def build_table + ::CommitStatus.arel_table + end end end end diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index b1ebb452659..bd8ddf8a638 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -55,7 +55,7 @@ module Gitlab def test { start_time_attrs: mr_metrics_table[:latest_build_started_at], end_time_attrs: mr_metrics_table[:latest_build_finished_at], - projections: [mr_metrics_table[:ci_commit_id]], + projections: [build_table[:id]], order: mr_table[:created_at] } end diff --git a/lib/gitlab/light_url_builder.rb b/lib/gitlab/light_url_builder.rb index 7df14acfe85..b33fad12eec 100644 --- a/lib/gitlab/light_url_builder.rb +++ b/lib/gitlab/light_url_builder.rb @@ -16,6 +16,7 @@ module Gitlab end def url + #TODO refactor this case @entity when :issue issue_url @@ -27,6 +28,10 @@ module Gitlab commit_url when :merge_request mr_url + when :build_url + namespace_project_build_url(@project.namespace, @project, @id) + when :branch_url + branch_url else raise NotImplementedError.new("No URL builder defined for #{object.class}") end @@ -61,5 +66,18 @@ module Gitlab id: @id }.merge!(@opts)) end + + + def pipeline_url + namespace_project_build_url({ + namespace_id: @project.namespace, + project_id: @project, + id: @id + }.merge!(@opts)) + end + + def branch_url + "#{project_url(@project)}/commits/#{@id}" + end end end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 32e7bd7b12c..c83dafbd256 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -112,16 +112,47 @@ describe Gitlab::CycleAnalytics::Events do end before do + create(:ci_build, pipeline: pipeline, status: :success) + create(:ci_build, pipeline: pipeline, status: :success) + pipeline.run! pipeline.succeed! end - it 'has the build info as a pipeline' do - expect(subject.test_events.first['pipeline']).to eq(pipeline) + it 'has the name' do + expect(subject.test_events.first['name']).not_to be_nil + end + + it 'has the ID' do + expect(subject.test_events.first['id']).not_to be_nil + end + + it 'has the URL' do + expect(subject.test_events.first['url']).not_to be_nil + end + + it 'has the branch name' do + expect(subject.test_events.first['branch']).not_to be_nil + end + + it 'has the branch URL' do + expect(subject.test_events.first['branch_url']).not_to be_nil + end + + it 'has the short SHA' do + expect(subject.test_events.first['sha']).not_to be_nil + end + + it 'has the commit URL' do + expect(subject.test_events.first['commit_url']).not_to be_nil + end + + it 'has the date' do + expect(subject.test_events.first['date']).not_to be_nil end it 'has the total time' do - expect(subject.test_events.first['total_time']).to eq('less than a minute') + expect(subject.test_events.first['total_time']).not_to be_nil end end -- cgit v1.2.1 From bd31f24c548878597322b34965789c88ff8d2dde Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Nov 2016 12:57:23 +0100 Subject: Added branch option to test events Also fixed test events ordering issue --- app/controllers/projects/cycle_analytics/events_controller.rb | 10 ++++++++-- lib/gitlab/cycle_analytics/events.rb | 5 ++--- lib/gitlab/cycle_analytics/events_fetcher.rb | 4 ++-- lib/gitlab/cycle_analytics/events_query.rb | 5 +++-- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 2 ++ lib/gitlab/cycle_analytics/query_config.rb | 2 +- spec/lib/gitlab/cycle_analytics/events_spec.rb | 2 +- 7 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 86ed01b9ea4..0f0fd17c8a7 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -17,6 +17,8 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll end def test + @opts = { from: start_date, branch: events_params[:branch_name] } + render_events(builds: events.test_events) end @@ -54,12 +56,16 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll end def events - @events ||= Gitlab::CycleAnalytics::Events.new(project: project, from: start_date) + @events ||= Gitlab::CycleAnalytics::Events.new(project: project, options: options) + end + + def options + @opts ||= { from: start_date } end def events_params return {} unless params[:events].present? - { start_date: params[:events][:start_date] } + { start_date: params[:events][:start_date], branch_name: params[:events][:branch_name] } end end diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index b39282b1e0f..c86b242857e 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -3,10 +3,9 @@ module Gitlab class Events include ActionView::Helpers::DateHelper - def initialize(project:, from:) + def initialize(project:, options:) @project = project - @from = from - @fetcher = EventsFetcher.new(project: project, from: from) + @fetcher = EventsFetcher.new(project: project, options: options) end def issue_events diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 069b5873736..9b62a596aeb 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -3,8 +3,8 @@ module Gitlab class EventsFetcher include MetricsFetcher - def initialize(project:, from:) - @query = EventsQuery.new(project: project, from: from) + def initialize(project:, options:) + @query = EventsQuery.new(project: project, options: options) end def fetch(stage:) diff --git a/lib/gitlab/cycle_analytics/events_query.rb b/lib/gitlab/cycle_analytics/events_query.rb index f78272a4108..7a27e099be3 100644 --- a/lib/gitlab/cycle_analytics/events_query.rb +++ b/lib/gitlab/cycle_analytics/events_query.rb @@ -3,9 +3,10 @@ module Gitlab class EventsQuery include MetricsFetcher - def initialize(project:, from:) + def initialize(project:, options: {}) @project = project - @from = from + @from = options[:from] + @branch = options[:branch] end def execute(stage, &block) diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index 11f923cc27d..f6522905c10 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -38,6 +38,8 @@ module Gitlab where(issue_table[:deleted_at].eq(nil)). where(issue_table[:created_at].gteq(@from)) + query = query.where(build_table[:ref].eq(@branch)) if name == :test && @branch + # Load merge_requests query = query.join(mr_table, Arel::Nodes::OuterJoin). on(mr_table[:id].eq(mr_closing_issues_table[:merge_request_id])). diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index bd8ddf8a638..55db37ec796 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -56,7 +56,7 @@ module Gitlab { start_time_attrs: mr_metrics_table[:latest_build_started_at], end_time_attrs: mr_metrics_table[:latest_build_finished_at], projections: [build_table[:id]], - order: mr_table[:created_at] + order: build_table[:created_at] } end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index c83dafbd256..a531c4c31b7 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -6,7 +6,7 @@ describe Gitlab::CycleAnalytics::Events do let(:user) { create(:user, :admin) } let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } - subject { described_class.new(project: project, from: from_date) } + subject { described_class.new(project: project, options: { from: from_date }) } before do setup(context) -- cgit v1.2.1 From beeb64610e6a1907d6675b535659a519db129453 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Nov 2016 15:59:27 +0100 Subject: Refactored staging events, added missing fields and fixed specs --- lib/gitlab/cycle_analytics/events.rb | 26 ++++++----- lib/gitlab/cycle_analytics/events_fetcher.rb | 7 +-- lib/gitlab/cycle_analytics/query_config.rb | 15 ++++--- spec/lib/gitlab/cycle_analytics/events_spec.rb | 62 +++++++++++++++++++++++--- 4 files changed, 84 insertions(+), 26 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index c86b242857e..1a78bea3e18 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -31,15 +31,7 @@ module Gitlab def test_events @fetcher.fetch(stage: :test).each do |event| - build = ::Ci::Build.find(event['id']) - event['name'] = build.name - event['url'] = Gitlab::LightUrlBuilder.build(entity: :build_url, project: @project, id: build.id) - event['branch'] = build.ref - event['branch_url'] = Gitlab::LightUrlBuilder.build(entity: :branch_url, project: @project, id: build.ref) - event['sha'] = build.short_sha - event['commit_url'] = Gitlab::LightUrlBuilder.build(entity: :commit_url, project: @project, id: build.sha) - event['date'] = build.started_at - event['total_time'] = build.duration + parse_build_event(event) end end @@ -49,8 +41,7 @@ module Gitlab def staging_events @fetcher.fetch(stage: :staging).each do |event| - event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) - event['pipeline'] = ::Ci::Pipeline.find_by_id(event['ci_commit_id']) # we may not have a pipeline + parse_build_event(event) end end @@ -70,6 +61,19 @@ module Gitlab event.except!('author_id', 'author_username') end + def parse_build_event(event) + build = ::Ci::Build.find(event['id']) + event['name'] = build.name + event['url'] = Gitlab::LightUrlBuilder.build(entity: :build_url, project: @project, id: build.id) + event['branch'] = build.ref + event['branch_url'] = Gitlab::LightUrlBuilder.build(entity: :branch_url, project: @project, id: build.ref) + event['sha'] = build.short_sha + event['commit_url'] = Gitlab::LightUrlBuilder.build(entity: :commit_url, project: @project, id: build.sha) + event['date'] = build.started_at + event['total_time'] = build.duration + event['author_name'] = build.author.try(:name) + end + def first_time_reference_commit(commits, event) st_commit = YAML.load(commits).detect do |commit| commit['created_at'] == event['first_mentioned_in_commit_at'] diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 9b62a596aeb..5631867d451 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -19,9 +19,6 @@ module Gitlab base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])) end - alias_method :code_custom_query, :issue_custom_query - alias_method :production_custom_query, :issue_custom_query - def plan_custom_query(base_query) base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) end @@ -33,6 +30,10 @@ module Gitlab def test_custom_query(base_query) base_query.join(build_table).on(mr_metrics_table[:ci_commit_id].eq(build_table[:commit_id])) end + + alias_method :code_custom_query, :issue_custom_query + alias_method :production_custom_query, :issue_custom_query + alias_method :staging_custom_query, :test_custom_query end end end diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index 55db37ec796..68692f3a4e6 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -63,17 +63,22 @@ module Gitlab def review { start_time_attrs: mr_table[:created_at], end_time_attrs: mr_metrics_table[:merged_at], - projections: [mr_table[:title], mr_table[:iid], - mr_table[:created_at], - user_table[:name], - user_table[:email]] + projections: [mr_table[:title], + mr_table[:iid], + mr_table[:id], + mr_table[:created_at].as('opened_at'), + mr_table[:state], + user_table[:name].as('author_name'), + user_table[:username].as('author_username'), + user_table[:id].as('author_id')] } end def staging { start_time_attrs: mr_metrics_table[:merged_at], end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], - projections: [mr_metrics_table[:ci_commit_id]] + projections: [build_table[:id]], + order: build_table[:created_at] } end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index a531c4c31b7..815e70ab6a8 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -171,16 +171,28 @@ describe Gitlab::CycleAnalytics::Events do expect(subject.review_events.first['iid']).to eq(context.iid.to_s) end + it 'has the URL' do + expect(subject.review_events.first['url']).not_to be_nil + end + + it 'has a state' do + expect(subject.review_events.first['state']).not_to be_nil + end + it 'has a created_at timestamp' do - expect(subject.review_events.first['created_at']).to end_with('ago') + expect(subject.review_events.first['opened_at']).not_to be_nil end - it "has the author's email" do - expect(subject.review_events.first['email']).to eq(MergeRequest.first.author.email) + it "has the author's URL" do + expect(subject.review_events.first['author_profile_url']).not_to be_nil + end + + it "has the author's avatar URL" do + expect(subject.review_events.first['author_avatar_url']).not_to be_nil end it "has the author's name" do - expect(subject.review_events.first['name']).to eq(MergeRequest.first.author.name) + expect(subject.review_events.first['author_name']).to eq(context.author.name) end end @@ -194,6 +206,9 @@ describe Gitlab::CycleAnalytics::Events do end before do + create(:ci_build, pipeline: pipeline, status: :success, author: user) + create(:ci_build, pipeline: pipeline, status: :success, author: user) + pipeline.run! pipeline.succeed! @@ -201,12 +216,45 @@ describe Gitlab::CycleAnalytics::Events do deploy_master end - it 'has the build info as a pipeline' do - expect(subject.staging_events.first['pipeline']).to eq(pipeline) + + it 'has the name' do + expect(subject.staging_events.first['name']).not_to be_nil + end + + it 'has the ID' do + expect(subject.staging_events.first['id']).not_to be_nil + end + + it 'has the URL' do + expect(subject.staging_events.first['url']).not_to be_nil + end + + it 'has the branch name' do + expect(subject.staging_events.first['branch']).not_to be_nil + end + + it 'has the branch URL' do + expect(subject.staging_events.first['branch_url']).not_to be_nil + end + + it 'has the short SHA' do + expect(subject.staging_events.first['sha']).not_to be_nil + end + + it 'has the commit URL' do + expect(subject.staging_events.first['commit_url']).not_to be_nil + end + + it 'has the date' do + expect(subject.staging_events.first['date']).not_to be_nil end it 'has the total time' do - expect(subject.staging_events.first['total_time']).to eq('less than a minute') + expect(subject.staging_events.first['total_time']).not_to be_nil + end + + it 'has the author name' do + expect(subject.staging_events.first['author_name']).not_to be_nil end end -- cgit v1.2.1 From 83130ae877ffa946ef270743905f8fe1ac123305 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Nov 2016 16:16:15 +0100 Subject: Updated production events with new fields --- lib/gitlab/cycle_analytics/events.rb | 2 +- lib/gitlab/cycle_analytics/query_config.rb | 6 ++++-- spec/lib/gitlab/cycle_analytics/events_spec.rb | 19 +++++++++++++++---- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 1a78bea3e18..7d06c5e890f 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -17,7 +17,7 @@ module Gitlab event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) commit = first_time_reference_commit(event.delete('commits'), event) event['title'] = commit.title - event['url'] = Gitlab::LightUrlBuilder.build(entity: :commit_url, project: @project, id: commit.id) + event['url'] = Gitlab::LightUrlBuilder.build(entity: :commit_url, project: @project, id: commit.id) event['sha'] = commit.short_id event['author_name'] = commit.author.name event['author_profile_url'] = Gitlab::LightUrlBuilder.build(entity: :user, id: commit.author.username) diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index 68692f3a4e6..9c8d31c1a97 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -87,9 +87,11 @@ module Gitlab end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], projections: [issue_table[:title], issue_table[:iid], + issue_table[:id], issue_table[:created_at], - user_table[:name], - user_table[:email]] + user_table[:name].as('author_name'), + user_table[:username].as('author_username'), + user_table[:id].as('author_id')] } end end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 815e70ab6a8..29cc3011542 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -21,6 +21,10 @@ describe Gitlab::CycleAnalytics::Events do expect(subject.issue_events.first['title']).to eq(context.title) end + it 'has the URL' do + expect(subject.issue_events.first['url']).not_to be_nil + end + it 'has an iid' do expect(subject.issue_events.first['iid']).to eq(context.iid.to_s) end @@ -216,7 +220,6 @@ describe Gitlab::CycleAnalytics::Events do deploy_master end - it 'has the name' do expect(subject.staging_events.first['name']).not_to be_nil end @@ -274,6 +277,10 @@ describe Gitlab::CycleAnalytics::Events do expect(subject.production_events.first['title']).to eq(context.title) end + it 'has the URL' do + expect(subject.production_events.first['url']).not_to be_nil + end + it 'has an iid' do expect(subject.production_events.first['iid']).to eq(context.iid.to_s) end @@ -282,12 +289,16 @@ describe Gitlab::CycleAnalytics::Events do expect(subject.production_events.first['created_at']).to end_with('ago') end - it "has the author's email" do - expect(subject.production_events.first['email']).to eq(context.author.email) + it "has the author's URL" do + expect(subject.production_events.first['author_profile_url']).not_to be_nil + end + + it "has the author's avatar URL" do + expect(subject.production_events.first['author_avatar_url']).not_to be_nil end it "has the author's name" do - expect(subject.production_events.first['name']).to eq(context.author.name) + expect(subject.production_events.first['author_name']).to eq(context.author.name) end end -- cgit v1.2.1 From f59b8afc8212ba1da34913b15e25769dc4cc37db Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Nov 2016 16:31:05 +0100 Subject: fix module issue in cycle analytics controller --- app/controllers/projects/cycle_analytics_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index 16a7b1fc6e2..04f4cc7000e 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -5,7 +5,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController before_action :authorize_read_cycle_analytics! def show - @cycle_analytics = CycleAnalytics.new(@project, from: parse_start_date) + @cycle_analytics = ::CycleAnalytics.new(@project, from: parse_start_date) respond_to do |format| format.html -- cgit v1.2.1 From 449a9fb1f16ff8b0d4328c4ebd26018dcd6fce36 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Nov 2016 17:14:35 +0100 Subject: fixed integration spec after big refactoring of fields per stage --- .../projects/cycle_analytics_events_spec.rb | 56 +++++++++++++--------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index 9884df3f110..c0b6a97c202 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -14,71 +14,78 @@ describe 'cycle analytics events' do login_as(user) end - it 'lists the issue events in the right order' do + it 'lists the issue events' do get namespace_project_cycle_analytics_issue_path(project.namespace, project, format: :json) - expect(json_response['events']).not_to be_empty + expect(json_response['issues']).not_to be_empty first_issue_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s - expect(json_response['events'].first['iid']).to eq(first_issue_iid) + expect(json_response['issues'].first['iid']).to eq(first_issue_iid) end - it 'lists the plan events in the right order' do + it 'lists the plan events' do get namespace_project_cycle_analytics_plan_path(project.namespace, project, format: :json) - expect(json_response['events']).not_to be_empty + expect(json_response['commits']).not_to be_empty - first_date = DateTime.parse(json_response['events'].first['commit']['authored_date']) - last_date = DateTime.parse(json_response['events'].last['commit']['authored_date']) + commits = [] - expect(first_date).to be > last_date + MergeRequest.all.each do |mr| + mr.merge_request_diff.st_commits.each do |commit| + commits << { date: commit[:authored_date], sha: commit[:id] } + end + end + + newest_sha = commits.sort_by { |k| k['date'] }.first[:sha][0...8] + + expect(json_response['commits'].first['sha']).to eq(newest_sha) end - it 'lists the code events in the right order' do + it 'lists the code events' do get namespace_project_cycle_analytics_code_path(project.namespace, project, format: :json) - expect(json_response['events']).not_to be_empty + expect(json_response['merge_requests']).not_to be_empty first_mr_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s - expect(json_response['events'].first['iid']).to eq(first_mr_iid) + expect(json_response['merge_requests'].first['iid']).to eq(first_mr_iid) end - it 'lists the test events in the right order' do + it 'lists the test events' do get namespace_project_cycle_analytics_test_path(project.namespace, project, format: :json) - expect(json_response['events']).not_to be_empty + expect(json_response['builds']).not_to be_empty - expect(json_response['events'].first['pipeline']).not_to be_empty + expect(json_response['builds'].first['date']).not_to be_empty end - it 'lists the review events in the right order' do + it 'lists the review events' do get namespace_project_cycle_analytics_review_path(project.namespace, project, format: :json) - expect(json_response['events']).not_to be_empty + expect(json_response['merge_requests']).not_to be_empty first_mr_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s - expect(json_response['events'].first['iid']).to eq(first_mr_iid) + expect(json_response['merge_requests'].first['iid']).to eq(first_mr_iid) end - it 'lists the staging events in the right order' do + it 'lists the staging events' do get namespace_project_cycle_analytics_staging_path(project.namespace, project, format: :json) - expect(json_response['events']).not_to be_empty + expect(json_response['builds']).not_to be_empty - expect(json_response['events'].first['pipeline']).not_to be_empty + expect(json_response['builds'].first['date']).not_to be_empty end - it 'lists the production events in the right order' do + it 'lists the production events' do get namespace_project_cycle_analytics_production_path(project.namespace, project, format: :json) - expect(json_response['events']).not_to be_empty + expect(json_response['issues']).not_to be_empty first_issue_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s - expect(json_response['events'].first['iid']).to eq(first_issue_iid) + expect(json_response['issues'].first['iid']).to eq(first_issue_iid) end end @@ -95,6 +102,9 @@ describe 'cycle analytics events' do pipeline = create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha) pipeline.run + create(:ci_build, pipeline: pipeline, status: :success, author: user) + create(:ci_build, pipeline: pipeline, status: :success, author: user) + merge_merge_requests_closing_issue(issue) end end -- cgit v1.2.1 From b4bb33c5b7f30dedbb56f4b9f7dbc9fd58d4bdb9 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 4 Nov 2016 17:24:54 +0100 Subject: fix rubocop warning --- lib/gitlab/light_url_builder.rb | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/gitlab/light_url_builder.rb b/lib/gitlab/light_url_builder.rb index b33fad12eec..0b589217273 100644 --- a/lib/gitlab/light_url_builder.rb +++ b/lib/gitlab/light_url_builder.rb @@ -16,24 +16,24 @@ module Gitlab end def url - #TODO refactor this + # TODO refactor this case @entity - when :issue - issue_url - when :user - user_url(@id) - when :user_avatar_url - user_avatar_url - when :commit_url - commit_url - when :merge_request - mr_url - when :build_url - namespace_project_build_url(@project.namespace, @project, @id) - when :branch_url - branch_url - else - raise NotImplementedError.new("No URL builder defined for #{object.class}") + when :issue + issue_url + when :user + user_url(@id) + when :user_avatar_url + user_avatar_url + when :commit_url + commit_url + when :merge_request + mr_url + when :build_url + namespace_project_build_url(@project.namespace, @project, @id) + when :branch_url + branch_url + else + raise NotImplementedError.new("No URL builder defined for #{object.class}") end end @@ -67,7 +67,6 @@ module Gitlab }.merge!(@opts)) end - def pipeline_url namespace_project_build_url({ namespace_id: @project.namespace, -- cgit v1.2.1 From 192918cde949c8399d7d526a638f6e09f9fb7a5a Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 7 Nov 2016 10:08:18 +0100 Subject: refactored duplicated code in controller, updated JSON array naming and fixed specs --- app/controllers/concerns/cycle_analytics_params.rb | 7 ++++ .../projects/cycle_analytics/events_controller.rb | 39 ++++++++-------------- .../projects/cycle_analytics_controller.rb | 11 ++---- .../projects/cycle_analytics_events_spec.rb | 28 ++++++++-------- 4 files changed, 36 insertions(+), 49 deletions(-) create mode 100644 app/controllers/concerns/cycle_analytics_params.rb diff --git a/app/controllers/concerns/cycle_analytics_params.rb b/app/controllers/concerns/cycle_analytics_params.rb new file mode 100644 index 00000000000..2aaf8f2b451 --- /dev/null +++ b/app/controllers/concerns/cycle_analytics_params.rb @@ -0,0 +1,7 @@ +module CycleAnalyticsParams + extend ActiveSupport::Concern + + def start_date(params) + params[:start_date] == '30' ? 30.days.ago : 90.days.ago + end +end diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 0f0fd17c8a7..5ef1f911fc5 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -1,57 +1,44 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationController - # TODO: fix authorization - # before_action :authorize_read_cycle_analytics! + include CycleAnalyticsParams - # TODO: refactor +event_hash+ + before_action :authorize_read_cycle_analytics! def issue - render_events(issues: events.issue_events) + render_events(events.issue_events) end def plan - render_events(commits: events.plan_events) + render_events(events.plan_events) end def code - render_events(merge_requests: events.code_events) + render_events(events.code_events) end def test - @opts = { from: start_date, branch: events_params[:branch_name] } + @opts = { from: start_date(events_params), branch: events_params[:branch_name] } - render_events(builds: events.test_events) + render_events(events.test_events) end def review - render_events(merge_requests: events.review_events) + render_events(events.review_events) end def staging - render_events(builds: events.staging_events) + render_events(events.staging_events) end def production - render_events(issues: events.production_events) + render_events(events.production_events) end private - def render_events(event_hash) + def render_events(events) respond_to do |format| format.html - format.json { render json: event_hash } - end - end - - # TODO refactor this - def start_date - case events_params[:start_date] - when '30' then - 30.days.ago - when '90' then - 90.days.ago - else - 90.days.ago + format.json { render json: { items: events } } end end @@ -60,7 +47,7 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll end def options - @opts ||= { from: start_date } + @opts ||= { from: start_date(events_params) } end def events_params diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index 04f4cc7000e..96eb75a0547 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -1,11 +1,12 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController include ActionView::Helpers::DateHelper include ActionView::Helpers::TextHelper + include CycleAnalyticsParams before_action :authorize_read_cycle_analytics! def show - @cycle_analytics = ::CycleAnalytics.new(@project, from: parse_start_date) + @cycle_analytics = ::CycleAnalytics.new(@project, from: start_date(cycle_analytics_params)) respond_to do |format| format.html @@ -15,14 +16,6 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController private - def parse_start_date - case cycle_analytics_params[:start_date] - when '30' then 30.days.ago - when '90' then 90.days.ago - else 90.days.ago - end - end - def cycle_analytics_params return {} unless params[:cycle_analytics].present? diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index c0b6a97c202..d57e0342544 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -17,17 +17,17 @@ describe 'cycle analytics events' do it 'lists the issue events' do get namespace_project_cycle_analytics_issue_path(project.namespace, project, format: :json) - expect(json_response['issues']).not_to be_empty + expect(json_response['items']).not_to be_empty first_issue_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s - expect(json_response['issues'].first['iid']).to eq(first_issue_iid) + expect(json_response['items'].first['iid']).to eq(first_issue_iid) end it 'lists the plan events' do get namespace_project_cycle_analytics_plan_path(project.namespace, project, format: :json) - expect(json_response['commits']).not_to be_empty + expect(json_response['items']).not_to be_empty commits = [] @@ -39,53 +39,53 @@ describe 'cycle analytics events' do newest_sha = commits.sort_by { |k| k['date'] }.first[:sha][0...8] - expect(json_response['commits'].first['sha']).to eq(newest_sha) + expect(json_response['items'].first['sha']).to eq(newest_sha) end it 'lists the code events' do get namespace_project_cycle_analytics_code_path(project.namespace, project, format: :json) - expect(json_response['merge_requests']).not_to be_empty + expect(json_response['items']).not_to be_empty first_mr_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s - expect(json_response['merge_requests'].first['iid']).to eq(first_mr_iid) + expect(json_response['items'].first['iid']).to eq(first_mr_iid) end it 'lists the test events' do get namespace_project_cycle_analytics_test_path(project.namespace, project, format: :json) - expect(json_response['builds']).not_to be_empty + expect(json_response['items']).not_to be_empty - expect(json_response['builds'].first['date']).not_to be_empty + expect(json_response['items'].first['date']).not_to be_empty end it 'lists the review events' do get namespace_project_cycle_analytics_review_path(project.namespace, project, format: :json) - expect(json_response['merge_requests']).not_to be_empty + expect(json_response['items']).not_to be_empty first_mr_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s - expect(json_response['merge_requests'].first['iid']).to eq(first_mr_iid) + expect(json_response['items'].first['iid']).to eq(first_mr_iid) end it 'lists the staging events' do get namespace_project_cycle_analytics_staging_path(project.namespace, project, format: :json) - expect(json_response['builds']).not_to be_empty + expect(json_response['items']).not_to be_empty - expect(json_response['builds'].first['date']).not_to be_empty + expect(json_response['items'].first['date']).not_to be_empty end it 'lists the production events' do get namespace_project_cycle_analytics_production_path(project.namespace, project, format: :json) - expect(json_response['issues']).not_to be_empty + expect(json_response['items']).not_to be_empty first_issue_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s - expect(json_response['issues'].first['iid']).to eq(first_issue_iid) + expect(json_response['items'].first['iid']).to eq(first_issue_iid) end end -- cgit v1.2.1 From 7ac7cfeb75242c4e3c5ec967425aaf598f879033 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 7 Nov 2016 15:43:55 +0100 Subject: refactored and added missing spec to light URL builder --- lib/gitlab/cycle_analytics/events.rb | 8 +- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 4 - lib/gitlab/light_url_builder.rb | 16 +-- spec/lib/gitlab/cycle_analytics/events_spec.rb | 2 +- spec/lib/gitlab/url_builder_spec.rb | 137 +++++++++---------------- spec/lib/light_url_builder_spec.rb | 119 +++++++++++++++++++++ 6 files changed, 174 insertions(+), 112 deletions(-) create mode 100644 spec/lib/light_url_builder_spec.rb diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 7d06c5e890f..68a2de79409 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -52,7 +52,7 @@ module Gitlab private def parse_event(event, entity: :issue) - event['url'] = Gitlab::LightUrlBuilder.build(entity: entity, project: @project, id: event['id']) + event['url'] = Gitlab::LightUrlBuilder.build(entity: entity, project: @project, id: event['iid']) event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) event['created_at'] = interval_in_words(event['created_at']) event['author_profile_url'] = Gitlab::LightUrlBuilder.build(entity: :user, id: event['author_username']) @@ -64,11 +64,11 @@ module Gitlab def parse_build_event(event) build = ::Ci::Build.find(event['id']) event['name'] = build.name - event['url'] = Gitlab::LightUrlBuilder.build(entity: :build_url, project: @project, id: build.id) + event['url'] = Gitlab::LightUrlBuilder.build(entity: :build, project: @project, id: build.id) event['branch'] = build.ref - event['branch_url'] = Gitlab::LightUrlBuilder.build(entity: :branch_url, project: @project, id: build.ref) + event['branch'] = Gitlab::LightUrlBuilder.build(entity: :branch, project: @project, id: build.ref) event['sha'] = build.short_sha - event['commit_url'] = Gitlab::LightUrlBuilder.build(entity: :commit_url, project: @project, id: build.sha) + event['commit'] = Gitlab::LightUrlBuilder.build(entity: :commit, project: @project, id: build.sha) event['date'] = build.started_at event['total_time'] = build.duration event['author_name'] = build.author.try(:name) diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index f6522905c10..6743cbcc9e0 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -6,10 +6,6 @@ module Gitlab DEPLOYMENT_METRIC_STAGES = %i[production staging] - def self.included(klass) - klass.extend self - end - private def calculate_metric(name, start_time_attrs, end_time_attrs) diff --git a/lib/gitlab/light_url_builder.rb b/lib/gitlab/light_url_builder.rb index 0b589217273..7875491a1d8 100644 --- a/lib/gitlab/light_url_builder.rb +++ b/lib/gitlab/light_url_builder.rb @@ -22,15 +22,15 @@ module Gitlab issue_url when :user user_url(@id) - when :user_avatar_url + when :user_avatar user_avatar_url - when :commit_url + when :commit commit_url when :merge_request mr_url - when :build_url + when :build namespace_project_build_url(@project.namespace, @project, @id) - when :branch_url + when :branch branch_url else raise NotImplementedError.new("No URL builder defined for #{object.class}") @@ -67,14 +67,6 @@ module Gitlab }.merge!(@opts)) end - def pipeline_url - namespace_project_build_url({ - namespace_id: @project.namespace, - project_id: @project, - id: @id - }.merge!(@opts)) - end - def branch_url "#{project_url(@project)}/commits/#{@id}" end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 29cc3011542..0fb6a62f10d 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -196,7 +196,7 @@ describe Gitlab::CycleAnalytics::Events do end it "has the author's name" do - expect(subject.review_events.first['author_name']).to eq(context.author.name) + expect(subject.review_events.first['author_name']).to eq(MergeRequest.first.author.name) end end diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb index a826b24419a..5d876fdb603 100644 --- a/spec/lib/gitlab/url_builder_spec.rb +++ b/spec/lib/gitlab/url_builder_spec.rb @@ -1,119 +1,74 @@ require 'spec_helper' -describe Gitlab::UrlBuilder, lib: true do - describe '.build' do - context 'when passing a Commit' do - it 'returns a proper URL' do - commit = build_stubbed(:commit) +describe Gitlab::LightUrlBuilder, lib: true do + context 'when passing a Commit' do + it 'returns a proper URL' do + commit = build_stubbed(:commit) - url = described_class.build(commit) + url = described_class.build(entity: :commit, project: commit.project, id: commit.id) - expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}" - end + expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}" end + end - context 'when passing an Issue' do - it 'returns a proper URL' do - issue = build_stubbed(:issue, iid: 42) + context 'when passing an Issue' do + it 'returns a proper URL' do + issue = build_stubbed(:issue, iid: 42) - url = described_class.build(issue) + url = described_class.build(entity: :issue, project: issue.project, id: issue.iid) - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" - end + expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" end + end - context 'when passing a MergeRequest' do - it 'returns a proper URL' do - merge_request = build_stubbed(:merge_request, iid: 42) + context 'when passing a MergeRequest' do + it 'returns a proper URL' do + merge_request = build_stubbed(:merge_request, iid: 42) - url = described_class.build(merge_request) + url = described_class.build(entity: :merge_request, project: merge_request.project, id: merge_request.iid) - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}" - end + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}" end + end - context 'when passing a Note' do - context 'on a Commit' do - it 'returns a proper URL' do - note = build_stubbed(:note_on_commit) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" - end - end - - context 'on a Commit Diff' do - it 'returns a proper URL' do - note = build_stubbed(:diff_note_on_commit) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" - end - end - - context 'on an Issue' do - it 'returns a proper URL' do - issue = create(:issue, iid: 42) - note = build_stubbed(:note_on_issue, noteable: issue) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}" - end - end - - context 'on a MergeRequest' do - it 'returns a proper URL' do - merge_request = create(:merge_request, iid: 42) - note = build_stubbed(:note_on_merge_request, noteable: merge_request) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" - end - end + context 'when passing a build' do + it 'returns a proper URL' do + build = build_stubbed(:ci_build, project: build_stubbed(:empty_project)) - context 'on a MergeRequest Diff' do - it 'returns a proper URL' do - merge_request = create(:merge_request, iid: 42) - note = build_stubbed(:diff_note_on_merge_request, noteable: merge_request) + url = described_class.build(entity: :build, project: build.project, id: build.id) - url = described_class.build(note) + expect(url).to eq "#{Settings.gitlab['url']}/#{build.project.path_with_namespace}/builds/#{build.id}" + end + end - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" - end - end + context 'when passing a branch' do + it 'returns a proper URL' do + branch = 'branch_name' + project = build_stubbed(:empty_project) - context 'on a ProjectSnippet' do - it 'returns a proper URL' do - project_snippet = create(:project_snippet) - note = build_stubbed(:note_on_project_snippet, noteable: project_snippet) + url = described_class.build(entity: :branch, project: project, id: branch) - url = described_class.build(note) + expect(url).to eq "#{Settings.gitlab['url']}/#{project.path_with_namespace}/commits/#{branch}" + end + end - expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}" - end - end + context 'on a User' do + it 'returns a proper URL' do + user = build_stubbed(:user) - context 'on another object' do - it 'returns a proper URL' do - project = build_stubbed(:project) + url = described_class.build(entity: :user, id: user.username) - expect { described_class.build(project) }. - to raise_error(NotImplementedError, 'No URL builder defined for Project') - end - end + expect(url).to eq "#{Settings.gitlab['url']}/#{user.username}" end + end + + context 'on a user avatar' do + it 'returns a proper URL' do + user = create(:user) - context 'when passing a WikiPage' do - it 'returns a proper URL' do - wiki_page = build(:wiki_page) - url = described_class.build(wiki_page) + url = described_class.build(entity: :user_avatar, id: user.id) - expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}" - end + expect(url).to eq user.avatar_url end end end diff --git a/spec/lib/light_url_builder_spec.rb b/spec/lib/light_url_builder_spec.rb new file mode 100644 index 00000000000..a826b24419a --- /dev/null +++ b/spec/lib/light_url_builder_spec.rb @@ -0,0 +1,119 @@ +require 'spec_helper' + +describe Gitlab::UrlBuilder, lib: true do + describe '.build' do + context 'when passing a Commit' do + it 'returns a proper URL' do + commit = build_stubbed(:commit) + + url = described_class.build(commit) + + expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}" + end + end + + context 'when passing an Issue' do + it 'returns a proper URL' do + issue = build_stubbed(:issue, iid: 42) + + url = described_class.build(issue) + + expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" + end + end + + context 'when passing a MergeRequest' do + it 'returns a proper URL' do + merge_request = build_stubbed(:merge_request, iid: 42) + + url = described_class.build(merge_request) + + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}" + end + end + + context 'when passing a Note' do + context 'on a Commit' do + it 'returns a proper URL' do + note = build_stubbed(:note_on_commit) + + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" + end + end + + context 'on a Commit Diff' do + it 'returns a proper URL' do + note = build_stubbed(:diff_note_on_commit) + + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" + end + end + + context 'on an Issue' do + it 'returns a proper URL' do + issue = create(:issue, iid: 42) + note = build_stubbed(:note_on_issue, noteable: issue) + + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}" + end + end + + context 'on a MergeRequest' do + it 'returns a proper URL' do + merge_request = create(:merge_request, iid: 42) + note = build_stubbed(:note_on_merge_request, noteable: merge_request) + + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" + end + end + + context 'on a MergeRequest Diff' do + it 'returns a proper URL' do + merge_request = create(:merge_request, iid: 42) + note = build_stubbed(:diff_note_on_merge_request, noteable: merge_request) + + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" + end + end + + context 'on a ProjectSnippet' do + it 'returns a proper URL' do + project_snippet = create(:project_snippet) + note = build_stubbed(:note_on_project_snippet, noteable: project_snippet) + + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}" + end + end + + context 'on another object' do + it 'returns a proper URL' do + project = build_stubbed(:project) + + expect { described_class.build(project) }. + to raise_error(NotImplementedError, 'No URL builder defined for Project') + end + end + end + + context 'when passing a WikiPage' do + it 'returns a proper URL' do + wiki_page = build(:wiki_page) + url = described_class.build(wiki_page) + + expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}" + end + end + end +end -- cgit v1.2.1 From fb424b3d514ac096a78b2c5fabc2ddcb10117c73 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 7 Nov 2016 15:50:41 +0100 Subject: rename events hash --- app/controllers/projects/cycle_analytics/events_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 5ef1f911fc5..bb9f93908ad 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -38,7 +38,7 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll def render_events(events) respond_to do |format| format.html - format.json { render json: { items: events } } + format.json { render json: { events: events } } end end -- cgit v1.2.1 From c6532ee3706340bcbe1d8ee25a593b22f9aee811 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 7 Nov 2016 16:54:40 +0100 Subject: refactored light url builder --- lib/gitlab/cycle_analytics/query_config.rb | 21 +++++++++++------ lib/gitlab/light_url_builder.rb | 37 ++++++++++++------------------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index 9c8d31c1a97..0393567378f 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -16,7 +16,8 @@ module Gitlab end def issue - { start_time_attrs: issue_table[:created_at], + { + start_time_attrs: issue_table[:created_at], end_time_attrs: [issue_metrics_table[:first_associated_with_milestone_at], issue_metrics_table[:first_added_to_board_at]], projections: [issue_table[:title], @@ -30,7 +31,8 @@ module Gitlab end def plan - { start_time_attrs: issue_metrics_table[:first_associated_with_milestone_at], + { + start_time_attrs: issue_metrics_table[:first_associated_with_milestone_at], end_time_attrs: [issue_metrics_table[:first_added_to_board_at], issue_metrics_table[:first_mentioned_in_commit_at]], projections: [mr_diff_table[:st_commits].as('commits')] @@ -38,7 +40,8 @@ module Gitlab end def code - { start_time_attrs: issue_metrics_table[:first_mentioned_in_commit_at], + { + start_time_attrs: issue_metrics_table[:first_mentioned_in_commit_at], end_time_attrs: mr_table[:created_at], projections: [mr_table[:title], mr_table[:iid], @@ -53,7 +56,8 @@ module Gitlab end def test - { start_time_attrs: mr_metrics_table[:latest_build_started_at], + { + start_time_attrs: mr_metrics_table[:latest_build_started_at], end_time_attrs: mr_metrics_table[:latest_build_finished_at], projections: [build_table[:id]], order: build_table[:created_at] @@ -61,7 +65,8 @@ module Gitlab end def review - { start_time_attrs: mr_table[:created_at], + { + start_time_attrs: mr_table[:created_at], end_time_attrs: mr_metrics_table[:merged_at], projections: [mr_table[:title], mr_table[:iid], @@ -75,7 +80,8 @@ module Gitlab end def staging - { start_time_attrs: mr_metrics_table[:merged_at], + { + start_time_attrs: mr_metrics_table[:merged_at], end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], projections: [build_table[:id]], order: build_table[:created_at] @@ -83,7 +89,8 @@ module Gitlab end def production - { start_time_attrs: issue_table[:created_at], + { + start_time_attrs: issue_table[:created_at], end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], projections: [issue_table[:title], issue_table[:iid], diff --git a/lib/gitlab/light_url_builder.rb b/lib/gitlab/light_url_builder.rb index 7875491a1d8..ceb8715ad2b 100644 --- a/lib/gitlab/light_url_builder.rb +++ b/lib/gitlab/light_url_builder.rb @@ -1,4 +1,6 @@ module Gitlab + # Similar to UrlBuilder, but using IDs to avoid querying the DB for objects + # Useful for using in conjunction with Arel queries. class LightUrlBuilder include Gitlab::Routing.url_helpers include GitlabRoutingHelper @@ -16,28 +18,11 @@ module Gitlab end def url - # TODO refactor this - case @entity - when :issue - issue_url - when :user - user_url(@id) - when :user_avatar - user_avatar_url - when :commit - commit_url - when :merge_request - mr_url - when :build - namespace_project_build_url(@project.namespace, @project, @id) - when :branch - branch_url - else - raise NotImplementedError.new("No URL builder defined for #{object.class}") - end - end + url_method = "#{@entity}_url" + raise NotImplementedError.new("No Light URL builder defined for #{@entity.to_s}") unless respond_to?(url_method) - private + public_send(url_method) + end def issue_url namespace_project_issue_url({ @@ -59,7 +44,7 @@ module Gitlab }.merge!(@opts)) end - def mr_url + def merge_request_url namespace_project_merge_request_url({ namespace_id: @project.namespace, project_id: @project, @@ -70,5 +55,13 @@ module Gitlab def branch_url "#{project_url(@project)}/commits/#{@id}" end + + def user_url + Gitlab::Routing.url_helpers.user_url(@id) + end + + def build_url + namespace_project_build_url(@project.namespace, @project, @id) + end end end -- cgit v1.2.1 From 2e5c1c27ebdf0ed57f3848f312e147dca6fc38c8 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 9 Nov 2016 10:52:57 +0100 Subject: fix events presenter after refactoring --- lib/gitlab/cycle_analytics/events.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 68a2de79409..47b42621f2d 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -17,11 +17,11 @@ module Gitlab event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) commit = first_time_reference_commit(event.delete('commits'), event) event['title'] = commit.title - event['url'] = Gitlab::LightUrlBuilder.build(entity: :commit_url, project: @project, id: commit.id) + event['url'] = Gitlab::LightUrlBuilder.build(entity: :commit, project: @project, id: commit.id) event['sha'] = commit.short_id event['author_name'] = commit.author.name event['author_profile_url'] = Gitlab::LightUrlBuilder.build(entity: :user, id: commit.author.username) - event['author_avatar_url'] = Gitlab::LightUrlBuilder.build(entity: :user_avatar_url, id: commit.author.id) + event['author_avatar_url'] = Gitlab::LightUrlBuilder.build(entity: :user_avatar, id: commit.author.id) end end @@ -56,7 +56,7 @@ module Gitlab event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) event['created_at'] = interval_in_words(event['created_at']) event['author_profile_url'] = Gitlab::LightUrlBuilder.build(entity: :user, id: event['author_username']) - event['author_avatar_url'] = Gitlab::LightUrlBuilder.build(entity: :user_avatar_url, id: event['author_id']) + event['author_avatar_url'] = Gitlab::LightUrlBuilder.build(entity: :user_avatar, id: event['author_id']) event.except!('author_id', 'author_username') end @@ -66,9 +66,9 @@ module Gitlab event['name'] = build.name event['url'] = Gitlab::LightUrlBuilder.build(entity: :build, project: @project, id: build.id) event['branch'] = build.ref - event['branch'] = Gitlab::LightUrlBuilder.build(entity: :branch, project: @project, id: build.ref) + event['branch_url'] = Gitlab::LightUrlBuilder.build(entity: :branch, project: @project, id: build.ref) event['sha'] = build.short_sha - event['commit'] = Gitlab::LightUrlBuilder.build(entity: :commit, project: @project, id: build.sha) + event['commit_url'] = Gitlab::LightUrlBuilder.build(entity: :commit, project: @project, id: build.sha) event['date'] = build.started_at event['total_time'] = build.duration event['author_name'] = build.author.try(:name) -- cgit v1.2.1 From 3d00503368c355a4a9a45cda0e6673580074d2da Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 9 Nov 2016 11:44:29 +0100 Subject: fixed and added missing scenario to events integration spec --- .../projects/cycle_analytics_events_spec.rb | 40 ++++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index d57e0342544..d4da8707ea5 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -17,17 +17,17 @@ describe 'cycle analytics events' do it 'lists the issue events' do get namespace_project_cycle_analytics_issue_path(project.namespace, project, format: :json) - expect(json_response['items']).not_to be_empty + expect(json_response['events']).not_to be_empty first_issue_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s - expect(json_response['items'].first['iid']).to eq(first_issue_iid) + expect(json_response['events'].first['iid']).to eq(first_issue_iid) end it 'lists the plan events' do get namespace_project_cycle_analytics_plan_path(project.namespace, project, format: :json) - expect(json_response['items']).not_to be_empty + expect(json_response['events']).not_to be_empty commits = [] @@ -39,53 +39,65 @@ describe 'cycle analytics events' do newest_sha = commits.sort_by { |k| k['date'] }.first[:sha][0...8] - expect(json_response['items'].first['sha']).to eq(newest_sha) + expect(json_response['events'].first['sha']).to eq(newest_sha) end it 'lists the code events' do get namespace_project_cycle_analytics_code_path(project.namespace, project, format: :json) - expect(json_response['items']).not_to be_empty + expect(json_response['events']).not_to be_empty first_mr_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s - expect(json_response['items'].first['iid']).to eq(first_mr_iid) + expect(json_response['events'].first['iid']).to eq(first_mr_iid) end it 'lists the test events' do get namespace_project_cycle_analytics_test_path(project.namespace, project, format: :json) - expect(json_response['items']).not_to be_empty + expect(json_response['events']).not_to be_empty - expect(json_response['items'].first['date']).not_to be_empty + expect(json_response['events'].first['date']).not_to be_empty end it 'lists the review events' do get namespace_project_cycle_analytics_review_path(project.namespace, project, format: :json) - expect(json_response['items']).not_to be_empty + expect(json_response['events']).not_to be_empty first_mr_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s - expect(json_response['items'].first['iid']).to eq(first_mr_iid) + expect(json_response['events'].first['iid']).to eq(first_mr_iid) end it 'lists the staging events' do get namespace_project_cycle_analytics_staging_path(project.namespace, project, format: :json) - expect(json_response['items']).not_to be_empty + expect(json_response['events']).not_to be_empty - expect(json_response['items'].first['date']).not_to be_empty + expect(json_response['events'].first['date']).not_to be_empty end it 'lists the production events' do get namespace_project_cycle_analytics_production_path(project.namespace, project, format: :json) - expect(json_response['items']).not_to be_empty + expect(json_response['events']).not_to be_empty first_issue_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s - expect(json_response['items'].first['iid']).to eq(first_issue_iid) + expect(json_response['events'].first['iid']).to eq(first_issue_iid) + end + + context 'specific branch' do + it 'lists the test events' do + branch = MergeRequest.first.source_branch + + get namespace_project_cycle_analytics_test_path(project.namespace, project, format: :json, branch: branch) + + expect(json_response['events']).not_to be_empty + + expect(json_response['events'].first['date']).not_to be_empty + end end end -- cgit v1.2.1 From 848ea241db74ffc160e61c23775e336c207e65af Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 9 Nov 2016 16:03:47 +0100 Subject: fix MySQL problem with query --- lib/gitlab/cycle_analytics/events_query.rb | 2 +- lib/gitlab/light_url_builder.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/cycle_analytics/events_query.rb b/lib/gitlab/cycle_analytics/events_query.rb index 7a27e099be3..9d69e267d8d 100644 --- a/lib/gitlab/cycle_analytics/events_query.rb +++ b/lib/gitlab/cycle_analytics/events_query.rb @@ -14,7 +14,7 @@ module Gitlab @config = QueryConfig.get(stage) query = build_query(&block) - ActiveRecord::Base.connection.execute(query.to_sql).to_a + ActiveRecord::Base.connection.exec_query(query.to_sql) end private diff --git a/lib/gitlab/light_url_builder.rb b/lib/gitlab/light_url_builder.rb index ceb8715ad2b..e4517b5610b 100644 --- a/lib/gitlab/light_url_builder.rb +++ b/lib/gitlab/light_url_builder.rb @@ -19,7 +19,7 @@ module Gitlab def url url_method = "#{@entity}_url" - raise NotImplementedError.new("No Light URL builder defined for #{@entity.to_s}") unless respond_to?(url_method) + raise NotImplementedError.new("No Light URL builder defined for #{@entity}") unless respond_to?(url_method) public_send(url_method) end -- cgit v1.2.1 From 4dfcab6ec3d4b3dae1e68374c1252d139c161534 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 9 Nov 2016 17:19:29 +0100 Subject: fix iid issue making spec to fail - MySQL only --- lib/gitlab/cycle_analytics/events.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 47b42621f2d..e4c9752f3f9 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -52,7 +52,7 @@ module Gitlab private def parse_event(event, entity: :issue) - event['url'] = Gitlab::LightUrlBuilder.build(entity: entity, project: @project, id: event['iid']) + event['url'] = Gitlab::LightUrlBuilder.build(entity: entity, project: @project, id: event['iid'].to_s) event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) event['created_at'] = interval_in_words(event['created_at']) event['author_profile_url'] = Gitlab::LightUrlBuilder.build(entity: :user, id: event['author_username']) -- cgit v1.2.1 From 1744d633eded81103ee4691e6980efb6930dc975 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 9 Nov 2016 17:42:23 +0100 Subject: add changelog entry --- changelogs/unreleased/feature-cycle-analytics-events.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/feature-cycle-analytics-events.yml diff --git a/changelogs/unreleased/feature-cycle-analytics-events.yml b/changelogs/unreleased/feature-cycle-analytics-events.yml new file mode 100644 index 00000000000..e1211a3c774 --- /dev/null +++ b/changelogs/unreleased/feature-cycle-analytics-events.yml @@ -0,0 +1,4 @@ +--- +title: Add events per stage to cycle analytics +merge_request: +author: -- cgit v1.2.1 From 81d0146c4cf0d34b1b81da770483ed864482149c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 14 Nov 2016 11:52:29 +0100 Subject: WIP - refactoring URL builder and events presenter into serializers --- .../projects/cycle_analytics/events_controller.rb | 4 ++-- app/serializers/analytics_build_entity.rb | 27 ++++++++++++++++++++++ app/serializers/analytics_build_serializer.rb | 3 +++ lib/gitlab/cycle_analytics/events.rb | 11 ++------- lib/gitlab/light_url_builder.rb | 2 +- spec/serializers/analytics_build_entity_spec.rb | 22 ++++++++++++++++++ .../serializers/analytics_build_serializer_spec.rb | 24 +++++++++++++++++++ 7 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 app/serializers/analytics_build_entity.rb create mode 100644 app/serializers/analytics_build_serializer.rb create mode 100644 spec/serializers/analytics_build_entity_spec.rb create mode 100644 spec/serializers/analytics_build_serializer_spec.rb diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index bb9f93908ad..77b49d6c4b9 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -35,10 +35,10 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll private - def render_events(events) + def render_events(events_list) respond_to do |format| format.html - format.json { render json: { events: events } } + format.json { render json: { events: events_list } } end end diff --git a/app/serializers/analytics_build_entity.rb b/app/serializers/analytics_build_entity.rb new file mode 100644 index 00000000000..7ed89f1f347 --- /dev/null +++ b/app/serializers/analytics_build_entity.rb @@ -0,0 +1,27 @@ +class AnalyticsBuildEntity < Grape::Entity + include RequestAwareEntity + + expose :name + expose :ref, as: :branch + expose :short_sha + expose :started_at, as: :date + expose :duration, as: :total_time + + expose :url do |build| + url_to(:namespace_project_build, build) + end + + expose :branch_url do |build| + url_to(:namespace_project_tree, build, build.ref) + end + + expose :commit_url do |build| + url_to(:namespace_project_commit, build, build.sha) + end + + private + + def url_to(route, build, id = nil) + public_send("#{route}_url", build.project.namespace, build.project, id || build) + end +end diff --git a/app/serializers/analytics_build_serializer.rb b/app/serializers/analytics_build_serializer.rb new file mode 100644 index 00000000000..f172d67d356 --- /dev/null +++ b/app/serializers/analytics_build_serializer.rb @@ -0,0 +1,3 @@ +class AnalyticsBuildSerializer < BaseSerializer + entity AnalyticsBuildEntity +end diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index e4c9752f3f9..21e295ceeb6 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -63,15 +63,8 @@ module Gitlab def parse_build_event(event) build = ::Ci::Build.find(event['id']) - event['name'] = build.name - event['url'] = Gitlab::LightUrlBuilder.build(entity: :build, project: @project, id: build.id) - event['branch'] = build.ref - event['branch_url'] = Gitlab::LightUrlBuilder.build(entity: :branch, project: @project, id: build.ref) - event['sha'] = build.short_sha - event['commit_url'] = Gitlab::LightUrlBuilder.build(entity: :commit, project: @project, id: build.sha) - event['date'] = build.started_at - event['total_time'] = build.duration - event['author_name'] = build.author.try(:name) + + #event['author_name'] = build.author.try(:name) end def first_time_reference_commit(commits, event) diff --git a/lib/gitlab/light_url_builder.rb b/lib/gitlab/light_url_builder.rb index e4517b5610b..0c86b122b88 100644 --- a/lib/gitlab/light_url_builder.rb +++ b/lib/gitlab/light_url_builder.rb @@ -53,7 +53,7 @@ module Gitlab end def branch_url - "#{project_url(@project)}/commits/#{@id}" + namespace_project_commit_url(@project.namespace, @project, @id) end def user_url diff --git a/spec/serializers/analytics_build_entity_spec.rb b/spec/serializers/analytics_build_entity_spec.rb new file mode 100644 index 00000000000..33653d5b1e0 --- /dev/null +++ b/spec/serializers/analytics_build_entity_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe AnalyticsBuildEntity do + let(:entity) do + described_class.new(build, request: double) + end + + context 'when build is a regular job' do + let(:build) { create(:ci_build) } + + subject { entity.as_json } + + it 'contains url to build page and retry action' do + expect(subject).to include(:url, :branch_url, :commit_url) + end + + it 'does not contain sensitive information' do + expect(subject).not_to include(/token/) + expect(subject).not_to include(/variables/) + end + end +end diff --git a/spec/serializers/analytics_build_serializer_spec.rb b/spec/serializers/analytics_build_serializer_spec.rb new file mode 100644 index 00000000000..24fd94810d4 --- /dev/null +++ b/spec/serializers/analytics_build_serializer_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe AnalyticsBuildSerializer do + let(:serializer) do + described_class + .new(project: project) + .represent(resource) + end + + let(:json) { serializer.as_json } + let(:project) { create(:project) } + let(:resource) { create(:ci_build) } + + context 'when there is a single object provided' do + it 'it generates payload for single object' do + expect(json).to be_an_instance_of Hash + end + + it 'contains important elements of analyticsBuild' do + expect(json) + .to include(:name, :branch, :short_sha, :date, :total_time, :url, :branch_url, :commit_url, :author) + end + end +end -- cgit v1.2.1 From 3b179bc37b940521522af6d8bf6762c2a9a0e251 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 14 Nov 2016 14:53:06 +0100 Subject: WIP - refactored events to use build serializer, related spec passing --- app/serializers/analytics_build_entity.rb | 14 ++- lib/gitlab/cycle_analytics/events.rb | 6 +- spec/lib/gitlab/cycle_analytics/events_spec.rb | 118 ++++++++++++------------ spec/serializers/analytics_build_entity_spec.rb | 9 +- 4 files changed, 79 insertions(+), 68 deletions(-) diff --git a/app/serializers/analytics_build_entity.rb b/app/serializers/analytics_build_entity.rb index 7ed89f1f347..a07f0beccd4 100644 --- a/app/serializers/analytics_build_entity.rb +++ b/app/serializers/analytics_build_entity.rb @@ -2,17 +2,23 @@ class AnalyticsBuildEntity < Grape::Entity include RequestAwareEntity expose :name + expose :id expose :ref, as: :branch expose :short_sha expose :started_at, as: :date expose :duration, as: :total_time + expose :author, using: UserEntity - expose :url do |build| - url_to(:namespace_project_build, build) + expose :branch do + expose :ref, as: :name + + expose :url do |build| + url_to(:namespace_project_tree, build, build.ref) + end end - expose :branch_url do |build| - url_to(:namespace_project_tree, build, build.ref) + expose :url do |build| + url_to(:namespace_project_build, build) end expose :commit_url do |build| diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 21e295ceeb6..f2fa54f208f 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -30,7 +30,7 @@ module Gitlab end def test_events - @fetcher.fetch(stage: :test).each do |event| + @fetcher.fetch(stage: :test).map do |event| parse_build_event(event) end end @@ -40,7 +40,7 @@ module Gitlab end def staging_events - @fetcher.fetch(stage: :staging).each do |event| + @fetcher.fetch(stage: :staging).map do |event| parse_build_event(event) end end @@ -64,7 +64,7 @@ module Gitlab def parse_build_event(event) build = ::Ci::Build.find(event['id']) - #event['author_name'] = build.author.try(:name) + AnalyticsBuildSerializer.new(project: @project).represent(build).as_json end def first_time_reference_commit(commits, event) diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 0fb6a62f10d..ebeea2be39e 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -14,61 +14,61 @@ describe Gitlab::CycleAnalytics::Events do describe '#issue_events' do it 'has the total time' do - expect(subject.issue_events.first['total_time']).to eq('2 days') + expect(subject.issue_events.first[:total_time]).to eq('2 days') end it 'has a title' do - expect(subject.issue_events.first['title']).to eq(context.title) + expect(subject.issue_events.first[:title]).to eq(context.title) end it 'has the URL' do - expect(subject.issue_events.first['url']).not_to be_nil + expect(subject.issue_events.first[:url]).not_to be_nil end it 'has an iid' do - expect(subject.issue_events.first['iid']).to eq(context.iid.to_s) + expect(subject.issue_events.first[:iid]).to eq(context.iid.to_s) end it 'has a created_at timestamp' do - expect(subject.issue_events.first['created_at']).to end_with('ago') + expect(subject.issue_events.first[:created_at]).to end_with('ago') end it "has the author's URL" do - expect(subject.issue_events.first['author_profile_url']).not_to be_nil + expect(subject.issue_events.first[:author_profile_url]).not_to be_nil end it "has the author's avatar URL" do - expect(subject.issue_events.first['author_avatar_url']).not_to be_nil + expect(subject.issue_events.first[:author_avatar_url]).not_to be_nil end it "has the author's name" do - expect(subject.issue_events.first['author_name']).to eq(context.author.name) + expect(subject.issue_events.first[:author_name]).to eq(context.author.name) end end describe '#plan_events' do it 'has a title' do - expect(subject.plan_events.first['title']).not_to be_nil + expect(subject.plan_events.first[:title]).not_to be_nil end it 'has a sha short ID' do - expect(subject.plan_events.first['sha']).not_to be_nil + expect(subject.plan_events.first[:sha]).not_to be_nil end it 'has the total time' do - expect(subject.plan_events.first['total_time']).to eq('less than a minute') + expect(subject.plan_events.first[:total_time]).to eq('less than a minute') end it "has the author's URL" do - expect(subject.plan_events.first['author_profile_url']).not_to be_nil + expect(subject.plan_events.first[:author_profile_url]).not_to be_nil end it "has the author's avatar URL" do - expect(subject.plan_events.first['author_avatar_url']).not_to be_nil + expect(subject.plan_events.first[:author_avatar_url]).not_to be_nil end it "has the author's name" do - expect(subject.plan_events.first['author_name']).not_to be_nil + expect(subject.plan_events.first[:author_name]).not_to be_nil end end @@ -78,31 +78,31 @@ describe Gitlab::CycleAnalytics::Events do end it 'has the total time' do - expect(subject.code_events.first['total_time']).to eq('less than a minute') + expect(subject.code_events.first[:total_time]).to eq('less than a minute') end it 'has a title' do - expect(subject.code_events.first['title']).to eq('Awesome merge_request') + expect(subject.code_events.first[:title]).to eq('Awesome merge_request') end it 'has an iid' do - expect(subject.code_events.first['iid']).to eq(context.iid.to_s) + expect(subject.code_events.first[:iid]).to eq(context.iid.to_s) end it 'has a created_at timestamp' do - expect(subject.code_events.first['created_at']).to end_with('ago') + expect(subject.code_events.first[:created_at]).to end_with('ago') end it "has the author's URL" do - expect(subject.code_events.first['author_profile_url']).not_to be_nil + expect(subject.code_events.first[:author_profile_url]).not_to be_nil end it "has the author's avatar URL" do - expect(subject.code_events.first['author_avatar_url']).not_to be_nil + expect(subject.code_events.first[:author_avatar_url]).not_to be_nil end it "has the author's name" do - expect(subject.code_events.first['author_name']).to eq(context.author.name) + expect(subject.code_events.first[:author_name]).to eq(context.author.name) end end @@ -116,47 +116,47 @@ describe Gitlab::CycleAnalytics::Events do end before do - create(:ci_build, pipeline: pipeline, status: :success) - create(:ci_build, pipeline: pipeline, status: :success) + create(:ci_build, pipeline: pipeline, status: :success, author: user) + create(:ci_build, pipeline: pipeline, status: :success, author: user) pipeline.run! pipeline.succeed! end it 'has the name' do - expect(subject.test_events.first['name']).not_to be_nil + expect(subject.test_events.first[:name]).not_to be_nil end it 'has the ID' do - expect(subject.test_events.first['id']).not_to be_nil + expect(subject.test_events.first[:id]).not_to be_nil end it 'has the URL' do - expect(subject.test_events.first['url']).not_to be_nil + expect(subject.test_events.first[:url]).not_to be_nil end it 'has the branch name' do - expect(subject.test_events.first['branch']).not_to be_nil + expect(subject.test_events.first[:branch]).not_to be_nil end it 'has the branch URL' do - expect(subject.test_events.first['branch_url']).not_to be_nil + expect(subject.test_events.first[:branch][:url]).not_to be_nil end it 'has the short SHA' do - expect(subject.test_events.first['sha']).not_to be_nil + expect(subject.test_events.first[:short_sha]).not_to be_nil end it 'has the commit URL' do - expect(subject.test_events.first['commit_url']).not_to be_nil + expect(subject.test_events.first[:commit_url]).not_to be_nil end it 'has the date' do - expect(subject.test_events.first['date']).not_to be_nil + expect(subject.test_events.first[:date]).not_to be_nil end it 'has the total time' do - expect(subject.test_events.first['total_time']).not_to be_nil + expect(subject.test_events.first[:total_time]).not_to be_nil end end @@ -164,39 +164,39 @@ describe Gitlab::CycleAnalytics::Events do let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } it 'has the total time' do - expect(subject.review_events.first['total_time']).to eq('less than a minute') + expect(subject.review_events.first[:total_time]).to eq('less than a minute') end it 'has a title' do - expect(subject.review_events.first['title']).to eq('Awesome merge_request') + expect(subject.review_events.first[:title]).to eq('Awesome merge_request') end it 'has an iid' do - expect(subject.review_events.first['iid']).to eq(context.iid.to_s) + expect(subject.review_events.first[:iid]).to eq(context.iid.to_s) end it 'has the URL' do - expect(subject.review_events.first['url']).not_to be_nil + expect(subject.review_events.first[:url]).not_to be_nil end it 'has a state' do - expect(subject.review_events.first['state']).not_to be_nil + expect(subject.review_events.first[:state]).not_to be_nil end it 'has a created_at timestamp' do - expect(subject.review_events.first['opened_at']).not_to be_nil + expect(subject.review_events.first[:opened_at]).not_to be_nil end it "has the author's URL" do - expect(subject.review_events.first['author_profile_url']).not_to be_nil + expect(subject.review_events.first[:author_profile_url]).not_to be_nil end it "has the author's avatar URL" do - expect(subject.review_events.first['author_avatar_url']).not_to be_nil + expect(subject.review_events.first[:author_avatar_url]).not_to be_nil end it "has the author's name" do - expect(subject.review_events.first['author_name']).to eq(MergeRequest.first.author.name) + expect(subject.review_events.first[:author_name]).to eq(MergeRequest.first.author.name) end end @@ -221,43 +221,43 @@ describe Gitlab::CycleAnalytics::Events do end it 'has the name' do - expect(subject.staging_events.first['name']).not_to be_nil + expect(subject.staging_events.first[:name]).not_to be_nil end it 'has the ID' do - expect(subject.staging_events.first['id']).not_to be_nil + expect(subject.staging_events.first[:id]).not_to be_nil end it 'has the URL' do - expect(subject.staging_events.first['url']).not_to be_nil + expect(subject.staging_events.first[:url]).not_to be_nil end it 'has the branch name' do - expect(subject.staging_events.first['branch']).not_to be_nil + expect(subject.staging_events.first[:branch]).not_to be_nil end it 'has the branch URL' do - expect(subject.staging_events.first['branch_url']).not_to be_nil + expect(subject.staging_events.first[:branch_url]).not_to be_nil end it 'has the short SHA' do - expect(subject.staging_events.first['sha']).not_to be_nil + expect(subject.staging_events.first[:sha]).not_to be_nil end it 'has the commit URL' do - expect(subject.staging_events.first['commit_url']).not_to be_nil + expect(subject.staging_events.first[:commit_url]).not_to be_nil end it 'has the date' do - expect(subject.staging_events.first['date']).not_to be_nil + expect(subject.staging_events.first[:date]).not_to be_nil end it 'has the total time' do - expect(subject.staging_events.first['total_time']).not_to be_nil + expect(subject.staging_events.first[:total_time]).not_to be_nil end it 'has the author name' do - expect(subject.staging_events.first['author_name']).not_to be_nil + expect(subject.staging_events.first[:author_name]).not_to be_nil end end @@ -270,35 +270,35 @@ describe Gitlab::CycleAnalytics::Events do end it 'has the total time' do - expect(subject.production_events.first['total_time']).to eq('2 days') + expect(subject.production_events.first[:total_time]).to eq('2 days') end it 'has a title' do - expect(subject.production_events.first['title']).to eq(context.title) + expect(subject.production_events.first[:title]).to eq(context.title) end it 'has the URL' do - expect(subject.production_events.first['url']).not_to be_nil + expect(subject.production_events.first[:url]).not_to be_nil end it 'has an iid' do - expect(subject.production_events.first['iid']).to eq(context.iid.to_s) + expect(subject.production_events.first[:iid]).to eq(context.iid.to_s) end it 'has a created_at timestamp' do - expect(subject.production_events.first['created_at']).to end_with('ago') + expect(subject.production_events.first[:created_at]).to end_with('ago') end it "has the author's URL" do - expect(subject.production_events.first['author_profile_url']).not_to be_nil + expect(subject.production_events.first[:author_profile_url]).not_to be_nil end it "has the author's avatar URL" do - expect(subject.production_events.first['author_avatar_url']).not_to be_nil + expect(subject.production_events.first[:author_avatar_url]).not_to be_nil end it "has the author's name" do - expect(subject.production_events.first['author_name']).to eq(context.author.name) + expect(subject.production_events.first[:author_name]).to eq(context.author.name) end end diff --git a/spec/serializers/analytics_build_entity_spec.rb b/spec/serializers/analytics_build_entity_spec.rb index 33653d5b1e0..24040034f68 100644 --- a/spec/serializers/analytics_build_entity_spec.rb +++ b/spec/serializers/analytics_build_entity_spec.rb @@ -6,14 +6,19 @@ describe AnalyticsBuildEntity do end context 'when build is a regular job' do - let(:build) { create(:ci_build) } + let(:user) { create(:user) } + let(:build) { create(:ci_build, author: user) } subject { entity.as_json } - it 'contains url to build page and retry action' do + it 'contains URLs' do expect(subject).to include(:url, :branch_url, :commit_url) end + it 'contains the author' do + expect(subject).to include(:author) + end + it 'does not contain sensitive information' do expect(subject).not_to include(/token/) expect(subject).not_to include(/variables/) -- cgit v1.2.1 From 0ddf825ddf7bc480004919762b187390d0b900e9 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 14 Nov 2016 18:05:13 +0100 Subject: WIP - adding a generic entity serializer that should accept a Hash coming from Arel --- app/serializers/analytics_generic_entity.rb | 32 ++++++++++++++++++ app/serializers/analytics_generic_serializer.rb | 3 ++ spec/serializers/analytics_generic_entity_spec.rb | 39 ++++++++++++++++++++++ .../analytics_generic_serializer_spec.rb | 24 +++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 app/serializers/analytics_generic_entity.rb create mode 100644 app/serializers/analytics_generic_serializer.rb create mode 100644 spec/serializers/analytics_generic_entity_spec.rb create mode 100644 spec/serializers/analytics_generic_serializer_spec.rb diff --git a/app/serializers/analytics_generic_entity.rb b/app/serializers/analytics_generic_entity.rb new file mode 100644 index 00000000000..b007d9056d0 --- /dev/null +++ b/app/serializers/analytics_generic_entity.rb @@ -0,0 +1,32 @@ +class AnalyticsGenericEntity < Grape::Entity + include RequestAwareEntity + include ActionView::Helpers::DateHelper + + + expose :title + expose :iid + expose :started_at, as: :date + expose :author, using: UserEntity + + expose :total_time do |object| + distance_of_time_in_words(object[:total_time].to_f) + end + + expose(:date) do |object| + interval_in_words(object[:created_at]) + end + + expose :url do |object| + url_to("namespace_project_#{object[:entity]}".to_sym, id: object[:iid].to_s) + end + + private + + def url_to(route, id = nil) + public_send("#{route}_url", options[:project].namespace, options[:project], id) + end + + def interval_in_words(diff) + "#{distance_of_time_in_words(diff.to_f)} ago" + end +end diff --git a/app/serializers/analytics_generic_serializer.rb b/app/serializers/analytics_generic_serializer.rb new file mode 100644 index 00000000000..2347b56a1d6 --- /dev/null +++ b/app/serializers/analytics_generic_serializer.rb @@ -0,0 +1,3 @@ +class AnalyticsGenericSerializer < BaseSerializer + entity AnalyticsGenericEntity +end diff --git a/spec/serializers/analytics_generic_entity_spec.rb b/spec/serializers/analytics_generic_entity_spec.rb new file mode 100644 index 00000000000..a09dae4520c --- /dev/null +++ b/spec/serializers/analytics_generic_entity_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe AnalyticsGenericEntity do + let(:user) { create(:user) } + let(:entity_hash) { + { + total_time: "172802.724419", + title: "Eos voluptatem inventore in sed.", + iid: "1", + id: "1", + created_at: "2016-11-12 15:04:02.948604", + author: user, + entity: :merge_request + } + } + + let(:project) { create(:empty_project) } + + let(:entity) do + described_class.new(entity_hash, request: double, project: project) + end + + context 'generic entity' do + subject { entity.as_json } + + it 'contains the entity URL' do + expect(subject).to include(:url) + end + + it 'contains the author' do + expect(subject).to include(:author) + end + + it 'does not contain sensitive information' do + expect(subject).not_to include(/token/) + expect(subject).not_to include(/variables/) + end + end +end diff --git a/spec/serializers/analytics_generic_serializer_spec.rb b/spec/serializers/analytics_generic_serializer_spec.rb new file mode 100644 index 00000000000..24fd94810d4 --- /dev/null +++ b/spec/serializers/analytics_generic_serializer_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe AnalyticsBuildSerializer do + let(:serializer) do + described_class + .new(project: project) + .represent(resource) + end + + let(:json) { serializer.as_json } + let(:project) { create(:project) } + let(:resource) { create(:ci_build) } + + context 'when there is a single object provided' do + it 'it generates payload for single object' do + expect(json).to be_an_instance_of Hash + end + + it 'contains important elements of analyticsBuild' do + expect(json) + .to include(:name, :branch, :short_sha, :date, :total_time, :url, :branch_url, :commit_url, :author) + end + end +end -- cgit v1.2.1 From 6a2737e6a82875311f71f451939b2732562533d4 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 15 Nov 2016 09:39:45 +0100 Subject: WIP - refactor events to use a generic build entity for some of the hybrid events --- app/serializers/analytics_generic_entity.rb | 2 +- lib/gitlab/cycle_analytics/events.rb | 14 ++++---------- lib/gitlab/cycle_analytics/events_fetcher.rb | 10 ---------- lib/gitlab/cycle_analytics/query_config.rb | 16 ++++------------ spec/serializers/analytics_build_entity_spec.rb | 6 +++--- spec/serializers/analytics_build_serializer_spec.rb | 6 ++---- spec/serializers/analytics_generic_serializer_spec.rb | 18 ++++++++++++++---- 7 files changed, 28 insertions(+), 44 deletions(-) diff --git a/app/serializers/analytics_generic_entity.rb b/app/serializers/analytics_generic_entity.rb index b007d9056d0..2692e9f21f1 100644 --- a/app/serializers/analytics_generic_entity.rb +++ b/app/serializers/analytics_generic_entity.rb @@ -23,7 +23,7 @@ class AnalyticsGenericEntity < Grape::Entity private def url_to(route, id = nil) - public_send("#{route}_url", options[:project].namespace, options[:project], id) + public_send("#{route}_url", request.project.namespace, request.project, id) end def interval_in_words(diff) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index f2fa54f208f..8be102c9fa0 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -1,8 +1,6 @@ module Gitlab module CycleAnalytics class Events - include ActionView::Helpers::DateHelper - def initialize(project:, options:) @project = project @fetcher = EventsFetcher.new(project: project, options: options) @@ -51,20 +49,16 @@ module Gitlab private - def parse_event(event, entity: :issue) - event['url'] = Gitlab::LightUrlBuilder.build(entity: entity, project: @project, id: event['iid'].to_s) - event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) - event['created_at'] = interval_in_words(event['created_at']) - event['author_profile_url'] = Gitlab::LightUrlBuilder.build(entity: :user, id: event['author_username']) - event['author_avatar_url'] = Gitlab::LightUrlBuilder.build(entity: :user_avatar, id: event['author_id']) + def parse_event(event) + event['author'] = User.find(event.remove('author_id')) - event.except!('author_id', 'author_username') + AnalyticsGenericSerializer.new(project: @project).represent(event).as_json end def parse_build_event(event) build = ::Ci::Build.find(event['id']) - AnalyticsBuildSerializer.new(project: @project).represent(build).as_json + AnalyticsBuildSerializer.new.represent(build).as_json end def first_time_reference_commit(commits, event) diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 5631867d451..c12fdd58a47 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -15,24 +15,14 @@ module Gitlab end end - def issue_custom_query(base_query) - base_query.join(user_table).on(issue_table[:author_id].eq(user_table[:id])) - end - def plan_custom_query(base_query) base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) end - def review_custom_query(base_query) - base_query.join(user_table).on(mr_table[:author_id].eq(user_table[:id])) - end - def test_custom_query(base_query) base_query.join(build_table).on(mr_metrics_table[:ci_commit_id].eq(build_table[:commit_id])) end - alias_method :code_custom_query, :issue_custom_query - alias_method :production_custom_query, :issue_custom_query alias_method :staging_custom_query, :test_custom_query end end diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index 0393567378f..d077a3e1a14 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -24,9 +24,7 @@ module Gitlab issue_table[:iid], issue_table[:id], issue_table[:created_at], - user_table[:name].as('author_name'), - user_table[:username].as('author_username'), - user_table[:id].as('author_id')] + issue_table[:author_id]] } end @@ -48,9 +46,7 @@ module Gitlab mr_table[:id], mr_table[:created_at], mr_table[:state], - user_table[:name].as('author_name'), - user_table[:username].as('author_username'), - user_table[:id].as('author_id')], + mr_table[:author_id]], order: mr_table[:created_at] } end @@ -73,9 +69,7 @@ module Gitlab mr_table[:id], mr_table[:created_at].as('opened_at'), mr_table[:state], - user_table[:name].as('author_name'), - user_table[:username].as('author_username'), - user_table[:id].as('author_id')] + mr_table[:author_id]] } end @@ -96,9 +90,7 @@ module Gitlab issue_table[:iid], issue_table[:id], issue_table[:created_at], - user_table[:name].as('author_name'), - user_table[:username].as('author_username'), - user_table[:id].as('author_id')] + issue_table[:author_id]] } end end diff --git a/spec/serializers/analytics_build_entity_spec.rb b/spec/serializers/analytics_build_entity_spec.rb index 24040034f68..9ac6f20fd3c 100644 --- a/spec/serializers/analytics_build_entity_spec.rb +++ b/spec/serializers/analytics_build_entity_spec.rb @@ -5,14 +5,14 @@ describe AnalyticsBuildEntity do described_class.new(build, request: double) end - context 'when build is a regular job' do + context 'build with an author' do let(:user) { create(:user) } let(:build) { create(:ci_build, author: user) } subject { entity.as_json } - it 'contains URLs' do - expect(subject).to include(:url, :branch_url, :commit_url) + it 'contains the URL' do + expect(subject).to include(:url) end it 'contains the author' do diff --git a/spec/serializers/analytics_build_serializer_spec.rb b/spec/serializers/analytics_build_serializer_spec.rb index 24fd94810d4..a0a9d9a5f12 100644 --- a/spec/serializers/analytics_build_serializer_spec.rb +++ b/spec/serializers/analytics_build_serializer_spec.rb @@ -3,12 +3,10 @@ require 'spec_helper' describe AnalyticsBuildSerializer do let(:serializer) do described_class - .new(project: project) - .represent(resource) + .new.represent(resource) end let(:json) { serializer.as_json } - let(:project) { create(:project) } let(:resource) { create(:ci_build) } context 'when there is a single object provided' do @@ -18,7 +16,7 @@ describe AnalyticsBuildSerializer do it 'contains important elements of analyticsBuild' do expect(json) - .to include(:name, :branch, :short_sha, :date, :total_time, :url, :branch_url, :commit_url, :author) + .to include(:name, :branch, :short_sha, :date, :total_time, :url, :author) end end end diff --git a/spec/serializers/analytics_generic_serializer_spec.rb b/spec/serializers/analytics_generic_serializer_spec.rb index 24fd94810d4..4d523878015 100644 --- a/spec/serializers/analytics_generic_serializer_spec.rb +++ b/spec/serializers/analytics_generic_serializer_spec.rb @@ -1,15 +1,26 @@ require 'spec_helper' -describe AnalyticsBuildSerializer do +describe AnalyticsGenericSerializer do let(:serializer) do described_class .new(project: project) .represent(resource) end + let(:user) { create(:user) } let(:json) { serializer.as_json } let(:project) { create(:project) } - let(:resource) { create(:ci_build) } + let(:resource) { + { + total_time: "172802.724419", + title: "Eos voluptatem inventore in sed.", + iid: "1", + id: "1", + created_at: "2016-11-12 15:04:02.948604", + author: user, + entity: :merge_request + } + } context 'when there is a single object provided' do it 'it generates payload for single object' do @@ -17,8 +28,7 @@ describe AnalyticsBuildSerializer do end it 'contains important elements of analyticsBuild' do - expect(json) - .to include(:name, :branch, :short_sha, :date, :total_time, :url, :branch_url, :commit_url, :author) + expect(json).to include(:title, :iid, :date, :total_time, :url, :author) end end end -- cgit v1.2.1 From 747e5c3b6fb6e744bc1fce04f94930fdbb5f9121 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 15 Nov 2016 09:59:35 +0100 Subject: use request to pass extra parameters --- app/serializers/analytics_generic_entity.rb | 2 +- lib/gitlab/cycle_analytics/events.rb | 4 ++-- spec/serializers/analytics_generic_entity_spec.rb | 4 ++-- spec/serializers/analytics_generic_serializer_spec.rb | 3 +-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/serializers/analytics_generic_entity.rb b/app/serializers/analytics_generic_entity.rb index 2692e9f21f1..e8cad86030e 100644 --- a/app/serializers/analytics_generic_entity.rb +++ b/app/serializers/analytics_generic_entity.rb @@ -17,7 +17,7 @@ class AnalyticsGenericEntity < Grape::Entity end expose :url do |object| - url_to("namespace_project_#{object[:entity]}".to_sym, id: object[:iid].to_s) + url_to("namespace_project_#{request.entity}".to_sym, id: object[:iid].to_s) end private diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 8be102c9fa0..077141110d0 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -49,10 +49,10 @@ module Gitlab private - def parse_event(event) + def parse_event(event, entity: :issue) event['author'] = User.find(event.remove('author_id')) - AnalyticsGenericSerializer.new(project: @project).represent(event).as_json + AnalyticsGenericSerializer.new(project: @project, entity: entity).represent(event).as_json end def parse_build_event(event) diff --git a/spec/serializers/analytics_generic_entity_spec.rb b/spec/serializers/analytics_generic_entity_spec.rb index a09dae4520c..7c731da1dd1 100644 --- a/spec/serializers/analytics_generic_entity_spec.rb +++ b/spec/serializers/analytics_generic_entity_spec.rb @@ -10,14 +10,14 @@ describe AnalyticsGenericEntity do id: "1", created_at: "2016-11-12 15:04:02.948604", author: user, - entity: :merge_request } } let(:project) { create(:empty_project) } + let(:request) { EntityRequest.new(project: project, entity: :merge_request) } let(:entity) do - described_class.new(entity_hash, request: double, project: project) + described_class.new(entity_hash, request: request, project: project) end context 'generic entity' do diff --git a/spec/serializers/analytics_generic_serializer_spec.rb b/spec/serializers/analytics_generic_serializer_spec.rb index 4d523878015..452595fb1f5 100644 --- a/spec/serializers/analytics_generic_serializer_spec.rb +++ b/spec/serializers/analytics_generic_serializer_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe AnalyticsGenericSerializer do let(:serializer) do described_class - .new(project: project) + .new(project: project, entity: :merge_request) .represent(resource) end @@ -18,7 +18,6 @@ describe AnalyticsGenericSerializer do id: "1", created_at: "2016-11-12 15:04:02.948604", author: user, - entity: :merge_request } } -- cgit v1.2.1 From f5600997512f1068cdc36ad7d086e7447dbc6d9d Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 15 Nov 2016 10:33:01 +0100 Subject: fix issue events and related spec - now using generic serializer for a hash --- app/serializers/analytics_generic_entity.rb | 5 ++--- app/serializers/analytics_generic_serializer.rb | 6 ++++++ lib/gitlab/cycle_analytics/events.rb | 8 ++++---- spec/lib/gitlab/cycle_analytics/events_spec.rb | 6 +++--- spec/serializers/analytics_generic_serializer_spec.rb | 2 +- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/app/serializers/analytics_generic_entity.rb b/app/serializers/analytics_generic_entity.rb index e8cad86030e..8c8a40cd90b 100644 --- a/app/serializers/analytics_generic_entity.rb +++ b/app/serializers/analytics_generic_entity.rb @@ -5,14 +5,13 @@ class AnalyticsGenericEntity < Grape::Entity expose :title expose :iid - expose :started_at, as: :date expose :author, using: UserEntity expose :total_time do |object| distance_of_time_in_words(object[:total_time].to_f) end - expose(:date) do |object| + expose(:created_at) do |object| interval_in_words(object[:created_at]) end @@ -22,7 +21,7 @@ class AnalyticsGenericEntity < Grape::Entity private - def url_to(route, id = nil) + def url_to(route, id) public_send("#{route}_url", request.project.namespace, request.project, id) end diff --git a/app/serializers/analytics_generic_serializer.rb b/app/serializers/analytics_generic_serializer.rb index 2347b56a1d6..9000e25a91d 100644 --- a/app/serializers/analytics_generic_serializer.rb +++ b/app/serializers/analytics_generic_serializer.rb @@ -1,3 +1,9 @@ class AnalyticsGenericSerializer < BaseSerializer entity AnalyticsGenericEntity + + def represent(resource, opts = {}) + resource.symbolize_keys! + + super(resource, opts) + end end diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 077141110d0..3b397bab178 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -7,7 +7,7 @@ module Gitlab end def issue_events - @fetcher.fetch(stage: :issue).each { |event| parse_event(event) } + @fetcher.fetch(stage: :issue).map { |event| parse_event(event) } end def plan_events @@ -24,7 +24,7 @@ module Gitlab end def code_events - @fetcher.fetch(stage: :code).each { |event| parse_event(event, entity: :merge_request) } + @fetcher.fetch(stage: :code).map { |event| parse_event(event, entity: :merge_request) } end def test_events @@ -34,7 +34,7 @@ module Gitlab end def review_events - @fetcher.fetch(stage: :review).each { |event| parse_event(event) } + @fetcher.fetch(stage: :review).map { |event| parse_event(event) } end def staging_events @@ -50,7 +50,7 @@ module Gitlab private def parse_event(event, entity: :issue) - event['author'] = User.find(event.remove('author_id')) + event['author'] = User.find(event.delete('author_id')) AnalyticsGenericSerializer.new(project: @project, entity: entity).represent(event).as_json end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index ebeea2be39e..073469a2b80 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -34,15 +34,15 @@ describe Gitlab::CycleAnalytics::Events do end it "has the author's URL" do - expect(subject.issue_events.first[:author_profile_url]).not_to be_nil + expect(subject.issue_events.first[:author][:web_url]).not_to be_nil end it "has the author's avatar URL" do - expect(subject.issue_events.first[:author_avatar_url]).not_to be_nil + expect(subject.issue_events.first[:author][:avatar_url]).not_to be_nil end it "has the author's name" do - expect(subject.issue_events.first[:author_name]).to eq(context.author.name) + expect(subject.issue_events.first[:author][:name]).to eq(context.author.name) end end diff --git a/spec/serializers/analytics_generic_serializer_spec.rb b/spec/serializers/analytics_generic_serializer_spec.rb index 452595fb1f5..9eeb7bee562 100644 --- a/spec/serializers/analytics_generic_serializer_spec.rb +++ b/spec/serializers/analytics_generic_serializer_spec.rb @@ -27,7 +27,7 @@ describe AnalyticsGenericSerializer do end it 'contains important elements of analyticsBuild' do - expect(json).to include(:title, :iid, :date, :total_time, :url, :author) + expect(json).to include(:title, :iid, :created_at, :total_time, :url, :author) end end end -- cgit v1.2.1 From 8743e59f78cd8f8701460796dcc06854281f3f73 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 15 Nov 2016 11:36:39 +0100 Subject: get all stages to use serlalizers - apart from plan - WIP --- app/serializers/analytics_build_entity.rb | 11 +++++-- app/serializers/analytics_generic_entity.rb | 8 ++--- app/serializers/entity_date_helper.rb | 7 +++++ lib/gitlab/cycle_analytics/events.rb | 4 +-- lib/gitlab/cycle_analytics/query_config.rb | 2 +- spec/lib/gitlab/cycle_analytics/events_spec.rb | 42 +++++++++++++++----------- 6 files changed, 46 insertions(+), 28 deletions(-) create mode 100644 app/serializers/entity_date_helper.rb diff --git a/app/serializers/analytics_build_entity.rb b/app/serializers/analytics_build_entity.rb index a07f0beccd4..eb1eef3424a 100644 --- a/app/serializers/analytics_build_entity.rb +++ b/app/serializers/analytics_build_entity.rb @@ -1,14 +1,21 @@ class AnalyticsBuildEntity < Grape::Entity include RequestAwareEntity + include EntityDateHelper expose :name expose :id expose :ref, as: :branch expose :short_sha - expose :started_at, as: :date - expose :duration, as: :total_time expose :author, using: UserEntity + expose :started_at, as: :date do |build| + interval_in_words(build[:started_at]) + end + + expose :duration, as: :total_time do |build| + distance_of_time_in_words(build[:duration].to_f) + end + expose :branch do expose :ref, as: :name diff --git a/app/serializers/analytics_generic_entity.rb b/app/serializers/analytics_generic_entity.rb index 8c8a40cd90b..d7abe3f5f50 100644 --- a/app/serializers/analytics_generic_entity.rb +++ b/app/serializers/analytics_generic_entity.rb @@ -1,10 +1,10 @@ class AnalyticsGenericEntity < Grape::Entity include RequestAwareEntity - include ActionView::Helpers::DateHelper - + include EntityDateHelper expose :title expose :iid + expose :state, if: ->(_instance, options) { options[:request].entity == :merge_request } expose :author, using: UserEntity expose :total_time do |object| @@ -24,8 +24,4 @@ class AnalyticsGenericEntity < Grape::Entity def url_to(route, id) public_send("#{route}_url", request.project.namespace, request.project, id) end - - def interval_in_words(diff) - "#{distance_of_time_in_words(diff.to_f)} ago" - end end diff --git a/app/serializers/entity_date_helper.rb b/app/serializers/entity_date_helper.rb new file mode 100644 index 00000000000..0772f652f0c --- /dev/null +++ b/app/serializers/entity_date_helper.rb @@ -0,0 +1,7 @@ +module EntityDateHelper + include ActionView::Helpers::DateHelper + + def interval_in_words(diff) + "#{distance_of_time_in_words(diff.to_f)} ago" + end +end \ No newline at end of file diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 3b397bab178..69688a2a0ca 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -34,7 +34,7 @@ module Gitlab end def review_events - @fetcher.fetch(stage: :review).map { |event| parse_event(event) } + @fetcher.fetch(stage: :review).map { |event| parse_event(event, entity: :merge_request) } end def staging_events @@ -44,7 +44,7 @@ module Gitlab end def production_events - @fetcher.fetch(stage: :production).each { |event| parse_event(event) } + @fetcher.fetch(stage: :production).map { |event| parse_event(event) } end private diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index d077a3e1a14..a18c263ba53 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -67,7 +67,7 @@ module Gitlab projections: [mr_table[:title], mr_table[:iid], mr_table[:id], - mr_table[:created_at].as('opened_at'), + mr_table[:created_at], mr_table[:state], mr_table[:author_id]] } diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 073469a2b80..55ab28e2af8 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -60,15 +60,15 @@ describe Gitlab::CycleAnalytics::Events do end it "has the author's URL" do - expect(subject.plan_events.first[:author_profile_url]).not_to be_nil + expect(subject.plan_events.first[:author][:web_url]).not_to be_nil end it "has the author's avatar URL" do - expect(subject.plan_events.first[:author_avatar_url]).not_to be_nil + expect(subject.plan_events.first[:author][:avatar_url]).not_to be_nil end it "has the author's name" do - expect(subject.plan_events.first[:author_name]).not_to be_nil + expect(subject.plan_events.first[:author][:name]).to eq(context.author.name) end end @@ -94,15 +94,15 @@ describe Gitlab::CycleAnalytics::Events do end it "has the author's URL" do - expect(subject.code_events.first[:author_profile_url]).not_to be_nil + expect(subject.code_events.first[:author][:web_url]).not_to be_nil end it "has the author's avatar URL" do - expect(subject.code_events.first[:author_avatar_url]).not_to be_nil + expect(subject.code_events.first[:author][:avatar_url]).not_to be_nil end it "has the author's name" do - expect(subject.code_events.first[:author_name]).to eq(context.author.name) + expect(subject.code_events.first[:author][:name]).to eq(MergeRequest.first.author.name) end end @@ -184,19 +184,19 @@ describe Gitlab::CycleAnalytics::Events do end it 'has a created_at timestamp' do - expect(subject.review_events.first[:opened_at]).not_to be_nil + expect(subject.review_events.first[:created_at]).not_to be_nil end it "has the author's URL" do - expect(subject.review_events.first[:author_profile_url]).not_to be_nil + expect(subject.review_events.first[:author][:web_url]).not_to be_nil end it "has the author's avatar URL" do - expect(subject.review_events.first[:author_avatar_url]).not_to be_nil + expect(subject.review_events.first[:author][:avatar_url]).not_to be_nil end it "has the author's name" do - expect(subject.review_events.first[:author_name]).to eq(MergeRequest.first.author.name) + expect(subject.review_events.first[:author][:name]).to eq(MergeRequest.first.author.name) end end @@ -237,11 +237,11 @@ describe Gitlab::CycleAnalytics::Events do end it 'has the branch URL' do - expect(subject.staging_events.first[:branch_url]).not_to be_nil + expect(subject.staging_events.first[:branch][:url]).not_to be_nil end it 'has the short SHA' do - expect(subject.staging_events.first[:sha]).not_to be_nil + expect(subject.staging_events.first[:short_sha]).not_to be_nil end it 'has the commit URL' do @@ -256,8 +256,16 @@ describe Gitlab::CycleAnalytics::Events do expect(subject.staging_events.first[:total_time]).not_to be_nil end - it 'has the author name' do - expect(subject.staging_events.first[:author_name]).not_to be_nil + it "has the author's URL" do + expect(subject.staging_events.first[:author][:web_url]).not_to be_nil + end + + it "has the author's avatar URL" do + expect(subject.staging_events.first[:author][:avatar_url]).not_to be_nil + end + + it "has the author's name" do + expect(subject.staging_events.first[:author][:name]).to eq(MergeRequest.first.author.name) end end @@ -290,15 +298,15 @@ describe Gitlab::CycleAnalytics::Events do end it "has the author's URL" do - expect(subject.production_events.first[:author_profile_url]).not_to be_nil + expect(subject.production_events.first[:author][:web_url]).not_to be_nil end it "has the author's avatar URL" do - expect(subject.production_events.first[:author_avatar_url]).not_to be_nil + expect(subject.production_events.first[:author][:avatar_url]).not_to be_nil end it "has the author's name" do - expect(subject.production_events.first[:author_name]).to eq(context.author.name) + expect(subject.production_events.first[:author][:name]).to eq(context.author.name) end end -- cgit v1.2.1 From 73e9ec631995d5ed91a3eb5e8416c924e7d7c5ad Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 15 Nov 2016 13:23:17 +0100 Subject: serialize all the things! --- app/serializers/analytics_commit_entity.rb | 14 ++++++++++++ app/serializers/analytics_commit_serializer.rb | 3 +++ lib/gitlab/cycle_analytics/events.rb | 31 +++++++++----------------- spec/lib/gitlab/cycle_analytics/events_spec.rb | 8 +++++-- 4 files changed, 34 insertions(+), 22 deletions(-) create mode 100644 app/serializers/analytics_commit_entity.rb create mode 100644 app/serializers/analytics_commit_serializer.rb diff --git a/app/serializers/analytics_commit_entity.rb b/app/serializers/analytics_commit_entity.rb new file mode 100644 index 00000000000..a932d612e0f --- /dev/null +++ b/app/serializers/analytics_commit_entity.rb @@ -0,0 +1,14 @@ +class AnalyticsCommitEntity < CommitEntity + include RequestAwareEntity + include EntityDateHelper + + expose :short_id, as: :short_sha + + expose :total_time do |commit| + distance_of_time_in_words(request.total_time.to_f) + end + + unexpose :author_name + unexpose :author_email + unexpose :message +end diff --git a/app/serializers/analytics_commit_serializer.rb b/app/serializers/analytics_commit_serializer.rb new file mode 100644 index 00000000000..cdbfecf2b70 --- /dev/null +++ b/app/serializers/analytics_commit_serializer.rb @@ -0,0 +1,3 @@ +class AnalyticsCommitSerializer < BaseSerializer + entity AnalyticsCommitEntity +end diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 69688a2a0ca..d3c7d6fe1aa 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -7,55 +7,46 @@ module Gitlab end def issue_events - @fetcher.fetch(stage: :issue).map { |event| parse_event(event) } + @fetcher.fetch(stage: :issue).map { |event| serialize_event(event) } end def plan_events - @fetcher.fetch(stage: :plan).each do |event| - event['total_time'] = distance_of_time_in_words(event['total_time'].to_f) + @fetcher.fetch(stage: :plan).map do |event| commit = first_time_reference_commit(event.delete('commits'), event) - event['title'] = commit.title - event['url'] = Gitlab::LightUrlBuilder.build(entity: :commit, project: @project, id: commit.id) - event['sha'] = commit.short_id - event['author_name'] = commit.author.name - event['author_profile_url'] = Gitlab::LightUrlBuilder.build(entity: :user, id: commit.author.username) - event['author_avatar_url'] = Gitlab::LightUrlBuilder.build(entity: :user_avatar, id: commit.author.id) + + AnalyticsCommitSerializer.new(project: @project, total_time: event['total_time']).represent(commit).as_json end end def code_events - @fetcher.fetch(stage: :code).map { |event| parse_event(event, entity: :merge_request) } + @fetcher.fetch(stage: :code).map { |event| serialize_event(event, entity: :merge_request) } end def test_events - @fetcher.fetch(stage: :test).map do |event| - parse_build_event(event) - end + @fetcher.fetch(stage: :test).map { |event| serialize_build_event(event) } end def review_events - @fetcher.fetch(stage: :review).map { |event| parse_event(event, entity: :merge_request) } + @fetcher.fetch(stage: :review).map { |event| serialize_event(event, entity: :merge_request) } end def staging_events - @fetcher.fetch(stage: :staging).map do |event| - parse_build_event(event) - end + @fetcher.fetch(stage: :staging).map { |event| serialize_build_event(event) } end def production_events - @fetcher.fetch(stage: :production).map { |event| parse_event(event) } + @fetcher.fetch(stage: :production).map { |event| serialize_event(event) } end private - def parse_event(event, entity: :issue) + def serialize_event(event, entity: :issue) event['author'] = User.find(event.delete('author_id')) AnalyticsGenericSerializer.new(project: @project, entity: entity).represent(event).as_json end - def parse_build_event(event) + def serialize_build_event(event) build = ::Ci::Build.find(event['id']) AnalyticsBuildSerializer.new.represent(build).as_json diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 55ab28e2af8..d46e70e9ba2 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -52,7 +52,11 @@ describe Gitlab::CycleAnalytics::Events do end it 'has a sha short ID' do - expect(subject.plan_events.first[:sha]).not_to be_nil + expect(subject.plan_events.first[:short_sha]).not_to be_nil + end + + it 'has the URL' do + expect(subject.plan_events.first[:commit_url]).not_to be_nil end it 'has the total time' do @@ -68,7 +72,7 @@ describe Gitlab::CycleAnalytics::Events do end it "has the author's name" do - expect(subject.plan_events.first[:author][:name]).to eq(context.author.name) + expect(subject.plan_events.first[:author][:name]).not_to be_nil end end -- cgit v1.2.1 From ca6da6ea3034a58c0edbc62336d2d49ea6e11fc9 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 15 Nov 2016 15:22:35 +0100 Subject: Renamed pipeline column and fixed permissions for builds in events controller --- app/controllers/projects/cycle_analytics/events_controller.rb | 7 ++++--- app/models/merge_request/metrics.rb | 2 +- app/workers/pipeline_metrics_worker.rb | 4 ++-- .../20161020083353_add_pipeline_id_to_merge_request_metrics.rb | 4 +++- db/schema.rb | 6 +++--- lib/gitlab/cycle_analytics/events_fetcher.rb | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 77b49d6c4b9..cc75dc247d3 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -2,6 +2,7 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll include CycleAnalyticsParams before_action :authorize_read_cycle_analytics! + before_action :authorize_read_builds!, only: [:test, :staging] def issue render_events(events.issue_events) @@ -16,7 +17,7 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll end def test - @opts = { from: start_date(events_params), branch: events_params[:branch_name] } + @options = { from: start_date(events_params), branch: events_params[:branch_name] } render_events(events.test_events) end @@ -47,12 +48,12 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll end def options - @opts ||= { from: start_date(events_params) } + @options ||= { from: start_date(events_params) } end def events_params return {} unless params[:events].present? - { start_date: params[:events][:start_date], branch_name: params[:events][:branch_name] } + params[:events].slice(:start_date, :branch_name) end end diff --git a/app/models/merge_request/metrics.rb b/app/models/merge_request/metrics.rb index 7d19943769e..cdc408738be 100644 --- a/app/models/merge_request/metrics.rb +++ b/app/models/merge_request/metrics.rb @@ -1,6 +1,6 @@ class MergeRequest::Metrics < ActiveRecord::Base belongs_to :merge_request - belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :ci_commit_id + belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :pipeline_id def record! if merge_request.merged? && self.merged_at.blank? diff --git a/app/workers/pipeline_metrics_worker.rb b/app/workers/pipeline_metrics_worker.rb index cca39761d8f..070943f1ecc 100644 --- a/app/workers/pipeline_metrics_worker.rb +++ b/app/workers/pipeline_metrics_worker.rb @@ -12,11 +12,11 @@ class PipelineMetricsWorker private def update_metrics_for_active_pipeline(pipeline) - metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: nil, ci_commit_id: pipeline.id) + metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: nil, pipeline_id: pipeline.id) end def update_metrics_for_succeeded_pipeline(pipeline) - metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: pipeline.finished_at, ci_commit_id: pipeline.id) + metrics(pipeline).update_all(latest_build_started_at: pipeline.started_at, latest_build_finished_at: pipeline.finished_at, pipeline_id: pipeline.id) end def metrics(pipeline) diff --git a/db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb b/db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb index a47e1ae7f6a..507d576d0bc 100644 --- a/db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb +++ b/db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb @@ -24,6 +24,8 @@ class AddPipelineIdToMergeRequestMetrics < ActiveRecord::Migration # disable_ddl_transaction! def change - add_reference :merge_request_metrics, :ci_commit, index: true, foreign_key: { on_delete: :cascade } + add_column :merge_request_metrics, :pipeline_id, :integer + add_index :merge_request_metrics, :pipeline_id + add_foreign_key :merge_request_metrics, :ci_commits, column: :pipeline_id, on_delete: :cascade end end diff --git a/db/schema.rb b/db/schema.rb index 5baa82e1058..983e1c90682 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -634,12 +634,12 @@ ActiveRecord::Schema.define(version: 20161109150329) do t.datetime "merged_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "ci_commit_id" + t.integer "pipeline_id" end - add_index "merge_request_metrics", ["ci_commit_id"], name: "index_merge_request_metrics_on_ci_commit_id", using: :btree add_index "merge_request_metrics", ["first_deployed_to_production_at"], name: "index_merge_request_metrics_on_first_deployed_to_production_at", using: :btree add_index "merge_request_metrics", ["merge_request_id"], name: "index_merge_request_metrics", using: :btree + add_index "merge_request_metrics", ["pipeline_id"], name: "index_merge_request_metrics_on_pipeline_id", using: :btree create_table "merge_requests", force: :cascade do |t| t.string "target_branch", null: false @@ -1246,7 +1246,7 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_foreign_key "labels", "namespaces", column: "group_id", on_delete: :cascade add_foreign_key "lists", "boards" add_foreign_key "lists", "labels" - add_foreign_key "merge_request_metrics", "ci_commits", on_delete: :cascade + add_foreign_key "merge_request_metrics", "ci_commits", column: "pipeline_id", on_delete: :cascade add_foreign_key "merge_request_metrics", "merge_requests", on_delete: :cascade add_foreign_key "merge_requests_closing_issues", "issues", on_delete: :cascade add_foreign_key "merge_requests_closing_issues", "merge_requests", on_delete: :cascade diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index c12fdd58a47..d19ce6e0f6f 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -20,7 +20,7 @@ module Gitlab end def test_custom_query(base_query) - base_query.join(build_table).on(mr_metrics_table[:ci_commit_id].eq(build_table[:commit_id])) + base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) end alias_method :staging_custom_query, :test_custom_query -- cgit v1.2.1 From f93607a305346607f4296c266d40be1692febbec Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 15 Nov 2016 15:27:50 +0100 Subject: get rid of light url builder and fix wrong spec --- lib/gitlab/light_url_builder.rb | 67 ------------------ spec/lib/gitlab/url_builder_spec.rb | 137 ++++++++++++++++++++++++------------ 2 files changed, 91 insertions(+), 113 deletions(-) delete mode 100644 lib/gitlab/light_url_builder.rb diff --git a/lib/gitlab/light_url_builder.rb b/lib/gitlab/light_url_builder.rb deleted file mode 100644 index 0c86b122b88..00000000000 --- a/lib/gitlab/light_url_builder.rb +++ /dev/null @@ -1,67 +0,0 @@ -module Gitlab - # Similar to UrlBuilder, but using IDs to avoid querying the DB for objects - # Useful for using in conjunction with Arel queries. - class LightUrlBuilder - include Gitlab::Routing.url_helpers - include GitlabRoutingHelper - include ActionView::RecordIdentifier - - def self.build(*args) - new(*args).url - end - - def initialize(entity:, project: nil, id:, opts: {}) - @entity = entity - @project = project - @id = id - @opts = opts - end - - def url - url_method = "#{@entity}_url" - raise NotImplementedError.new("No Light URL builder defined for #{@entity}") unless respond_to?(url_method) - - public_send(url_method) - end - - def issue_url - namespace_project_issue_url({ - namespace_id: @project.namespace, - project_id: @project, - id: @id - }.merge!(@opts)) - end - - def user_avatar_url - User.find(@id).avatar_url - end - - def commit_url - namespace_project_commit_url({ - namespace_id: @project.namespace, - project_id: @project, - id: @id - }.merge!(@opts)) - end - - def merge_request_url - namespace_project_merge_request_url({ - namespace_id: @project.namespace, - project_id: @project, - id: @id - }.merge!(@opts)) - end - - def branch_url - namespace_project_commit_url(@project.namespace, @project, @id) - end - - def user_url - Gitlab::Routing.url_helpers.user_url(@id) - end - - def build_url - namespace_project_build_url(@project.namespace, @project, @id) - end - end -end diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb index 5d876fdb603..a826b24419a 100644 --- a/spec/lib/gitlab/url_builder_spec.rb +++ b/spec/lib/gitlab/url_builder_spec.rb @@ -1,74 +1,119 @@ require 'spec_helper' -describe Gitlab::LightUrlBuilder, lib: true do - context 'when passing a Commit' do - it 'returns a proper URL' do - commit = build_stubbed(:commit) +describe Gitlab::UrlBuilder, lib: true do + describe '.build' do + context 'when passing a Commit' do + it 'returns a proper URL' do + commit = build_stubbed(:commit) - url = described_class.build(entity: :commit, project: commit.project, id: commit.id) + url = described_class.build(commit) - expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}" + expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}" + end end - end - context 'when passing an Issue' do - it 'returns a proper URL' do - issue = build_stubbed(:issue, iid: 42) + context 'when passing an Issue' do + it 'returns a proper URL' do + issue = build_stubbed(:issue, iid: 42) - url = described_class.build(entity: :issue, project: issue.project, id: issue.iid) + url = described_class.build(issue) - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" + expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" + end end - end - context 'when passing a MergeRequest' do - it 'returns a proper URL' do - merge_request = build_stubbed(:merge_request, iid: 42) + context 'when passing a MergeRequest' do + it 'returns a proper URL' do + merge_request = build_stubbed(:merge_request, iid: 42) - url = described_class.build(entity: :merge_request, project: merge_request.project, id: merge_request.iid) + url = described_class.build(merge_request) - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}" + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}" + end end - end - context 'when passing a build' do - it 'returns a proper URL' do - build = build_stubbed(:ci_build, project: build_stubbed(:empty_project)) + context 'when passing a Note' do + context 'on a Commit' do + it 'returns a proper URL' do + note = build_stubbed(:note_on_commit) - url = described_class.build(entity: :build, project: build.project, id: build.id) + url = described_class.build(note) - expect(url).to eq "#{Settings.gitlab['url']}/#{build.project.path_with_namespace}/builds/#{build.id}" - end - end + expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" + end + end - context 'when passing a branch' do - it 'returns a proper URL' do - branch = 'branch_name' - project = build_stubbed(:empty_project) + context 'on a Commit Diff' do + it 'returns a proper URL' do + note = build_stubbed(:diff_note_on_commit) - url = described_class.build(entity: :branch, project: project, id: branch) + url = described_class.build(note) - expect(url).to eq "#{Settings.gitlab['url']}/#{project.path_with_namespace}/commits/#{branch}" - end - end + expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" + end + end - context 'on a User' do - it 'returns a proper URL' do - user = build_stubbed(:user) + context 'on an Issue' do + it 'returns a proper URL' do + issue = create(:issue, iid: 42) + note = build_stubbed(:note_on_issue, noteable: issue) - url = described_class.build(entity: :user, id: user.username) + url = described_class.build(note) - expect(url).to eq "#{Settings.gitlab['url']}/#{user.username}" - end - end + expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}" + end + end + + context 'on a MergeRequest' do + it 'returns a proper URL' do + merge_request = create(:merge_request, iid: 42) + note = build_stubbed(:note_on_merge_request, noteable: merge_request) - context 'on a user avatar' do - it 'returns a proper URL' do - user = create(:user) + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" + end + end + + context 'on a MergeRequest Diff' do + it 'returns a proper URL' do + merge_request = create(:merge_request, iid: 42) + note = build_stubbed(:diff_note_on_merge_request, noteable: merge_request) + + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" + end + end + + context 'on a ProjectSnippet' do + it 'returns a proper URL' do + project_snippet = create(:project_snippet) + note = build_stubbed(:note_on_project_snippet, noteable: project_snippet) + + url = described_class.build(note) + + expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}" + end + end + + context 'on another object' do + it 'returns a proper URL' do + project = build_stubbed(:project) + + expect { described_class.build(project) }. + to raise_error(NotImplementedError, 'No URL builder defined for Project') + end + end + end - url = described_class.build(entity: :user_avatar, id: user.id) + context 'when passing a WikiPage' do + it 'returns a proper URL' do + wiki_page = build(:wiki_page) + url = described_class.build(wiki_page) - expect(url).to eq user.avatar_url + expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}" + end end end end -- cgit v1.2.1 From 633ddc9ed98c690c082c7347422ac85f9b592fb4 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 15 Nov 2016 16:25:37 +0100 Subject: fix authorization of builds and added relevant spec --- .../projects/cycle_analytics/events_controller.rb | 6 ++++- .../projects/cycle_analytics_events_spec.rb | 26 +++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index cc75dc247d3..cb52dfc830a 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -2,7 +2,7 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll include CycleAnalyticsParams before_action :authorize_read_cycle_analytics! - before_action :authorize_read_builds!, only: [:test, :staging] + before_action :authorize_builds!, only: [:test, :staging] def issue render_events(events.issue_events) @@ -56,4 +56,8 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll params[:events].slice(:start_date, :branch_name) end + + def authorize_builds! + return access_denied! unless current_user.can?(:read_build, project) + end end diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index d4da8707ea5..ef6e4c80911 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -39,7 +39,7 @@ describe 'cycle analytics events' do newest_sha = commits.sort_by { |k| k['date'] }.first[:sha][0...8] - expect(json_response['events'].first['sha']).to eq(newest_sha) + expect(json_response['events'].first['short_sha']).to eq(newest_sha) end it 'lists the code events' do @@ -99,6 +99,30 @@ describe 'cycle analytics events' do expect(json_response['events'].first['date']).not_to be_empty end end + + context 'with private project and builds' do + before do + ProjectMember.first.update(access_level: Gitlab::Access::GUEST) + end + + it 'does not list the test events' do + get namespace_project_cycle_analytics_test_path(project.namespace, project, format: :json) + + expect(response).to have_http_status(:not_found) + end + + it 'does not list the staging events' do + get namespace_project_cycle_analytics_staging_path(project.namespace, project, format: :json) + + expect(response).to have_http_status(:not_found) + end + + it 'lists the issue events' do + get namespace_project_cycle_analytics_issue_path(project.namespace, project, format: :json) + + expect(response).to have_http_status(:ok) + end + end end def json_response -- cgit v1.2.1 From cbc9f0cd1aa9f379952b6e4d3ad6df9971f9092a Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 16 Nov 2016 09:58:23 +0100 Subject: fix issue with commits and also updated routes --- .../projects/cycle_analytics/events_controller.rb | 6 +++++- config/routes/project.rb | 2 +- lib/gitlab/cycle_analytics/events.rb | 18 +++++++++++++----- lib/gitlab/cycle_analytics/query_config.rb | 3 ++- spec/requests/projects/cycle_analytics_events_spec.rb | 2 ++ 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index cb52dfc830a..961183f44c3 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -1,4 +1,6 @@ -class Projects::CycleAnalytics::EventsController < Projects::ApplicationController +module Projects + module CycleAnalytics +class EventsController < Projects::ApplicationController include CycleAnalyticsParams before_action :authorize_read_cycle_analytics! @@ -60,4 +62,6 @@ class Projects::CycleAnalytics::EventsController < Projects::ApplicationControll def authorize_builds! return access_denied! unless current_user.can?(:read_build, project) end +end + end end diff --git a/config/routes/project.rb b/config/routes/project.rb index 7e0536b464f..d6eae1c9fce 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -154,7 +154,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: resource :cycle_analytics, only: [:show] namespace :cycle_analytics do - scope :events, controller: '/projects/cycle_analytics/events' do + scope :events, controller: 'events' do get :issue get :plan get :code diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index d3c7d6fe1aa..f4d6d2049ef 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -12,9 +12,11 @@ module Gitlab def plan_events @fetcher.fetch(stage: :plan).map do |event| - commit = first_time_reference_commit(event.delete('commits'), event) + st_commit = first_time_reference_commit(event.delete('commits'), event) - AnalyticsCommitSerializer.new(project: @project, total_time: event['total_time']).represent(commit).as_json + next unless st_commit + + serialize_commit(event, st_commit) end end @@ -53,11 +55,17 @@ module Gitlab end def first_time_reference_commit(commits, event) - st_commit = YAML.load(commits).detect do |commit| - commit['created_at'] == event['first_mentioned_in_commit_at'] + YAML.load(commits).find do |commit| + next unless commit[:committed_date] && event['first_mentioned_in_commit_at'] + + commit[:committed_date].to_i == DateTime.parse(event['first_mentioned_in_commit_at']).to_i end + end + + def serialize_commit(event, st_commit) + commit = Commit.new(Gitlab::Git::Commit.new(st_commit), @project) - Commit.new(Gitlab::Git::Commit.new(st_commit), @project) + AnalyticsCommitSerializer.new(project: @project, total_time: event['total_time']).represent(commit).as_json end def interval_in_words(diff) diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb index a18c263ba53..4fddbad95f2 100644 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ b/lib/gitlab/cycle_analytics/query_config.rb @@ -33,7 +33,8 @@ module Gitlab start_time_attrs: issue_metrics_table[:first_associated_with_milestone_at], end_time_attrs: [issue_metrics_table[:first_added_to_board_at], issue_metrics_table[:first_mentioned_in_commit_at]], - projections: [mr_diff_table[:st_commits].as('commits')] + projections: [mr_diff_table[:st_commits].as('commits'), + issue_metrics_table[:first_mentioned_in_commit_at]] } end diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index ef6e4c80911..280aa2152b1 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -142,5 +142,7 @@ describe 'cycle analytics events' do create(:ci_build, pipeline: pipeline, status: :success, author: user) merge_merge_requests_closing_issue(issue) + + Issue::Metrics.update_all(first_mentioned_in_commit_at: mr.commits.last.committed_date) end end -- cgit v1.2.1 From cf2dcf043c0054785bc0258ab6393104499b8d70 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 16 Nov 2016 12:01:10 +0100 Subject: Refactor all query config stuff into separate classes and added specs --- .../projects/cycle_analytics/events_controller.rb | 124 ++++++++++----------- app/serializers/entity_date_helper.rb | 2 +- lib/gitlab/cycle_analytics/base_config.rb | 17 +++ lib/gitlab/cycle_analytics/code_config.rb | 18 +++ lib/gitlab/cycle_analytics/events_fetcher.rb | 16 +-- lib/gitlab/cycle_analytics/events_query.rb | 15 ++- lib/gitlab/cycle_analytics/issue_config.rb | 16 +++ lib/gitlab/cycle_analytics/plan_config.rb | 17 +++ lib/gitlab/cycle_analytics/production_config.rb | 15 +++ lib/gitlab/cycle_analytics/query_config.rb | 99 ---------------- lib/gitlab/cycle_analytics/review_config.rb | 16 +++ lib/gitlab/cycle_analytics/staging_config.rb | 14 +++ lib/gitlab/cycle_analytics/test_config.rb | 14 +++ .../lib/gitlab/cycle_analytics/code_config_spec.rb | 10 ++ spec/lib/gitlab/cycle_analytics/events_spec.rb | 6 +- .../gitlab/cycle_analytics/issue_config_spec.rb | 10 ++ .../lib/gitlab/cycle_analytics/plan_config_spec.rb | 10 ++ .../cycle_analytics/production_config_spec.rb | 10 ++ .../gitlab/cycle_analytics/review_config_spec.rb | 10 ++ .../gitlab/cycle_analytics/shared_config_spec.rb | 15 +++ .../gitlab/cycle_analytics/staging_config_spec.rb | 10 ++ .../lib/gitlab/cycle_analytics/test_config_spec.rb | 10 ++ .../projects/cycle_analytics_events_spec.rb | 4 +- spec/serializers/analytics_generic_entity_spec.rb | 4 +- .../analytics_generic_serializer_spec.rb | 4 +- 25 files changed, 296 insertions(+), 190 deletions(-) create mode 100644 lib/gitlab/cycle_analytics/base_config.rb create mode 100644 lib/gitlab/cycle_analytics/code_config.rb create mode 100644 lib/gitlab/cycle_analytics/issue_config.rb create mode 100644 lib/gitlab/cycle_analytics/plan_config.rb create mode 100644 lib/gitlab/cycle_analytics/production_config.rb delete mode 100644 lib/gitlab/cycle_analytics/query_config.rb create mode 100644 lib/gitlab/cycle_analytics/review_config.rb create mode 100644 lib/gitlab/cycle_analytics/staging_config.rb create mode 100644 lib/gitlab/cycle_analytics/test_config.rb create mode 100644 spec/lib/gitlab/cycle_analytics/code_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/issue_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/plan_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/production_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/review_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/shared_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/staging_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/test_config_spec.rb diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 961183f44c3..2b4db0fd4aa 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -1,67 +1,67 @@ module Projects module CycleAnalytics -class EventsController < Projects::ApplicationController - include CycleAnalyticsParams - - before_action :authorize_read_cycle_analytics! - before_action :authorize_builds!, only: [:test, :staging] - - def issue - render_events(events.issue_events) - end - - def plan - render_events(events.plan_events) - end - - def code - render_events(events.code_events) - end - - def test - @options = { from: start_date(events_params), branch: events_params[:branch_name] } - - render_events(events.test_events) - end - - def review - render_events(events.review_events) - end - - def staging - render_events(events.staging_events) - end - - def production - render_events(events.production_events) - end - - private - - def render_events(events_list) - respond_to do |format| - format.html - format.json { render json: { events: events_list } } + class EventsController < Projects::ApplicationController + include CycleAnalyticsParams + + before_action :authorize_read_cycle_analytics! + before_action :authorize_builds!, only: [:test, :staging] + + def issue + render_events(events.issue_events) + end + + def plan + render_events(events.plan_events) + end + + def code + render_events(events.code_events) + end + + def test + @options = { from: start_date(events_params), branch: events_params[:branch_name] } + + render_events(events.test_events) + end + + def review + render_events(events.review_events) + end + + def staging + render_events(events.staging_events) + end + + def production + render_events(events.production_events) + end + + private + + def render_events(events_list) + respond_to do |format| + format.html + format.json { render json: { events: events_list } } + end + end + + def events + @events ||= Gitlab::CycleAnalytics::Events.new(project: project, options: options) + end + + def options + @options ||= { from: start_date(events_params) } + end + + def events_params + return {} unless params[:events].present? + + params[:events].slice(:start_date, :branch_name) + end + + def authorize_builds! + return access_denied! unless current_user.can?(:read_build, project) + end end end - - def events - @events ||= Gitlab::CycleAnalytics::Events.new(project: project, options: options) - end - - def options - @options ||= { from: start_date(events_params) } - end - - def events_params - return {} unless params[:events].present? - - params[:events].slice(:start_date, :branch_name) - end - - def authorize_builds! - return access_denied! unless current_user.can?(:read_build, project) - end -end - end end diff --git a/app/serializers/entity_date_helper.rb b/app/serializers/entity_date_helper.rb index 0772f652f0c..57817ce1812 100644 --- a/app/serializers/entity_date_helper.rb +++ b/app/serializers/entity_date_helper.rb @@ -4,4 +4,4 @@ module EntityDateHelper def interval_in_words(diff) "#{distance_of_time_in_words(diff.to_f)} ago" end -end \ No newline at end of file +end diff --git a/lib/gitlab/cycle_analytics/base_config.rb b/lib/gitlab/cycle_analytics/base_config.rb new file mode 100644 index 00000000000..06ae9a15a7a --- /dev/null +++ b/lib/gitlab/cycle_analytics/base_config.rb @@ -0,0 +1,17 @@ +module Gitlab + module CycleAnalytics + class BaseConfig + extend MetricsFetcher + + class << self + attr_reader :start_time_attrs, :end_time_attrs, :projections + end + + def self.order + @order || @start_time_attrs + end + + def self.query(base_query); end + end + end +end diff --git a/lib/gitlab/cycle_analytics/code_config.rb b/lib/gitlab/cycle_analytics/code_config.rb new file mode 100644 index 00000000000..c27419310db --- /dev/null +++ b/lib/gitlab/cycle_analytics/code_config.rb @@ -0,0 +1,18 @@ +module Gitlab + module CycleAnalytics + class CodeConfig < BaseConfig + @start_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at] + + @end_time_attrs = mr_table[:created_at] + + @projections = [mr_table[:title], + mr_table[:iid], + mr_table[:id], + mr_table[:created_at], + mr_table[:state], + mr_table[:author_id]] + + @order = mr_table[:created_at] + end + end +end diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index d19ce6e0f6f..714afb88833 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -8,22 +8,10 @@ module Gitlab end def fetch(stage:) - custom_query = "#{stage}_custom_query".to_sym - - @query.execute(stage) do |base_query| - public_send(custom_query, base_query) if self.respond_to?(custom_query) + @query.execute(stage) do |stage_class, base_query| + stage_class.query(base_query) end end - - def plan_custom_query(base_query) - base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) - end - - def test_custom_query(base_query) - base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) - end - - alias_method :staging_custom_query, :test_custom_query end end end diff --git a/lib/gitlab/cycle_analytics/events_query.rb b/lib/gitlab/cycle_analytics/events_query.rb index 9d69e267d8d..b61320fc561 100644 --- a/lib/gitlab/cycle_analytics/events_query.rb +++ b/lib/gitlab/cycle_analytics/events_query.rb @@ -11,7 +11,6 @@ module Gitlab def execute(stage, &block) @stage = stage - @config = QueryConfig.get(stage) query = build_query(&block) ActiveRecord::Base.connection.exec_query(query.to_sql) @@ -21,15 +20,11 @@ module Gitlab def build_query base_query = base_query_for(@stage) - diff_fn = subtract_datetimes_diff(@config[:base_query], @config[:start_time_attrs], @config[:end_time_attrs]) + diff_fn = subtract_datetimes_diff(base_query, stage_class.start_time_attrs, stage_class.end_time_attrs) - yield base_query if block_given? + yield(stage_class, base_query) if block_given? - base_query.project(extract_epoch(diff_fn).as('total_time'), *@config[:projections]).order(order.desc) - end - - def order - @config[:order] || @config[:start_time_attrs] + base_query.project(extract_epoch(diff_fn).as('total_time'), *stage_class.projections).order(stage_class.order.desc) end def extract_epoch(arel_attribute) @@ -37,6 +32,10 @@ module Gitlab Arel.sql(%Q{EXTRACT(EPOCH FROM (#{arel_attribute.to_sql}))}) end + + def stage_class + @stage_class ||= "Gitlab::CycleAnalytics::#{@stage.to_s.camelize}Config".constantize + end end end end diff --git a/lib/gitlab/cycle_analytics/issue_config.rb b/lib/gitlab/cycle_analytics/issue_config.rb new file mode 100644 index 00000000000..985ac76feb2 --- /dev/null +++ b/lib/gitlab/cycle_analytics/issue_config.rb @@ -0,0 +1,16 @@ +module Gitlab + module CycleAnalytics + class IssueConfig < BaseConfig + @start_time_attrs = issue_table[:created_at] + + @end_time_attrs = [issue_metrics_table[:first_associated_with_milestone_at], + issue_metrics_table[:first_added_to_board_at]] + + @projections = [issue_table[:title], + issue_table[:iid], + issue_table[:id], + issue_table[:created_at], + issue_table[:author_id]] + end + end +end diff --git a/lib/gitlab/cycle_analytics/plan_config.rb b/lib/gitlab/cycle_analytics/plan_config.rb new file mode 100644 index 00000000000..453ee26b4c9 --- /dev/null +++ b/lib/gitlab/cycle_analytics/plan_config.rb @@ -0,0 +1,17 @@ +module Gitlab + module CycleAnalytics + class PlanConfig < BaseConfig + @start_time_attrs = issue_metrics_table[:first_associated_with_milestone_at] + + @end_time_attrs = [issue_metrics_table[:first_added_to_board_at], + issue_metrics_table[:first_mentioned_in_commit_at]] + + @projections = [mr_diff_table[:st_commits].as('commits'), + issue_metrics_table[:first_mentioned_in_commit_at]] + + def self.query(base_query) + base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/production_config.rb b/lib/gitlab/cycle_analytics/production_config.rb new file mode 100644 index 00000000000..dc9f1d5d059 --- /dev/null +++ b/lib/gitlab/cycle_analytics/production_config.rb @@ -0,0 +1,15 @@ +module Gitlab + module CycleAnalytics + class ProductionConfig < BaseConfig + @start_time_attrs = issue_table[:created_at] + + @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] + + @projections = [issue_table[:title], + issue_table[:iid], + issue_table[:id], + issue_table[:created_at], + issue_table[:author_id]] + end + end +end diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb deleted file mode 100644 index 4fddbad95f2..00000000000 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ /dev/null @@ -1,99 +0,0 @@ -module Gitlab - module CycleAnalytics - class QueryConfig - include MetricsFetcher - - def self.get(*args) - new(*args).get - end - - def initialize(stage) - @stage = stage - end - - def get - public_send(@stage).freeze if self.respond_to?(@stage) - end - - def issue - { - start_time_attrs: issue_table[:created_at], - end_time_attrs: [issue_metrics_table[:first_associated_with_milestone_at], - issue_metrics_table[:first_added_to_board_at]], - projections: [issue_table[:title], - issue_table[:iid], - issue_table[:id], - issue_table[:created_at], - issue_table[:author_id]] - } - end - - def plan - { - start_time_attrs: issue_metrics_table[:first_associated_with_milestone_at], - end_time_attrs: [issue_metrics_table[:first_added_to_board_at], - issue_metrics_table[:first_mentioned_in_commit_at]], - projections: [mr_diff_table[:st_commits].as('commits'), - issue_metrics_table[:first_mentioned_in_commit_at]] - } - end - - def code - { - start_time_attrs: issue_metrics_table[:first_mentioned_in_commit_at], - end_time_attrs: mr_table[:created_at], - projections: [mr_table[:title], - mr_table[:iid], - mr_table[:id], - mr_table[:created_at], - mr_table[:state], - mr_table[:author_id]], - order: mr_table[:created_at] - } - end - - def test - { - start_time_attrs: mr_metrics_table[:latest_build_started_at], - end_time_attrs: mr_metrics_table[:latest_build_finished_at], - projections: [build_table[:id]], - order: build_table[:created_at] - } - end - - def review - { - start_time_attrs: mr_table[:created_at], - end_time_attrs: mr_metrics_table[:merged_at], - projections: [mr_table[:title], - mr_table[:iid], - mr_table[:id], - mr_table[:created_at], - mr_table[:state], - mr_table[:author_id]] - } - end - - def staging - { - start_time_attrs: mr_metrics_table[:merged_at], - end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], - projections: [build_table[:id]], - order: build_table[:created_at] - } - end - - def production - { - start_time_attrs: issue_table[:created_at], - end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], - projections: [issue_table[:title], - issue_table[:iid], - issue_table[:id], - issue_table[:created_at], - issue_table[:author_id]] - } - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/review_config.rb b/lib/gitlab/cycle_analytics/review_config.rb new file mode 100644 index 00000000000..e91dc1ea104 --- /dev/null +++ b/lib/gitlab/cycle_analytics/review_config.rb @@ -0,0 +1,16 @@ +module Gitlab + module CycleAnalytics + class ReviewConfig < BaseConfig + @start_time_attrs = mr_table[:created_at] + + @end_time_attrs = mr_metrics_table[:merged_at] + + @projections = [mr_table[:title], + mr_table[:iid], + mr_table[:id], + mr_table[:created_at], + mr_table[:state], + mr_table[:author_id]] + end + end +end diff --git a/lib/gitlab/cycle_analytics/staging_config.rb b/lib/gitlab/cycle_analytics/staging_config.rb new file mode 100644 index 00000000000..f6f471286a0 --- /dev/null +++ b/lib/gitlab/cycle_analytics/staging_config.rb @@ -0,0 +1,14 @@ +module Gitlab + module CycleAnalytics + class StagingConfig < BaseConfig + @start_time_attrs = mr_metrics_table[:merged_at] + @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] + @projections = [build_table[:id]] + @order = build_table[:created_at] + + def self.query(base_query) + base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/test_config.rb b/lib/gitlab/cycle_analytics/test_config.rb new file mode 100644 index 00000000000..6cb80f9a62c --- /dev/null +++ b/lib/gitlab/cycle_analytics/test_config.rb @@ -0,0 +1,14 @@ +module Gitlab + module CycleAnalytics + class TestConfig < BaseConfig + @start_time_attrs = mr_metrics_table[:latest_build_started_at] + @end_time_attrs = mr_metrics_table[:latest_build_finished_at] + @projections = [build_table[:id]] + @order = build_table[:created_at] + + def self.query(base_query) + base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) + end + end + end +end diff --git a/spec/lib/gitlab/cycle_analytics/code_config_spec.rb b/spec/lib/gitlab/cycle_analytics/code_config_spec.rb new file mode 100644 index 00000000000..7b89fdd76aa --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/code_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::CodeConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).not_to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index d46e70e9ba2..aa0e54582f1 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -9,6 +9,8 @@ describe Gitlab::CycleAnalytics::Events do subject { described_class.new(project: project, options: { from: from_date }) } before do + allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([context]) + setup(context) end @@ -317,6 +319,8 @@ describe Gitlab::CycleAnalytics::Events do def setup(context) milestone = create(:milestone, project: project) context.update(milestone: milestone) - create_merge_request_closing_issue(context) + mr = create_merge_request_closing_issue(context) + + ProcessCommitWorker.new.perform(project.id, user.id, mr.commits.last.sha) end end diff --git a/spec/lib/gitlab/cycle_analytics/issue_config_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_config_spec.rb new file mode 100644 index 00000000000..ff8cd1e8068 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/issue_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::IssueConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/plan_config_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_config_spec.rb new file mode 100644 index 00000000000..92698defa5d --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/plan_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::PlanConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/production_config_spec.rb b/spec/lib/gitlab/cycle_analytics/production_config_spec.rb new file mode 100644 index 00000000000..92485777334 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/production_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::ProductionConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/review_config_spec.rb b/spec/lib/gitlab/cycle_analytics/review_config_spec.rb new file mode 100644 index 00000000000..59cb18ac16d --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/review_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::ReviewConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/shared_config_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_config_spec.rb new file mode 100644 index 00000000000..060a59151d8 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/shared_config_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +shared_examples 'default query config' do + it 'has the start attributes' do + expect(described_class.start_time_attrs).not_to be_nil + end + + it 'has the end attributes' do + expect(described_class.end_time_attrs ).not_to be_nil + end + + it 'has the projection attributes' do + expect(described_class.projections).not_to be_nil + end +end diff --git a/spec/lib/gitlab/cycle_analytics/staging_config_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_config_spec.rb new file mode 100644 index 00000000000..f13885d7f48 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/staging_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::StagingConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).not_to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/test_config_spec.rb b/spec/lib/gitlab/cycle_analytics/test_config_spec.rb new file mode 100644 index 00000000000..59421b99513 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/test_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::TestConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).not_to eq(described_class.start_time_attrs) + end +end diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index 280aa2152b1..1c78cd368db 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -12,6 +12,8 @@ describe 'cycle analytics events' do deploy_master login_as(user) + + allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([context]) end it 'lists the issue events' do @@ -143,6 +145,6 @@ describe 'cycle analytics events' do merge_merge_requests_closing_issue(issue) - Issue::Metrics.update_all(first_mentioned_in_commit_at: mr.commits.last.committed_date) + ProcessCommitWorker.new.perform(project.id, user.id, mr.commits.last.sha) end end diff --git a/spec/serializers/analytics_generic_entity_spec.rb b/spec/serializers/analytics_generic_entity_spec.rb index 7c731da1dd1..3bb15cb9475 100644 --- a/spec/serializers/analytics_generic_entity_spec.rb +++ b/spec/serializers/analytics_generic_entity_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe AnalyticsGenericEntity do let(:user) { create(:user) } - let(:entity_hash) { + let(:entity_hash) do { total_time: "172802.724419", title: "Eos voluptatem inventore in sed.", @@ -11,7 +11,7 @@ describe AnalyticsGenericEntity do created_at: "2016-11-12 15:04:02.948604", author: user, } - } + end let(:project) { create(:empty_project) } let(:request) { EntityRequest.new(project: project, entity: :merge_request) } diff --git a/spec/serializers/analytics_generic_serializer_spec.rb b/spec/serializers/analytics_generic_serializer_spec.rb index 9eeb7bee562..99f2254d22c 100644 --- a/spec/serializers/analytics_generic_serializer_spec.rb +++ b/spec/serializers/analytics_generic_serializer_spec.rb @@ -10,7 +10,7 @@ describe AnalyticsGenericSerializer do let(:user) { create(:user) } let(:json) { serializer.as_json } let(:project) { create(:project) } - let(:resource) { + let(:resource) do { total_time: "172802.724419", title: "Eos voluptatem inventore in sed.", @@ -19,7 +19,7 @@ describe AnalyticsGenericSerializer do created_at: "2016-11-12 15:04:02.948604", author: user, } - } + end context 'when there is a single object provided' do it 'it generates payload for single object' do -- cgit v1.2.1 From 4844476e77f625829d99b7db2680186939ef660a Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 16 Nov 2016 12:10:12 +0100 Subject: use concurrent index in migration --- db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb b/db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb index 507d576d0bc..ed53756cd24 100644 --- a/db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb +++ b/db/migrate/20161020083353_add_pipeline_id_to_merge_request_metrics.rb @@ -25,7 +25,7 @@ class AddPipelineIdToMergeRequestMetrics < ActiveRecord::Migration def change add_column :merge_request_metrics, :pipeline_id, :integer - add_index :merge_request_metrics, :pipeline_id + add_concurrent_index :merge_request_metrics, :pipeline_id add_foreign_key :merge_request_metrics, :ci_commits, column: :pipeline_id, on_delete: :cascade end end -- cgit v1.2.1 From cbd7d000395ff60fe3726e67ec351bd4d44582ec Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 16 Nov 2016 15:55:20 +0100 Subject: added custom date helper and spec and fixed some unrelated spec failures --- app/serializers/analytics_build_entity.rb | 2 +- app/serializers/analytics_commit_entity.rb | 2 +- app/serializers/analytics_generic_entity.rb | 7 +++- app/serializers/entity_date_helper.rb | 28 ++++++++++++++ spec/lib/gitlab/cycle_analytics/events_spec.rb | 14 +++---- .../projects/cycle_analytics_events_spec.rb | 22 +++-------- spec/serializers/entity_date_helper_spec.rb | 45 ++++++++++++++++++++++ 7 files changed, 93 insertions(+), 27 deletions(-) create mode 100644 spec/serializers/entity_date_helper_spec.rb diff --git a/app/serializers/analytics_build_entity.rb b/app/serializers/analytics_build_entity.rb index eb1eef3424a..5fdf2bbf7c3 100644 --- a/app/serializers/analytics_build_entity.rb +++ b/app/serializers/analytics_build_entity.rb @@ -13,7 +13,7 @@ class AnalyticsBuildEntity < Grape::Entity end expose :duration, as: :total_time do |build| - distance_of_time_in_words(build[:duration].to_f) + distance_of_time_as_hash(build[:duration].to_f) end expose :branch do diff --git a/app/serializers/analytics_commit_entity.rb b/app/serializers/analytics_commit_entity.rb index a932d612e0f..2b363abbbd2 100644 --- a/app/serializers/analytics_commit_entity.rb +++ b/app/serializers/analytics_commit_entity.rb @@ -5,7 +5,7 @@ class AnalyticsCommitEntity < CommitEntity expose :short_id, as: :short_sha expose :total_time do |commit| - distance_of_time_in_words(request.total_time.to_f) + distance_of_time_as_hash(request.total_time.to_f) end unexpose :author_name diff --git a/app/serializers/analytics_generic_entity.rb b/app/serializers/analytics_generic_entity.rb index d7abe3f5f50..203cf39b940 100644 --- a/app/serializers/analytics_generic_entity.rb +++ b/app/serializers/analytics_generic_entity.rb @@ -3,12 +3,15 @@ class AnalyticsGenericEntity < Grape::Entity include EntityDateHelper expose :title - expose :iid expose :state, if: ->(_instance, options) { options[:request].entity == :merge_request } expose :author, using: UserEntity + expose :iid do |object| + object[:iid].to_s + end + expose :total_time do |object| - distance_of_time_in_words(object[:total_time].to_f) + distance_of_time_as_hash(object[:total_time].to_f) end expose(:created_at) do |object| diff --git a/app/serializers/entity_date_helper.rb b/app/serializers/entity_date_helper.rb index 57817ce1812..b333b3344c3 100644 --- a/app/serializers/entity_date_helper.rb +++ b/app/serializers/entity_date_helper.rb @@ -4,4 +4,32 @@ module EntityDateHelper def interval_in_words(diff) "#{distance_of_time_in_words(diff.to_f)} ago" end + + # Converts seconds into a hash such as: + # { days: 1, hours: 3, mins: 42, seconds: 40 } + # + # It returns 0 seconds for zero or negative numbers + # It rounds to nearest time unit and does not return zero + # i.e { min: 1 } instead of { mins: 1, seconds: 0 } + def distance_of_time_as_hash(diff) + diff = diff.abs.floor + + return { seconds: 0 } if diff == 0 + + mins = (diff / 60).floor + seconds = diff % 60 + hours = (mins / 60).floor + mins = mins % 60 + days = (hours / 24).floor + hours = hours % 24 + + duration_hash = {} + + duration_hash[:days] = days if days > 0 + duration_hash[:hours] = hours if hours > 0 + duration_hash[:mins] = mins if mins > 0 + duration_hash[:seconds] = seconds if seconds > 0 + + duration_hash + end end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index aa0e54582f1..8e2d2f8b5bd 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -16,7 +16,7 @@ describe Gitlab::CycleAnalytics::Events do describe '#issue_events' do it 'has the total time' do - expect(subject.issue_events.first[:total_time]).to eq('2 days') + expect(subject.issue_events.first[:total_time]).not_to be_empty end it 'has a title' do @@ -62,7 +62,7 @@ describe Gitlab::CycleAnalytics::Events do end it 'has the total time' do - expect(subject.plan_events.first[:total_time]).to eq('less than a minute') + expect(subject.plan_events.first[:total_time]).not_to be_empty end it "has the author's URL" do @@ -84,7 +84,7 @@ describe Gitlab::CycleAnalytics::Events do end it 'has the total time' do - expect(subject.code_events.first[:total_time]).to eq('less than a minute') + expect(subject.code_events.first[:total_time]).not_to be_empty end it 'has a title' do @@ -162,7 +162,7 @@ describe Gitlab::CycleAnalytics::Events do end it 'has the total time' do - expect(subject.test_events.first[:total_time]).not_to be_nil + expect(subject.test_events.first[:total_time]).not_to be_empty end end @@ -170,7 +170,7 @@ describe Gitlab::CycleAnalytics::Events do let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } it 'has the total time' do - expect(subject.review_events.first[:total_time]).to eq('less than a minute') + expect(subject.review_events.first[:total_time]).not_to be_empty end it 'has a title' do @@ -259,7 +259,7 @@ describe Gitlab::CycleAnalytics::Events do end it 'has the total time' do - expect(subject.staging_events.first[:total_time]).not_to be_nil + expect(subject.staging_events.first[:total_time]).not_to be_empty end it "has the author's URL" do @@ -284,7 +284,7 @@ describe Gitlab::CycleAnalytics::Events do end it 'has the total time' do - expect(subject.production_events.first[:total_time]).to eq('2 days') + expect(subject.production_events.first[:total_time]).not_to be_empty end it 'has a title' do diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index 1c78cd368db..705dbb7d1c0 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -3,17 +3,18 @@ require 'spec_helper' describe 'cycle analytics events' do let(:user) { create(:user) } let(:project) { create(:project) } + let(:issue) { create(:issue, project: project, created_at: 2.days.ago) } describe 'GET /:namespace/:project/cycle_analytics/events/issues' do before do project.team << [user, :developer] + allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue]) + 3.times { create_cycle } deploy_master login_as(user) - - allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([context]) end it 'lists the issue events' do @@ -31,17 +32,7 @@ describe 'cycle analytics events' do expect(json_response['events']).not_to be_empty - commits = [] - - MergeRequest.all.each do |mr| - mr.merge_request_diff.st_commits.each do |commit| - commits << { date: commit[:authored_date], sha: commit[:id] } - end - end - - newest_sha = commits.sort_by { |k| k['date'] }.first[:sha][0...8] - - expect(json_response['events'].first['short_sha']).to eq(newest_sha) + expect(json_response['events'].first['short_sha']).to eq(MergeRequest.last.commits.first.short_id) end it 'lists the code events' do @@ -49,7 +40,7 @@ describe 'cycle analytics events' do expect(json_response['events']).not_to be_empty - first_mr_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s + first_mr_iid = MergeRequest.order(created_at: :desc).pluck(:iid).first.to_s expect(json_response['events'].first['iid']).to eq(first_mr_iid) end @@ -67,7 +58,7 @@ describe 'cycle analytics events' do expect(json_response['events']).not_to be_empty - first_mr_iid = Issue.order(created_at: :desc).pluck(:iid).first.to_s + first_mr_iid = MergeRequest.order(created_at: :desc).pluck(:iid).first.to_s expect(json_response['events'].first['iid']).to eq(first_mr_iid) end @@ -132,7 +123,6 @@ describe 'cycle analytics events' do end def create_cycle - issue = create(:issue, project: project, created_at: 2.days.ago) milestone = create(:milestone, project: project) issue.update(milestone: milestone) mr = create_merge_request_closing_issue(issue) diff --git a/spec/serializers/entity_date_helper_spec.rb b/spec/serializers/entity_date_helper_spec.rb new file mode 100644 index 00000000000..90e57833de7 --- /dev/null +++ b/spec/serializers/entity_date_helper_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe EntityDateHelper do + let(:date_helper_class) { Class.new { include EntityDateHelper }.new } + + it 'converts 0 seconds' do + expect(date_helper_class.distance_of_time_as_hash(0)).to eq(seconds: 0) + end + + it 'converts 40 seconds' do + expect(date_helper_class.distance_of_time_as_hash(40)).to eq(seconds: 40) + end + + it 'converts 60 seconds' do + expect(date_helper_class.distance_of_time_as_hash(60)).to eq(mins: 1) + end + + it 'converts 70 seconds' do + expect(date_helper_class.distance_of_time_as_hash(70)).to eq(mins: 1, seconds: 10) + end + + it 'converts 3600 seconds' do + expect(date_helper_class.distance_of_time_as_hash(3600)).to eq(hours: 1) + end + + it 'converts 3750 seconds' do + expect(date_helper_class.distance_of_time_as_hash(3750)).to eq(hours: 1, mins: 2, seconds: 30) + end + + it 'converts 86400 seconds' do + expect(date_helper_class.distance_of_time_as_hash(86400)).to eq(days: 1) + end + + it 'converts 86560 seconds' do + expect(date_helper_class.distance_of_time_as_hash(86560)).to eq(days: 1, mins: 2, seconds: 40) + end + + it 'converts 86760 seconds' do + expect(date_helper_class.distance_of_time_as_hash(99760)).to eq(days: 1, hours: 3, mins: 42, seconds: 40) + end + + it 'converts 986760 seconds' do + expect(date_helper_class.distance_of_time_as_hash(986760)).to eq(days: 11, hours: 10, mins: 6) + end +end \ No newline at end of file -- cgit v1.2.1 From 9e0102e494d27bbb7fcd4ae8f2b0c0bc4ce3e7ce Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 16 Nov 2016 16:09:24 +0100 Subject: small refactor - removing includes no longer needed --- app/serializers/analytics_commit_entity.rb | 1 - lib/gitlab/cycle_analytics/events_fetcher.rb | 2 -- spec/serializers/entity_date_helper_spec.rb | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app/serializers/analytics_commit_entity.rb b/app/serializers/analytics_commit_entity.rb index 2b363abbbd2..402cecbfd08 100644 --- a/app/serializers/analytics_commit_entity.rb +++ b/app/serializers/analytics_commit_entity.rb @@ -1,5 +1,4 @@ class AnalyticsCommitEntity < CommitEntity - include RequestAwareEntity include EntityDateHelper expose :short_id, as: :short_sha diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 714afb88833..4c1d19774f5 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -1,8 +1,6 @@ module Gitlab module CycleAnalytics class EventsFetcher - include MetricsFetcher - def initialize(project:, options:) @query = EventsQuery.new(project: project, options: options) end diff --git a/spec/serializers/entity_date_helper_spec.rb b/spec/serializers/entity_date_helper_spec.rb index 90e57833de7..b9cc2f64831 100644 --- a/spec/serializers/entity_date_helper_spec.rb +++ b/spec/serializers/entity_date_helper_spec.rb @@ -42,4 +42,4 @@ describe EntityDateHelper do it 'converts 986760 seconds' do expect(date_helper_class.distance_of_time_as_hash(986760)).to eq(days: 11, hours: 10, mins: 6) end -end \ No newline at end of file +end -- cgit v1.2.1 From f9de157e70234748cf4285fafda7b3ec13862f5c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 16 Nov 2016 16:45:13 +0100 Subject: fix date issue with mySQL only --- lib/gitlab/cycle_analytics/events.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index f4d6d2049ef..8e6e6b2f4a3 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -58,7 +58,7 @@ module Gitlab YAML.load(commits).find do |commit| next unless commit[:committed_date] && event['first_mentioned_in_commit_at'] - commit[:committed_date].to_i == DateTime.parse(event['first_mentioned_in_commit_at']).to_i + commit[:committed_date].to_i == DateTime.parse(event['first_mentioned_in_commit_at'].to_s).to_i end end -- cgit v1.2.1 From ed39d61d746925e49d952bd8169499d3200ae68b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 17 Nov 2016 13:22:27 +0100 Subject: refactor events facade so it uses separate classes and refactor query stuff --- .../projects/cycle_analytics/events_controller.rb | 2 +- app/models/cycle_analytics.rb | 17 +- app/serializers/analytics_generic_entity.rb | 30 - app/serializers/analytics_generic_serializer.rb | 2 - app/serializers/analytics_issue_entity.rb | 29 + app/serializers/analytics_issue_serializer.rb | 3 + app/serializers/analytics_merge_request_entity.rb | 7 + .../analytics_merge_request_serializer.rb | 3 + db/schema.rb | 1198 +++++++++++--------- lib/gitlab/cycle_analytics/base_config.rb | 17 - lib/gitlab/cycle_analytics/base_event.rb | 27 + lib/gitlab/cycle_analytics/code_config.rb | 18 - lib/gitlab/cycle_analytics/code_event.rb | 25 + lib/gitlab/cycle_analytics/event_config.rb | 16 + lib/gitlab/cycle_analytics/events.rb | 54 +- lib/gitlab/cycle_analytics/events_fetcher.rb | 4 +- lib/gitlab/cycle_analytics/events_query.rb | 22 +- lib/gitlab/cycle_analytics/issue_config.rb | 16 - lib/gitlab/cycle_analytics/issue_event.rb | 23 + lib/gitlab/cycle_analytics/metrics_fetcher.rb | 42 +- lib/gitlab/cycle_analytics/metrics_tables.rb | 37 + lib/gitlab/cycle_analytics/plan_config.rb | 17 - lib/gitlab/cycle_analytics/plan_event.rb | 44 + lib/gitlab/cycle_analytics/production_config.rb | 15 - lib/gitlab/cycle_analytics/production_event.rb | 22 + lib/gitlab/cycle_analytics/review_config.rb | 16 - lib/gitlab/cycle_analytics/review_event.rb | 21 + lib/gitlab/cycle_analytics/staging_config.rb | 14 - lib/gitlab/cycle_analytics/staging_event.rb | 21 + lib/gitlab/cycle_analytics/test_config.rb | 14 - lib/gitlab/cycle_analytics/test_event.rb | 21 + .../lib/gitlab/cycle_analytics/code_config_spec.rb | 10 - spec/lib/gitlab/cycle_analytics/code_event_spec.rb | 10 + .../gitlab/cycle_analytics/issue_config_spec.rb | 10 - .../lib/gitlab/cycle_analytics/issue_event_spec.rb | 10 + .../lib/gitlab/cycle_analytics/plan_config_spec.rb | 10 - spec/lib/gitlab/cycle_analytics/plan_event_spec.rb | 10 + .../cycle_analytics/production_config_spec.rb | 10 - .../cycle_analytics/production_event_spec.rb | 10 + .../gitlab/cycle_analytics/review_config_spec.rb | 10 - .../gitlab/cycle_analytics/review_event_spec.rb | 10 + .../gitlab/cycle_analytics/shared_config_spec.rb | 15 - .../gitlab/cycle_analytics/shared_event_spec.rb | 19 + .../gitlab/cycle_analytics/staging_config_spec.rb | 10 - .../gitlab/cycle_analytics/staging_event_spec.rb | 10 + .../lib/gitlab/cycle_analytics/test_config_spec.rb | 10 - spec/lib/gitlab/cycle_analytics/test_event_spec.rb | 10 + spec/serializers/analytics_generic_entity_spec.rb | 2 +- 48 files changed, 1113 insertions(+), 860 deletions(-) delete mode 100644 app/serializers/analytics_generic_entity.rb create mode 100644 app/serializers/analytics_issue_entity.rb create mode 100644 app/serializers/analytics_issue_serializer.rb create mode 100644 app/serializers/analytics_merge_request_entity.rb create mode 100644 app/serializers/analytics_merge_request_serializer.rb delete mode 100644 lib/gitlab/cycle_analytics/base_config.rb create mode 100644 lib/gitlab/cycle_analytics/base_event.rb delete mode 100644 lib/gitlab/cycle_analytics/code_config.rb create mode 100644 lib/gitlab/cycle_analytics/code_event.rb create mode 100644 lib/gitlab/cycle_analytics/event_config.rb delete mode 100644 lib/gitlab/cycle_analytics/issue_config.rb create mode 100644 lib/gitlab/cycle_analytics/issue_event.rb create mode 100644 lib/gitlab/cycle_analytics/metrics_tables.rb delete mode 100644 lib/gitlab/cycle_analytics/plan_config.rb create mode 100644 lib/gitlab/cycle_analytics/plan_event.rb delete mode 100644 lib/gitlab/cycle_analytics/production_config.rb create mode 100644 lib/gitlab/cycle_analytics/production_event.rb delete mode 100644 lib/gitlab/cycle_analytics/review_config.rb create mode 100644 lib/gitlab/cycle_analytics/review_event.rb delete mode 100644 lib/gitlab/cycle_analytics/staging_config.rb create mode 100644 lib/gitlab/cycle_analytics/staging_event.rb delete mode 100644 lib/gitlab/cycle_analytics/test_config.rb create mode 100644 lib/gitlab/cycle_analytics/test_event.rb delete mode 100644 spec/lib/gitlab/cycle_analytics/code_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/code_event_spec.rb delete mode 100644 spec/lib/gitlab/cycle_analytics/issue_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/issue_event_spec.rb delete mode 100644 spec/lib/gitlab/cycle_analytics/plan_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/plan_event_spec.rb delete mode 100644 spec/lib/gitlab/cycle_analytics/production_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/production_event_spec.rb delete mode 100644 spec/lib/gitlab/cycle_analytics/review_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/review_event_spec.rb delete mode 100644 spec/lib/gitlab/cycle_analytics/shared_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/shared_event_spec.rb delete mode 100644 spec/lib/gitlab/cycle_analytics/staging_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/staging_event_spec.rb delete mode 100644 spec/lib/gitlab/cycle_analytics/test_config_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/test_event_spec.rb diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 2b4db0fd4aa..81b59b4804b 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -19,7 +19,7 @@ module Projects end def test - @options = { from: start_date(events_params), branch: events_params[:branch_name] } + @options[:branch] = events_params[:branch_name] render_events(events.test_events) end diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb index 30ca3273a2c..314a1ce9b63 100644 --- a/app/models/cycle_analytics.rb +++ b/app/models/cycle_analytics.rb @@ -1,9 +1,8 @@ class CycleAnalytics - include Gitlab::CycleAnalytics::MetricsFetcher - def initialize(project, from:) @project = project @from = from + @fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, from: from, branch: nil) end def summary @@ -11,45 +10,45 @@ class CycleAnalytics end def issue - calculate_metric(:issue, + @fetcher.calculate_metric(:issue, Issue.arel_table[:created_at], [Issue::Metrics.arel_table[:first_associated_with_milestone_at], Issue::Metrics.arel_table[:first_added_to_board_at]]) end def plan - calculate_metric(:plan, + @fetcher.calculate_metric(:plan, [Issue::Metrics.arel_table[:first_associated_with_milestone_at], Issue::Metrics.arel_table[:first_added_to_board_at]], Issue::Metrics.arel_table[:first_mentioned_in_commit_at]) end def code - calculate_metric(:code, + @fetcher.calculate_metric(:code, Issue::Metrics.arel_table[:first_mentioned_in_commit_at], MergeRequest.arel_table[:created_at]) end def test - calculate_metric(:test, + @fetcher.calculate_metric(:test, MergeRequest::Metrics.arel_table[:latest_build_started_at], MergeRequest::Metrics.arel_table[:latest_build_finished_at]) end def review - calculate_metric(:review, + @fetcher.calculate_metric(:review, MergeRequest.arel_table[:created_at], MergeRequest::Metrics.arel_table[:merged_at]) end def staging - calculate_metric(:staging, + @fetcher.calculate_metric(:staging, MergeRequest::Metrics.arel_table[:merged_at], MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) end def production - calculate_metric(:production, + @fetcher.calculate_metric(:production, Issue.arel_table[:created_at], MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) end diff --git a/app/serializers/analytics_generic_entity.rb b/app/serializers/analytics_generic_entity.rb deleted file mode 100644 index 203cf39b940..00000000000 --- a/app/serializers/analytics_generic_entity.rb +++ /dev/null @@ -1,30 +0,0 @@ -class AnalyticsGenericEntity < Grape::Entity - include RequestAwareEntity - include EntityDateHelper - - expose :title - expose :state, if: ->(_instance, options) { options[:request].entity == :merge_request } - expose :author, using: UserEntity - - expose :iid do |object| - object[:iid].to_s - end - - expose :total_time do |object| - distance_of_time_as_hash(object[:total_time].to_f) - end - - expose(:created_at) do |object| - interval_in_words(object[:created_at]) - end - - expose :url do |object| - url_to("namespace_project_#{request.entity}".to_sym, id: object[:iid].to_s) - end - - private - - def url_to(route, id) - public_send("#{route}_url", request.project.namespace, request.project, id) - end -end diff --git a/app/serializers/analytics_generic_serializer.rb b/app/serializers/analytics_generic_serializer.rb index 9000e25a91d..9f4859e8410 100644 --- a/app/serializers/analytics_generic_serializer.rb +++ b/app/serializers/analytics_generic_serializer.rb @@ -1,6 +1,4 @@ class AnalyticsGenericSerializer < BaseSerializer - entity AnalyticsGenericEntity - def represent(resource, opts = {}) resource.symbolize_keys! diff --git a/app/serializers/analytics_issue_entity.rb b/app/serializers/analytics_issue_entity.rb new file mode 100644 index 00000000000..44c50f18613 --- /dev/null +++ b/app/serializers/analytics_issue_entity.rb @@ -0,0 +1,29 @@ +class AnalyticsIssueEntity < Grape::Entity + include RequestAwareEntity + include EntityDateHelper + + expose :title + expose :author, using: UserEntity + + expose :iid do |object| + object[:iid].to_s + end + + expose :total_time do |object| + distance_of_time_as_hash(object[:total_time].to_f) + end + + expose(:created_at) do |object| + interval_in_words(object[:created_at]) + end + + expose :url do |object| + url_to(:namespace_project_issue, id: object[:iid].to_s) + end + + private + + def url_to(route, id) + public_send("#{route}_url", request.project.namespace, request.project, id) + end +end diff --git a/app/serializers/analytics_issue_serializer.rb b/app/serializers/analytics_issue_serializer.rb new file mode 100644 index 00000000000..4fb3e8f1bb4 --- /dev/null +++ b/app/serializers/analytics_issue_serializer.rb @@ -0,0 +1,3 @@ +class AnalyticsIssueSerializer < AnalyticsGenericSerializer + entity AnalyticsIssueEntity +end diff --git a/app/serializers/analytics_merge_request_entity.rb b/app/serializers/analytics_merge_request_entity.rb new file mode 100644 index 00000000000..888265eaa38 --- /dev/null +++ b/app/serializers/analytics_merge_request_entity.rb @@ -0,0 +1,7 @@ +class AnalyticsMergeRequestEntity < AnalyticsIssueEntity + expose :state + + expose :url do |object| + url_to(:namespace_project_merge_request, id: object[:iid].to_s) + end +end diff --git a/app/serializers/analytics_merge_request_serializer.rb b/app/serializers/analytics_merge_request_serializer.rb new file mode 100644 index 00000000000..4622a1dd855 --- /dev/null +++ b/app/serializers/analytics_merge_request_serializer.rb @@ -0,0 +1,3 @@ +class AnalyticsMergeRequestSerializer < AnalyticsGenericSerializer + entity AnalyticsMergeRequestEntity +end diff --git a/db/schema.rb b/db/schema.rb index 983e1c90682..633107f7db7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -13,107 +13,142 @@ ActiveRecord::Schema.define(version: 20161109150329) do - # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" - enable_extension "pg_trgm" - create_table "abuse_reports", force: :cascade do |t| - t.integer "reporter_id" - t.integer "user_id" - t.text "message" + t.integer "reporter_id", limit: 4 + t.integer "user_id", limit: 4 + t.text "message", limit: 65535 t.datetime "created_at" t.datetime "updated_at" - t.text "message_html" + t.text "message_html", limit: 65535 end create_table "appearances", force: :cascade do |t| - t.string "title" - t.text "description" - t.string "header_logo" - t.string "logo" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.text "description_html" + t.string "title", limit: 255 + t.text "description", limit: 65535 + t.string "logo", limit: 255 + t.integer "updated_by", limit: 4 + t.datetime "created_at" + t.datetime "updated_at" + t.string "header_logo", limit: 255 + t.text "description_html", limit: 65535 end create_table "application_settings", force: :cascade do |t| - t.integer "default_projects_limit" + t.integer "default_projects_limit", limit: 4 t.boolean "signup_enabled" t.boolean "signin_enabled" t.boolean "gravatar_enabled" - t.text "sign_in_text" + t.text "sign_in_text", limit: 65535 t.datetime "created_at" t.datetime "updated_at" - t.string "home_page_url" - t.integer "default_branch_protection", default: 2 - t.text "restricted_visibility_levels" + t.string "home_page_url", limit: 255 + t.integer "default_branch_protection", limit: 4, default: 2 + t.text "help_text", limit: 65535 + t.text "restricted_visibility_levels", limit: 65535 t.boolean "version_check_enabled", default: true - t.integer "max_attachment_size", default: 10, null: false - t.integer "default_project_visibility" - t.integer "default_snippet_visibility" - t.text "domain_whitelist" + t.integer "max_attachment_size", limit: 4, default: 10, null: false + t.integer "default_project_visibility", limit: 4 + t.integer "default_snippet_visibility", limit: 4 + t.text "domain_whitelist", limit: 65535 t.boolean "user_oauth_applications", default: true - t.string "after_sign_out_path" - t.integer "session_expire_delay", default: 10080, null: false - t.text "import_sources" - t.text "help_page_text" - t.string "admin_notification_email" + t.string "after_sign_out_path", limit: 255 + t.integer "session_expire_delay", limit: 4, default: 10080, null: false + t.text "import_sources", limit: 65535 + t.text "help_page_text", limit: 65535 + t.string "admin_notification_email", limit: 255 t.boolean "shared_runners_enabled", default: true, null: false - t.integer "max_artifacts_size", default: 100, null: false - t.string "runners_registration_token" + t.integer "max_artifacts_size", limit: 4, default: 100, null: false + t.string "runners_registration_token", limit: 255 + t.integer "max_pages_size", limit: 4, default: 100, null: false t.boolean "require_two_factor_authentication", default: false - t.integer "two_factor_grace_period", default: 48 + t.integer "two_factor_grace_period", limit: 4, default: 48 t.boolean "metrics_enabled", default: false - t.string "metrics_host", default: "localhost" - t.integer "metrics_pool_size", default: 16 - t.integer "metrics_timeout", default: 10 - t.integer "metrics_method_call_threshold", default: 10 + t.string "metrics_host", limit: 255, default: "localhost" + t.integer "metrics_pool_size", limit: 4, default: 16 + t.integer "metrics_timeout", limit: 4, default: 10 + t.integer "metrics_method_call_threshold", limit: 4, default: 10 t.boolean "recaptcha_enabled", default: false - t.string "recaptcha_site_key" - t.string "recaptcha_private_key" - t.integer "metrics_port", default: 8089 + t.string "recaptcha_site_key", limit: 255 + t.string "recaptcha_private_key", limit: 255 + t.integer "metrics_port", limit: 4, default: 8089 t.boolean "akismet_enabled", default: false - t.string "akismet_api_key" - t.integer "metrics_sample_interval", default: 15 + t.string "akismet_api_key", limit: 255 + t.integer "metrics_sample_interval", limit: 4, default: 15 t.boolean "sentry_enabled", default: false - t.string "sentry_dsn" + t.string "sentry_dsn", limit: 255 t.boolean "email_author_in_body", default: false - t.integer "default_group_visibility" + t.integer "default_group_visibility", limit: 4 t.boolean "repository_checks_enabled", default: false - t.text "shared_runners_text" - t.integer "metrics_packet_size", default: 1 - t.text "disabled_oauth_sign_in_sources" - t.string "health_check_access_token" + t.text "shared_runners_text", limit: 65535 + t.integer "metrics_packet_size", limit: 4, default: 1 + t.text "disabled_oauth_sign_in_sources", limit: 65535 + t.string "health_check_access_token", limit: 255 t.boolean "send_user_confirmation_email", default: false - t.integer "container_registry_token_expire_delay", default: 5 - t.text "after_sign_up_text" + t.integer "container_registry_token_expire_delay", limit: 4, default: 5 + t.text "after_sign_up_text", limit: 65535 t.boolean "user_default_external", default: false, null: false - t.string "repository_storages", default: "default" - t.string "enabled_git_access_protocol" + t.boolean "elasticsearch_indexing", default: false, null: false + t.boolean "elasticsearch_search", default: false, null: false + t.string "elasticsearch_host", limit: 255, default: "localhost" + t.string "elasticsearch_port", limit: 255, default: "9200" + t.string "repository_storages", limit: 255, default: "default" + t.string "enabled_git_access_protocol", limit: 255 t.boolean "domain_blacklist_enabled", default: false - t.text "domain_blacklist" + t.text "domain_blacklist", limit: 65535 + t.boolean "usage_ping_enabled", default: true, null: false t.boolean "koding_enabled" - t.string "koding_url" - t.text "sign_in_text_html" - t.text "help_page_text_html" - t.text "shared_runners_text_html" - t.text "after_sign_up_text_html" - t.boolean "sidekiq_throttling_enabled", default: false - t.string "sidekiq_throttling_queues" - t.decimal "sidekiq_throttling_factor" + t.string "koding_url", limit: 255 + t.integer "repository_size_limit", limit: 4, default: 0 + t.text "sign_in_text_html", limit: 65535 + t.text "help_page_text_html", limit: 65535 + t.text "shared_runners_text_html", limit: 65535 + t.text "after_sign_up_text_html", limit: 65535 + t.boolean "user_activity_enabled", default: true, null: false t.boolean "housekeeping_enabled", default: true, null: false t.boolean "housekeeping_bitmaps_enabled", default: true, null: false - t.integer "housekeeping_incremental_repack_period", default: 10, null: false - t.integer "housekeeping_full_repack_period", default: 50, null: false - t.integer "housekeeping_gc_period", default: 200, null: false + t.integer "housekeeping_incremental_repack_period", limit: 4, default: 10, null: false + t.integer "housekeeping_full_repack_period", limit: 4, default: 50, null: false + t.integer "housekeeping_gc_period", limit: 4, default: 200, null: false + t.boolean "sidekiq_throttling_enabled", default: false + t.string "sidekiq_throttling_queues", limit: 255 + t.decimal "sidekiq_throttling_factor", precision: 10 + end + + create_table "approvals", force: :cascade do |t| + t.integer "merge_request_id", limit: 4, null: false + t.integer "user_id", limit: 4, null: false + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "approver_groups", force: :cascade do |t| + t.integer "target_id", limit: 4, null: false + t.string "target_type", limit: 255, null: false + t.integer "group_id", limit: 4, null: false + t.datetime "created_at" + t.datetime "updated_at" end + add_index "approver_groups", ["group_id"], name: "index_approver_groups_on_group_id", using: :btree + add_index "approver_groups", ["target_id", "target_type"], name: "index_approver_groups_on_target_id_and_target_type", using: :btree + + create_table "approvers", force: :cascade do |t| + t.integer "target_id", limit: 4, null: false + t.string "target_type", limit: 255 + t.integer "user_id", limit: 4, null: false + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "approvers", ["target_id", "target_type"], name: "index_approvers_on_target_id_and_target_type", using: :btree + add_index "approvers", ["user_id"], name: "index_approvers_on_user_id", using: :btree + create_table "audit_events", force: :cascade do |t| - t.integer "author_id", null: false - t.string "type", null: false - t.integer "entity_id", null: false - t.string "entity_type", null: false - t.text "details" + t.integer "author_id", limit: 4, null: false + t.string "type", limit: 255, null: false + t.integer "entity_id", limit: 4, null: false + t.string "entity_type", limit: 255, null: false + t.text "details", limit: 65535 t.datetime "created_at" t.datetime "updated_at" end @@ -121,10 +156,10 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "audit_events", ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree create_table "award_emoji", force: :cascade do |t| - t.string "name" - t.integer "user_id" - t.integer "awardable_id" - t.string "awardable_type" + t.string "name", limit: 255 + t.integer "user_id", limit: 4 + t.integer "awardable_id", limit: 4 + t.string "awardable_type", limit: 255 t.datetime "created_at" t.datetime "updated_at" end @@ -134,22 +169,23 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "award_emoji", ["user_id"], name: "index_award_emoji_on_user_id", using: :btree create_table "boards", force: :cascade do |t| - t.integer "project_id", null: false + t.integer "project_id", limit: 4, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "name", limit: 255, default: "Development", null: false end add_index "boards", ["project_id"], name: "index_boards_on_project_id", using: :btree create_table "broadcast_messages", force: :cascade do |t| - t.text "message", null: false + t.text "message", limit: 65535, null: false t.datetime "starts_at" t.datetime "ends_at" t.datetime "created_at" t.datetime "updated_at" - t.string "color" - t.string "font" - t.text "message_html" + t.string "color", limit: 255 + t.string "font", limit: 255 + t.text "message_html", limit: 65535 end create_table "ci_application_settings", force: :cascade do |t| @@ -160,44 +196,44 @@ ActiveRecord::Schema.define(version: 20161109150329) do end create_table "ci_builds", force: :cascade do |t| - t.integer "project_id" - t.string "status" + t.integer "project_id", limit: 4 + t.string "status", limit: 255 t.datetime "finished_at" - t.text "trace" + t.text "trace", limit: 65535 t.datetime "created_at" t.datetime "updated_at" t.datetime "started_at" - t.integer "runner_id" - t.float "coverage" - t.integer "commit_id" - t.text "commands" - t.integer "job_id" - t.string "name" + t.integer "runner_id", limit: 4 + t.float "coverage", limit: 24 + t.integer "commit_id", limit: 4 + t.text "commands", limit: 65535 + t.integer "job_id", limit: 4 + t.string "name", limit: 255 t.boolean "deploy", default: false - t.text "options" + t.text "options", limit: 65535 t.boolean "allow_failure", default: false, null: false - t.string "stage" - t.integer "trigger_request_id" - t.integer "stage_idx" + t.string "stage", limit: 255 + t.integer "trigger_request_id", limit: 4 + t.integer "stage_idx", limit: 4 t.boolean "tag" - t.string "ref" - t.integer "user_id" - t.string "type" - t.string "target_url" - t.string "description" - t.text "artifacts_file" - t.integer "gl_project_id" - t.text "artifacts_metadata" - t.integer "erased_by_id" + t.string "ref", limit: 255 + t.integer "user_id", limit: 4 + t.string "type", limit: 255 + t.string "target_url", limit: 255 + t.string "description", limit: 255 + t.text "artifacts_file", limit: 65535 + t.integer "gl_project_id", limit: 4 + t.text "artifacts_metadata", limit: 65535 + t.integer "erased_by_id", limit: 4 t.datetime "erased_at" t.datetime "artifacts_expire_at" - t.string "environment" + t.string "environment", limit: 255 t.integer "artifacts_size", limit: 8 - t.string "when" - t.text "yaml_variables" + t.string "when", limit: 255 + t.text "yaml_variables", limit: 65535 t.datetime "queued_at" - t.string "token" - t.integer "lock_version" + t.string "token", limit: 255 + t.integer "lock_version", limit: 4 end add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree @@ -212,23 +248,23 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "ci_builds", ["token"], name: "index_ci_builds_on_token", unique: true, using: :btree create_table "ci_commits", force: :cascade do |t| - t.integer "project_id" - t.string "ref" - t.string "sha" - t.string "before_sha" - t.text "push_data" + t.integer "project_id", limit: 4 + t.string "ref", limit: 255 + t.string "sha", limit: 255 + t.string "before_sha", limit: 255 + t.text "push_data", limit: 65535 t.datetime "created_at" t.datetime "updated_at" t.boolean "tag", default: false - t.text "yaml_errors" + t.text "yaml_errors", limit: 65535 t.datetime "committed_at" - t.integer "gl_project_id" - t.string "status" + t.integer "gl_project_id", limit: 4 + t.string "status", limit: 255 t.datetime "started_at" t.datetime "finished_at" - t.integer "duration" - t.integer "user_id" - t.integer "lock_version" + t.integer "duration", limit: 4 + t.integer "user_id", limit: 4 + t.integer "lock_version", limit: 4 end add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree @@ -238,75 +274,75 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "ci_commits", ["user_id"], name: "index_ci_commits_on_user_id", using: :btree create_table "ci_events", force: :cascade do |t| - t.integer "project_id" - t.integer "user_id" - t.integer "is_admin" - t.text "description" + t.integer "project_id", limit: 4 + t.integer "user_id", limit: 4 + t.integer "is_admin", limit: 4 + t.text "description", limit: 65535 t.datetime "created_at" t.datetime "updated_at" end create_table "ci_jobs", force: :cascade do |t| - t.integer "project_id", null: false - t.text "commands" + t.integer "project_id", limit: 4, null: false + t.text "commands", limit: 65535 t.boolean "active", default: true, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "name" + t.string "name", limit: 255 t.boolean "build_branches", default: true, null: false t.boolean "build_tags", default: false, null: false - t.string "job_type", default: "parallel" - t.string "refs" + t.string "job_type", limit: 255, default: "parallel" + t.string "refs", limit: 255 t.datetime "deleted_at" end create_table "ci_projects", force: :cascade do |t| - t.string "name" - t.integer "timeout", default: 3600, null: false + t.string "name", limit: 255 + t.integer "timeout", limit: 4, default: 3600, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "token" - t.string "default_ref" - t.string "path" + t.string "token", limit: 255 + t.string "default_ref", limit: 255 + t.string "path", limit: 255 t.boolean "always_build", default: false, null: false - t.integer "polling_interval" + t.integer "polling_interval", limit: 4 t.boolean "public", default: false, null: false - t.string "ssh_url_to_repo" - t.integer "gitlab_id" + t.string "ssh_url_to_repo", limit: 255 + t.integer "gitlab_id", limit: 4 t.boolean "allow_git_fetch", default: true, null: false - t.string "email_recipients", default: "", null: false + t.string "email_recipients", limit: 255, default: "", null: false t.boolean "email_add_pusher", default: true, null: false t.boolean "email_only_broken_builds", default: true, null: false - t.string "skip_refs" - t.string "coverage_regex" + t.string "skip_refs", limit: 255 + t.string "coverage_regex", limit: 255 t.boolean "shared_runners_enabled", default: false - t.text "generated_yaml_config" + t.text "generated_yaml_config", limit: 65535 end create_table "ci_runner_projects", force: :cascade do |t| - t.integer "runner_id", null: false - t.integer "project_id" + t.integer "runner_id", limit: 4, null: false + t.integer "project_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.integer "gl_project_id" + t.integer "gl_project_id", limit: 4 end add_index "ci_runner_projects", ["gl_project_id"], name: "index_ci_runner_projects_on_gl_project_id", using: :btree add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree create_table "ci_runners", force: :cascade do |t| - t.string "token" + t.string "token", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.string "description" + t.string "description", limit: 255 t.datetime "contacted_at" t.boolean "active", default: true, null: false t.boolean "is_shared", default: false - t.string "name" - t.string "version" - t.string "revision" - t.string "platform" - t.string "architecture" + t.string "name", limit: 255 + t.string "version", limit: 255 + t.string "revision", limit: 255 + t.string "platform", limit: 255 + t.string "architecture", limit: 255 t.boolean "run_untagged", default: true, null: false t.boolean "locked", default: false, null: false end @@ -315,18 +351,18 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree create_table "ci_sessions", force: :cascade do |t| - t.string "session_id", null: false - t.text "data" + t.string "session_id", limit: 255, null: false + t.text "data", limit: 65535 t.datetime "created_at" t.datetime "updated_at" end create_table "ci_taggings", force: :cascade do |t| - t.integer "tag_id" - t.integer "taggable_id" - t.string "taggable_type" - t.integer "tagger_id" - t.string "tagger_type" + t.integer "tag_id", limit: 4 + t.integer "taggable_id", limit: 4 + t.string "taggable_type", limit: 255 + t.integer "tagger_id", limit: 4 + t.string "tagger_type", limit: 255 t.string "context", limit: 128 t.datetime "created_at" end @@ -334,44 +370,44 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "ci_tags", force: :cascade do |t| - t.string "name" - t.integer "taggings_count", default: 0 + t.string "name", limit: 255 + t.integer "taggings_count", limit: 4, default: 0 end create_table "ci_trigger_requests", force: :cascade do |t| - t.integer "trigger_id", null: false - t.text "variables" + t.integer "trigger_id", limit: 4, null: false + t.text "variables", limit: 65535 t.datetime "created_at" t.datetime "updated_at" - t.integer "commit_id" + t.integer "commit_id", limit: 4 end create_table "ci_triggers", force: :cascade do |t| - t.string "token" - t.integer "project_id" + t.string "token", limit: 255 + t.integer "project_id", limit: 4 t.datetime "deleted_at" t.datetime "created_at" t.datetime "updated_at" - t.integer "gl_project_id" + t.integer "gl_project_id", limit: 4 end add_index "ci_triggers", ["gl_project_id"], name: "index_ci_triggers_on_gl_project_id", using: :btree create_table "ci_variables", force: :cascade do |t| - t.integer "project_id" - t.string "key" - t.text "value" - t.text "encrypted_value" - t.string "encrypted_value_salt" - t.string "encrypted_value_iv" - t.integer "gl_project_id" + t.integer "project_id", limit: 4 + t.string "key", limit: 255 + t.text "value", limit: 65535 + t.text "encrypted_value", limit: 65535 + t.string "encrypted_value_salt", limit: 255 + t.string "encrypted_value_iv", limit: 255 + t.integer "gl_project_id", limit: 4 end add_index "ci_variables", ["gl_project_id"], name: "index_ci_variables_on_gl_project_id", using: :btree create_table "deploy_keys_projects", force: :cascade do |t| - t.integer "deploy_key_id", null: false - t.integer "project_id", null: false + t.integer "deploy_key_id", limit: 4, null: false + t.integer "project_id", limit: 4, null: false t.datetime "created_at" t.datetime "updated_at" end @@ -379,18 +415,18 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree create_table "deployments", force: :cascade do |t| - t.integer "iid", null: false - t.integer "project_id", null: false - t.integer "environment_id", null: false - t.string "ref", null: false + t.integer "iid", limit: 4, null: false + t.integer "project_id", limit: 4, null: false + t.integer "environment_id", limit: 4, null: false + t.string "ref", limit: 255, null: false t.boolean "tag", null: false - t.string "sha", null: false - t.integer "user_id" - t.integer "deployable_id" - t.string "deployable_type" + t.string "sha", limit: 255, null: false + t.integer "user_id", limit: 4 + t.integer "deployable_id", limit: 4 + t.string "deployable_type", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.string "on_stop" + t.string "on_stop", limit: 255 end add_index "deployments", ["project_id", "environment_id", "iid"], name: "index_deployments_on_project_id_and_environment_id_and_iid", using: :btree @@ -399,8 +435,8 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "deployments", ["project_id"], name: "index_deployments_on_project_id", using: :btree create_table "emails", force: :cascade do |t| - t.integer "user_id", null: false - t.string "email", null: false + t.integer "user_id", limit: 4, null: false + t.string "email", limit: 255, null: false t.datetime "created_at" t.datetime "updated_at" end @@ -409,27 +445,27 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree create_table "environments", force: :cascade do |t| - t.integer "project_id" - t.string "name", null: false + t.integer "project_id", limit: 4 + t.string "name", limit: 255, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "external_url" - t.string "environment_type" - t.string "state", default: "available", null: false + t.string "external_url", limit: 255 + t.string "environment_type", limit: 255 + t.string "state", limit: 255, default: "available", null: false end add_index "environments", ["project_id", "name"], name: "index_environments_on_project_id_and_name", using: :btree create_table "events", force: :cascade do |t| - t.string "target_type" - t.integer "target_id" - t.string "title" - t.text "data" - t.integer "project_id" + t.string "target_type", limit: 255 + t.integer "target_id", limit: 4 + t.string "title", limit: 255 + t.text "data", limit: 4294967295 + t.integer "project_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.integer "action" - t.integer "author_id" + t.integer "action", limit: 4 + t.integer "author_id", limit: 4 end add_index "events", ["action"], name: "index_events_on_action", using: :btree @@ -440,26 +476,59 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "events", ["target_type"], name: "index_events_on_target_type", using: :btree create_table "forked_project_links", force: :cascade do |t| - t.integer "forked_to_project_id", null: false - t.integer "forked_from_project_id", null: false + t.integer "forked_to_project_id", limit: 4, null: false + t.integer "forked_from_project_id", limit: 4, null: false t.datetime "created_at" t.datetime "updated_at" end add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree + create_table "geo_nodes", force: :cascade do |t| + t.string "schema", limit: 255 + t.string "host", limit: 255 + t.integer "port", limit: 4 + t.string "relative_url_root", limit: 255 + t.boolean "primary" + t.integer "geo_node_key_id", limit: 4 + t.integer "oauth_application_id", limit: 4 + t.integer "system_hook_id", limit: 4 + end + + add_index "geo_nodes", ["host"], name: "index_geo_nodes_on_host", using: :btree + add_index "geo_nodes", ["primary"], name: "index_geo_nodes_on_primary", using: :btree + + create_table "historical_data", force: :cascade do |t| + t.date "date", null: false + t.integer "active_user_count", limit: 4 + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "identities", force: :cascade do |t| - t.string "extern_uid" - t.string "provider" - t.integer "user_id" + t.string "extern_uid", limit: 255 + t.string "provider", limit: 255 + t.integer "user_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" + t.string "secondary_extern_uid", limit: 255 end add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree + create_table "index_statuses", force: :cascade do |t| + t.integer "project_id", limit: 4, null: false + t.datetime "indexed_at" + t.text "note", limit: 65535 + t.string "last_commit", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "index_statuses", ["project_id"], name: "index_index_statuses_on_project_id", unique: true, using: :btree + create_table "issue_metrics", force: :cascade do |t| - t.integer "issue_id", null: false + t.integer "issue_id", limit: 4, null: false t.datetime "first_mentioned_in_commit_at" t.datetime "first_associated_with_milestone_at" t.datetime "first_added_to_board_at" @@ -470,26 +539,27 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "issue_metrics", ["issue_id"], name: "index_issue_metrics", using: :btree create_table "issues", force: :cascade do |t| - t.string "title" - t.integer "assignee_id" - t.integer "author_id" - t.integer "project_id" + t.string "title", limit: 255 + t.integer "assignee_id", limit: 4 + t.integer "author_id", limit: 4 + t.integer "project_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.integer "position", default: 0 - t.string "branch_name" - t.text "description" - t.integer "milestone_id" - t.string "state" - t.integer "iid" - t.integer "updated_by_id" + t.integer "position", limit: 4, default: 0 + t.string "branch_name", limit: 255 + t.text "description", limit: 65535 + t.integer "milestone_id", limit: 4 + t.string "state", limit: 255 + t.integer "iid", limit: 4 + t.integer "updated_by_id", limit: 4 + t.integer "weight", limit: 4 t.boolean "confidential", default: false t.datetime "deleted_at" t.date "due_date" - t.integer "moved_to_id" - t.integer "lock_version" - t.text "title_html" - t.text "description_html" + t.integer "moved_to_id", limit: 4 + t.integer "lock_version", limit: 4 + t.text "title_html", limit: 65535 + t.text "description_html", limit: 65535 end add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree @@ -497,21 +567,19 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "issues", ["confidential"], name: "index_issues_on_confidential", using: :btree add_index "issues", ["created_at"], name: "index_issues_on_created_at", using: :btree add_index "issues", ["deleted_at"], name: "index_issues_on_deleted_at", using: :btree - add_index "issues", ["description"], name: "index_issues_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "issues", ["due_date"], name: "index_issues_on_due_date", using: :btree add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree add_index "issues", ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true, using: :btree add_index "issues", ["state"], name: "index_issues_on_state", using: :btree - add_index "issues", ["title"], name: "index_issues_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"} create_table "keys", force: :cascade do |t| - t.integer "user_id" + t.integer "user_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.text "key" - t.string "title" - t.string "type" - t.string "fingerprint" + t.text "key", limit: 65535 + t.string "title", limit: 255 + t.string "type", limit: 255 + t.string "fingerprint", limit: 255 t.boolean "public", default: false, null: false end @@ -519,9 +587,9 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree create_table "label_links", force: :cascade do |t| - t.integer "label_id" - t.integer "target_id" - t.string "target_type" + t.integer "label_id", limit: 4 + t.integer "target_id", limit: 4 + t.string "target_type", limit: 255 t.datetime "created_at" t.datetime "updated_at" end @@ -530,56 +598,72 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "label_links", ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree create_table "label_priorities", force: :cascade do |t| - t.integer "project_id", null: false - t.integer "label_id", null: false - t.integer "priority", null: false + t.integer "project_id", limit: 4, null: false + t.integer "label_id", limit: 4, null: false + t.integer "priority", limit: 4, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end + add_index "label_priorities", ["label_id"], name: "fk_rails_e161058b0f", using: :btree add_index "label_priorities", ["priority"], name: "index_label_priorities_on_priority", using: :btree add_index "label_priorities", ["project_id", "label_id"], name: "index_label_priorities_on_project_id_and_label_id", unique: true, using: :btree create_table "labels", force: :cascade do |t| - t.string "title" - t.string "color" - t.integer "project_id" + t.string "title", limit: 255 + t.string "color", limit: 255 + t.integer "project_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" t.boolean "template", default: false - t.string "description" - t.text "description_html" - t.string "type" - t.integer "group_id" + t.string "description", limit: 255 + t.text "description_html", limit: 65535 + t.string "type", limit: 255 + t.integer "group_id", limit: 4 end add_index "labels", ["group_id", "project_id", "title"], name: "index_labels_on_group_id_and_project_id_and_title", unique: true, using: :btree add_index "labels", ["group_id"], name: "index_labels_on_group_id", using: :btree + create_table "ldap_group_links", force: :cascade do |t| + t.string "cn", limit: 255, null: false + t.integer "group_access", limit: 4, null: false + t.integer "group_id", limit: 4, null: false + t.datetime "created_at" + t.datetime "updated_at" + t.string "provider", limit: 255 + end + create_table "lfs_objects", force: :cascade do |t| - t.string "oid", null: false + t.string "oid", limit: 255, null: false t.integer "size", limit: 8, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "file" + t.string "file", limit: 255 end add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree create_table "lfs_objects_projects", force: :cascade do |t| - t.integer "lfs_object_id", null: false - t.integer "project_id", null: false + t.integer "lfs_object_id", limit: 4, null: false + t.integer "project_id", limit: 4, null: false t.datetime "created_at" t.datetime "updated_at" end add_index "lfs_objects_projects", ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree + create_table "licenses", force: :cascade do |t| + t.text "data", limit: 65535, null: false + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "lists", force: :cascade do |t| - t.integer "board_id", null: false - t.integer "label_id" - t.integer "list_type", default: 1, null: false - t.integer "position" + t.integer "board_id", limit: 4, null: false + t.integer "label_id", limit: 4 + t.integer "list_type", limit: 4, default: 1, null: false + t.integer "position", limit: 4 t.datetime "created_at", null: false t.datetime "updated_at", null: false end @@ -589,20 +673,22 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "lists", ["label_id"], name: "index_lists_on_label_id", using: :btree create_table "members", force: :cascade do |t| - t.integer "access_level", null: false - t.integer "source_id", null: false - t.string "source_type", null: false - t.integer "user_id" - t.integer "notification_level", null: false - t.string "type" + t.integer "access_level", limit: 4, null: false + t.integer "source_id", limit: 4, null: false + t.string "source_type", limit: 255, null: false + t.integer "user_id", limit: 4 + t.integer "notification_level", limit: 4, null: false + t.string "type", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.integer "created_by_id" - t.string "invite_email" - t.string "invite_token" + t.integer "created_by_id", limit: 4 + t.string "invite_email", limit: 255 + t.string "invite_token", limit: 255 t.datetime "invite_accepted_at" t.datetime "requested_at" t.date "expires_at" + t.boolean "ldap", default: false, null: false + t.boolean "override", default: false, null: false end add_index "members", ["access_level"], name: "index_members_on_access_level", using: :btree @@ -612,29 +698,29 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree create_table "merge_request_diffs", force: :cascade do |t| - t.string "state" - t.text "st_commits" - t.text "st_diffs" - t.integer "merge_request_id", null: false + t.string "state", limit: 255 + t.text "st_commits", limit: 4294967295 + t.text "st_diffs", limit: 4294967295 + t.integer "merge_request_id", limit: 4, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "base_commit_sha" - t.string "real_size" - t.string "head_commit_sha" - t.string "start_commit_sha" + t.string "base_commit_sha", limit: 255 + t.string "real_size", limit: 255 + t.string "head_commit_sha", limit: 255 + t.string "start_commit_sha", limit: 255 end add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", using: :btree create_table "merge_request_metrics", force: :cascade do |t| - t.integer "merge_request_id", null: false + t.integer "merge_request_id", limit: 4, null: false t.datetime "latest_build_started_at" t.datetime "latest_build_finished_at" t.datetime "first_deployed_to_production_at" t.datetime "merged_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "pipeline_id" + t.integer "pipeline_id", limit: 4 end add_index "merge_request_metrics", ["first_deployed_to_production_at"], name: "index_merge_request_metrics_on_first_deployed_to_production_at", using: :btree @@ -642,51 +728,51 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "merge_request_metrics", ["pipeline_id"], name: "index_merge_request_metrics_on_pipeline_id", using: :btree create_table "merge_requests", force: :cascade do |t| - t.string "target_branch", null: false - t.string "source_branch", null: false - t.integer "source_project_id", null: false - t.integer "author_id" - t.integer "assignee_id" - t.string "title" + t.string "target_branch", limit: 255, null: false + t.string "source_branch", limit: 255, null: false + t.integer "source_project_id", limit: 4, null: false + t.integer "author_id", limit: 4 + t.integer "assignee_id", limit: 4 + t.string "title", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.integer "milestone_id" - t.string "state" - t.string "merge_status" - t.integer "target_project_id", null: false - t.integer "iid" - t.text "description" - t.integer "position", default: 0 + t.integer "milestone_id", limit: 4 + t.string "state", limit: 255 + t.string "merge_status", limit: 255 + t.integer "target_project_id", limit: 4, null: false + t.integer "iid", limit: 4 + t.text "description", limit: 65535 + t.integer "position", limit: 4, default: 0 t.datetime "locked_at" - t.integer "updated_by_id" - t.text "merge_error" - t.text "merge_params" + t.integer "updated_by_id", limit: 4 + t.text "merge_error", limit: 65535 + t.text "merge_params", limit: 65535 t.boolean "merge_when_build_succeeds", default: false, null: false - t.integer "merge_user_id" - t.string "merge_commit_sha" + t.integer "merge_user_id", limit: 4 + t.string "merge_commit_sha", limit: 255 t.datetime "deleted_at" - t.string "in_progress_merge_commit_sha" - t.integer "lock_version" - t.text "title_html" - t.text "description_html" + t.integer "approvals_before_merge", limit: 4 + t.string "rebase_commit_sha", limit: 255 + t.string "in_progress_merge_commit_sha", limit: 255 + t.integer "lock_version", limit: 4 + t.text "title_html", limit: 65535 + t.text "description_html", limit: 65535 end add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree add_index "merge_requests", ["author_id"], name: "index_merge_requests_on_author_id", using: :btree add_index "merge_requests", ["created_at"], name: "index_merge_requests_on_created_at", using: :btree add_index "merge_requests", ["deleted_at"], name: "index_merge_requests_on_deleted_at", using: :btree - add_index "merge_requests", ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree add_index "merge_requests", ["source_project_id"], name: "index_merge_requests_on_source_project_id", using: :btree add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree add_index "merge_requests", ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true, using: :btree add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree - add_index "merge_requests", ["title"], name: "index_merge_requests_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"} create_table "merge_requests_closing_issues", force: :cascade do |t| - t.integer "merge_request_id", null: false - t.integer "issue_id", null: false + t.integer "merge_request_id", limit: 4, null: false + t.integer "issue_id", limit: 4, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end @@ -695,73 +781,78 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "merge_requests_closing_issues", ["merge_request_id"], name: "index_merge_requests_closing_issues_on_merge_request_id", using: :btree create_table "milestones", force: :cascade do |t| - t.string "title", null: false - t.integer "project_id", null: false - t.text "description" + t.string "title", limit: 255, null: false + t.integer "project_id", limit: 4, null: false + t.text "description", limit: 65535 t.date "due_date" t.datetime "created_at" t.datetime "updated_at" - t.string "state" - t.integer "iid" - t.text "title_html" - t.text "description_html" + t.string "state", limit: 255 + t.integer "iid", limit: 4 + t.text "title_html", limit: 65535 + t.text "description_html", limit: 65535 end - add_index "milestones", ["description"], name: "index_milestones_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree add_index "milestones", ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree add_index "milestones", ["title"], name: "index_milestones_on_title", using: :btree - add_index "milestones", ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"} create_table "namespaces", force: :cascade do |t| - t.string "name", null: false - t.string "path", null: false - t.integer "owner_id" + t.string "name", limit: 255, null: false + t.string "path", limit: 255, null: false + t.integer "owner_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.string "type" - t.string "description", default: "", null: false - t.string "avatar" + t.string "type", limit: 255 + t.string "description", limit: 255, default: "", null: false + t.string "avatar", limit: 255 + t.boolean "membership_lock", default: false t.boolean "share_with_group_lock", default: false - t.integer "visibility_level", default: 20, null: false + t.integer "visibility_level", limit: 4, default: 20, null: false t.boolean "request_access_enabled", default: false, null: false t.datetime "deleted_at" + t.string "ldap_sync_status", limit: 255, default: "ready", null: false + t.string "ldap_sync_error", limit: 255 + t.datetime "ldap_sync_last_update_at" + t.datetime "ldap_sync_last_successful_update_at" + t.datetime "ldap_sync_last_sync_at" t.boolean "lfs_enabled" - t.text "description_html" + t.integer "repository_size_limit", limit: 4 + t.text "description_html", limit: 65535 end add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree add_index "namespaces", ["deleted_at"], name: "index_namespaces_on_deleted_at", using: :btree + add_index "namespaces", ["ldap_sync_last_successful_update_at"], name: "index_namespaces_on_ldap_sync_last_successful_update_at", using: :btree + add_index "namespaces", ["ldap_sync_last_update_at"], name: "index_namespaces_on_ldap_sync_last_update_at", using: :btree add_index "namespaces", ["name"], name: "index_namespaces_on_name", unique: true, using: :btree - add_index "namespaces", ["name"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree - add_index "namespaces", ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"} add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree create_table "notes", force: :cascade do |t| - t.text "note" - t.string "noteable_type" - t.integer "author_id" + t.text "note", limit: 65535 + t.string "noteable_type", limit: 255 + t.integer "author_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.integer "project_id" - t.string "attachment" - t.string "line_code" - t.string "commit_id" - t.integer "noteable_id" + t.integer "project_id", limit: 4 + t.string "attachment", limit: 255 + t.string "line_code", limit: 255 + t.string "commit_id", limit: 255 + t.integer "noteable_id", limit: 4 t.boolean "system", default: false, null: false - t.text "st_diff" - t.integer "updated_by_id" - t.string "type" - t.text "position" - t.text "original_position" + t.text "st_diff", limit: 4294967295 + t.integer "updated_by_id", limit: 4 + t.string "type", limit: 255 + t.text "position", limit: 65535 + t.text "original_position", limit: 65535 t.datetime "resolved_at" - t.integer "resolved_by_id" - t.string "discussion_id" - t.string "original_discussion_id" - t.text "note_html" + t.integer "resolved_by_id", limit: 4 + t.string "discussion_id", limit: 255 + t.string "original_discussion_id", limit: 255 + t.text "note_html", limit: 65535 end add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree @@ -769,7 +860,6 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree add_index "notes", ["discussion_id"], name: "index_notes_on_discussion_id", using: :btree add_index "notes", ["line_code"], name: "index_notes_on_line_code", using: :btree - add_index "notes", ["note"], name: "index_notes_on_note_trigram", using: :gin, opclasses: {"note"=>"gin_trgm_ops"} add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree @@ -777,13 +867,13 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree create_table "notification_settings", force: :cascade do |t| - t.integer "user_id", null: false - t.integer "source_id" - t.string "source_type" - t.integer "level", default: 0, null: false + t.integer "user_id", limit: 4, null: false + t.integer "source_id", limit: 4 + t.string "source_type", limit: 255 + t.integer "level", limit: 4, default: 0, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.text "events" + t.text "events", limit: 65535 end add_index "notification_settings", ["source_id", "source_type"], name: "index_notification_settings_on_source_id_and_source_type", using: :btree @@ -791,27 +881,27 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "notification_settings", ["user_id"], name: "index_notification_settings_on_user_id", using: :btree create_table "oauth_access_grants", force: :cascade do |t| - t.integer "resource_owner_id", null: false - t.integer "application_id", null: false - t.string "token", null: false - t.integer "expires_in", null: false - t.text "redirect_uri", null: false + t.integer "resource_owner_id", limit: 4, null: false + t.integer "application_id", limit: 4, null: false + t.string "token", limit: 255, null: false + t.integer "expires_in", limit: 4, null: false + t.text "redirect_uri", limit: 65535, null: false t.datetime "created_at", null: false t.datetime "revoked_at" - t.string "scopes" + t.string "scopes", limit: 255 end add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree create_table "oauth_access_tokens", force: :cascade do |t| - t.integer "resource_owner_id" - t.integer "application_id" - t.string "token", null: false - t.string "refresh_token" - t.integer "expires_in" + t.integer "resource_owner_id", limit: 4 + t.integer "application_id", limit: 4 + t.string "token", limit: 255, null: false + t.string "refresh_token", limit: 255 + t.integer "expires_in", limit: 4 t.datetime "revoked_at" t.datetime "created_at", null: false - t.string "scopes" + t.string "scopes", limit: 255 end add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree @@ -819,24 +909,47 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree create_table "oauth_applications", force: :cascade do |t| - t.string "name", null: false - t.string "uid", null: false - t.string "secret", null: false - t.text "redirect_uri", null: false - t.string "scopes", default: "", null: false + t.string "name", limit: 255, null: false + t.string "uid", limit: 255, null: false + t.string "secret", limit: 255, null: false + t.text "redirect_uri", limit: 65535, null: false + t.string "scopes", limit: 255, default: "", null: false t.datetime "created_at" t.datetime "updated_at" - t.integer "owner_id" - t.string "owner_type" + t.integer "owner_id", limit: 4 + t.string "owner_type", limit: 255 end add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree + create_table "pages_domains", force: :cascade do |t| + t.integer "project_id", limit: 4 + t.text "certificate", limit: 65535 + t.text "encrypted_key", limit: 65535 + t.string "encrypted_key_iv", limit: 255 + t.string "encrypted_key_salt", limit: 255 + t.string "domain", limit: 255 + end + + add_index "pages_domains", ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree + + create_table "path_locks", force: :cascade do |t| + t.string "path", limit: 255, null: false + t.integer "project_id", limit: 4 + t.integer "user_id", limit: 4 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "path_locks", ["path"], name: "index_path_locks_on_path", using: :btree + add_index "path_locks", ["project_id"], name: "index_path_locks_on_project_id", using: :btree + add_index "path_locks", ["user_id"], name: "index_path_locks_on_user_id", using: :btree + create_table "personal_access_tokens", force: :cascade do |t| - t.integer "user_id", null: false - t.string "token", null: false - t.string "name", null: false + t.integer "user_id", limit: 4, null: false + t.string "token", limit: 255, null: false + t.string "name", limit: 255, null: false t.boolean "revoked", default: false t.datetime "expires_at" t.datetime "created_at", null: false @@ -847,64 +960,75 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "personal_access_tokens", ["user_id"], name: "index_personal_access_tokens_on_user_id", using: :btree create_table "project_features", force: :cascade do |t| - t.integer "project_id" - t.integer "merge_requests_access_level" - t.integer "issues_access_level" - t.integer "wiki_access_level" - t.integer "snippets_access_level" - t.integer "builds_access_level" + t.integer "project_id", limit: 4 + t.integer "merge_requests_access_level", limit: 4 + t.integer "issues_access_level", limit: 4 + t.integer "wiki_access_level", limit: 4 + t.integer "snippets_access_level", limit: 4 + t.integer "builds_access_level", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.integer "repository_access_level", default: 20, null: false + t.integer "repository_access_level", limit: 4, default: 20, null: false end add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", using: :btree create_table "project_group_links", force: :cascade do |t| - t.integer "project_id", null: false - t.integer "group_id", null: false + t.integer "project_id", limit: 4, null: false + t.integer "group_id", limit: 4, null: false t.datetime "created_at" t.datetime "updated_at" - t.integer "group_access", default: 30, null: false + t.integer "group_access", limit: 4, default: 30, null: false t.date "expires_at" end create_table "project_import_data", force: :cascade do |t| - t.integer "project_id" - t.text "data" - t.text "encrypted_credentials" - t.string "encrypted_credentials_iv" - t.string "encrypted_credentials_salt" + t.integer "project_id", limit: 4 + t.text "data", limit: 65535 + t.text "encrypted_credentials", limit: 65535 + t.string "encrypted_credentials_iv", limit: 255 + t.string "encrypted_credentials_salt", limit: 255 end add_index "project_import_data", ["project_id"], name: "index_project_import_data_on_project_id", using: :btree create_table "projects", force: :cascade do |t| - t.string "name" - t.string "path" - t.text "description" + t.string "name", limit: 255 + t.string "path", limit: 255 + t.text "description", limit: 65535 t.datetime "created_at" t.datetime "updated_at" - t.integer "creator_id" - t.integer "namespace_id" + t.integer "creator_id", limit: 4 + t.integer "namespace_id", limit: 4 t.datetime "last_activity_at" - t.string "import_url" - t.integer "visibility_level", default: 0, null: false + t.string "import_url", limit: 255 + t.integer "visibility_level", limit: 4, default: 0, null: false t.boolean "archived", default: false, null: false - t.string "avatar" - t.string "import_status" - t.float "repository_size", default: 0.0 - t.integer "star_count", default: 0, null: false - t.string "import_type" - t.string "import_source" - t.integer "commit_count", default: 0 - t.text "import_error" - t.integer "ci_id" + t.string "avatar", limit: 255 + t.string "import_status", limit: 255 + t.float "repository_size", limit: 24, default: 0.0 + t.text "merge_requests_template", limit: 65535 + t.integer "star_count", limit: 4, default: 0, null: false + t.boolean "merge_requests_rebase_enabled", default: false + t.string "import_type", limit: 255 + t.string "import_source", limit: 255 + t.integer "approvals_before_merge", limit: 4, default: 0, null: false + t.boolean "reset_approvals_on_push", default: true + t.integer "commit_count", limit: 4, default: 0 + t.boolean "merge_requests_ff_only_enabled", default: false + t.text "issues_template", limit: 65535 + t.boolean "mirror", default: false, null: false + t.datetime "mirror_last_update_at" + t.datetime "mirror_last_successful_update_at" + t.integer "mirror_user_id", limit: 4 + t.text "import_error", limit: 65535 + t.integer "ci_id", limit: 4 t.boolean "shared_runners_enabled", default: true, null: false - t.string "runners_token" - t.string "build_coverage_regex" + t.string "runners_token", limit: 255 + t.string "build_coverage_regex", limit: 255 t.boolean "build_allow_git_fetch", default: true, null: false - t.integer "build_timeout", default: 3600, null: false + t.integer "build_timeout", limit: 4, default: 3600, null: false + t.boolean "mirror_trigger_builds", default: false, null: false t.boolean "pending_delete", default: false t.boolean "public_builds", default: true, null: false t.boolean "last_repository_check_failed" @@ -912,90 +1036,132 @@ ActiveRecord::Schema.define(version: 20161109150329) do t.boolean "container_registry_enabled" t.boolean "only_allow_merge_if_build_succeeds", default: false, null: false t.boolean "has_external_issue_tracker" - t.string "repository_storage", default: "default", null: false + t.string "repository_storage", limit: 255, default: "default", null: false t.boolean "request_access_enabled", default: false, null: false t.boolean "has_external_wiki" + t.boolean "repository_read_only" t.boolean "lfs_enabled" - t.text "description_html" - t.boolean "only_allow_merge_if_all_discussions_are_resolved" + t.integer "repository_size_limit", limit: 4 + t.text "description_html", limit: 65535 + t.boolean "only_allow_merge_if_all_discussions_are_resolved", default: false, null: false end add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree add_index "projects", ["created_at"], name: "index_projects_on_created_at", using: :btree add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree - add_index "projects", ["description"], name: "index_projects_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree add_index "projects", ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed", using: :btree - add_index "projects", ["name"], name: "index_projects_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree add_index "projects", ["path"], name: "index_projects_on_path", using: :btree - add_index "projects", ["path"], name: "index_projects_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"} add_index "projects", ["pending_delete"], name: "index_projects_on_pending_delete", using: :btree add_index "projects", ["runners_token"], name: "index_projects_on_runners_token", using: :btree add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree create_table "protected_branch_merge_access_levels", force: :cascade do |t| - t.integer "protected_branch_id", null: false - t.integer "access_level", default: 40, null: false + t.integer "protected_branch_id", limit: 4, null: false + t.integer "access_level", limit: 4, default: 40 t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "user_id", limit: 4 + t.integer "group_id", limit: 4 end + add_index "protected_branch_merge_access_levels", ["group_id"], name: "fk_rails_98f3d044fe", using: :btree add_index "protected_branch_merge_access_levels", ["protected_branch_id"], name: "index_protected_branch_merge_access", using: :btree + add_index "protected_branch_merge_access_levels", ["user_id"], name: "index_protected_branch_merge_access_levels_on_user_id", using: :btree create_table "protected_branch_push_access_levels", force: :cascade do |t| - t.integer "protected_branch_id", null: false - t.integer "access_level", default: 40, null: false + t.integer "protected_branch_id", limit: 4, null: false + t.integer "access_level", limit: 4, default: 40 t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "user_id", limit: 4 + t.integer "group_id", limit: 4 end + add_index "protected_branch_push_access_levels", ["group_id"], name: "fk_rails_7111b68cdb", using: :btree add_index "protected_branch_push_access_levels", ["protected_branch_id"], name: "index_protected_branch_push_access", using: :btree + add_index "protected_branch_push_access_levels", ["user_id"], name: "index_protected_branch_push_access_levels_on_user_id", using: :btree create_table "protected_branches", force: :cascade do |t| - t.integer "project_id", null: false - t.string "name", null: false + t.integer "project_id", limit: 4, null: false + t.string "name", limit: 255, null: false t.datetime "created_at" t.datetime "updated_at" end add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree + create_table "push_rules", force: :cascade do |t| + t.string "force_push_regex", limit: 255 + t.string "delete_branch_regex", limit: 255 + t.string "commit_message_regex", limit: 255 + t.boolean "deny_delete_tag" + t.integer "project_id", limit: 4 + t.datetime "created_at" + t.datetime "updated_at" + t.string "author_email_regex", limit: 255 + t.boolean "member_check", default: false, null: false + t.string "file_name_regex", limit: 255 + t.boolean "is_sample", default: false + t.integer "max_file_size", limit: 4, default: 0, null: false + t.boolean "prevent_secrets", default: false, null: false + end + + add_index "push_rules", ["project_id"], name: "index_push_rules_on_project_id", using: :btree + create_table "releases", force: :cascade do |t| - t.string "tag" - t.text "description" - t.integer "project_id" + t.string "tag", limit: 255 + t.text "description", limit: 65535 + t.integer "project_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.text "description_html" + t.text "description_html", limit: 65535 end add_index "releases", ["project_id", "tag"], name: "index_releases_on_project_id_and_tag", using: :btree add_index "releases", ["project_id"], name: "index_releases_on_project_id", using: :btree + create_table "remote_mirrors", force: :cascade do |t| + t.integer "project_id", limit: 4 + t.string "url", limit: 255 + t.boolean "enabled", default: false + t.string "update_status", limit: 255 + t.datetime "last_update_at" + t.datetime "last_successful_update_at" + t.string "last_error", limit: 255 + t.text "encrypted_credentials", limit: 65535 + t.string "encrypted_credentials_iv", limit: 255 + t.string "encrypted_credentials_salt", limit: 255 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "remote_mirrors", ["project_id"], name: "index_remote_mirrors_on_project_id", using: :btree + create_table "sent_notifications", force: :cascade do |t| - t.integer "project_id" - t.integer "noteable_id" - t.string "noteable_type" - t.integer "recipient_id" - t.string "commit_id" - t.string "reply_key", null: false - t.string "line_code" - t.string "note_type" - t.text "position" + t.integer "project_id", limit: 4 + t.integer "noteable_id", limit: 4 + t.string "noteable_type", limit: 255 + t.integer "recipient_id", limit: 4 + t.string "commit_id", limit: 255 + t.string "reply_key", limit: 255, null: false + t.string "line_code", limit: 255 + t.string "note_type", limit: 255 + t.text "position", limit: 65535 end add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree create_table "services", force: :cascade do |t| - t.string "type" - t.string "title" - t.integer "project_id" + t.string "type", limit: 255 + t.string "title", limit: 255 + t.integer "project_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" t.boolean "active", default: false, null: false - t.text "properties" + t.text "properties", limit: 65535 t.boolean "template", default: false t.boolean "push_events", default: true t.boolean "issues_events", default: true @@ -1003,7 +1169,7 @@ ActiveRecord::Schema.define(version: 20161109150329) do t.boolean "tag_push_events", default: true t.boolean "note_events", default: true, null: false t.boolean "build_events", default: false, null: false - t.string "category", default: "common", null: false + t.string "category", limit: 255, default: "common", null: false t.boolean "default", default: false t.boolean "wiki_page_events", default: true t.boolean "pipeline_events", default: false, null: false @@ -1014,43 +1180,41 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "services", ["template"], name: "index_services_on_template", using: :btree create_table "snippets", force: :cascade do |t| - t.string "title" - t.text "content" - t.integer "author_id", null: false - t.integer "project_id" + t.string "title", limit: 255 + t.text "content", limit: 4294967295 + t.integer "author_id", limit: 4, null: false + t.integer "project_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.string "file_name" - t.string "type" - t.integer "visibility_level", default: 0, null: false - t.text "title_html" - t.text "content_html" + t.string "file_name", limit: 255 + t.string "type", limit: 255 + t.integer "visibility_level", limit: 4, default: 0, null: false + t.text "title_html", limit: 65535 + t.text "content_html", limit: 65535 end add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree - add_index "snippets", ["file_name"], name: "index_snippets_on_file_name_trigram", using: :gin, opclasses: {"file_name"=>"gin_trgm_ops"} add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree - add_index "snippets", ["title"], name: "index_snippets_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"} add_index "snippets", ["updated_at"], name: "index_snippets_on_updated_at", using: :btree add_index "snippets", ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree create_table "spam_logs", force: :cascade do |t| - t.integer "user_id" - t.string "source_ip" - t.string "user_agent" + t.integer "user_id", limit: 4 + t.string "source_ip", limit: 255 + t.string "user_agent", limit: 255 t.boolean "via_api" - t.string "noteable_type" - t.string "title" - t.text "description" + t.string "noteable_type", limit: 255 + t.string "title", limit: 255 + t.text "description", limit: 65535 t.datetime "created_at", null: false t.datetime "updated_at", null: false t.boolean "submitted_as_ham", default: false, null: false end create_table "subscriptions", force: :cascade do |t| - t.integer "user_id" - t.integer "subscribable_id" - t.string "subscribable_type" + t.integer "user_id", limit: 4 + t.integer "subscribable_id", limit: 4 + t.string "subscribable_type", limit: 255 t.boolean "subscribed" t.datetime "created_at" t.datetime "updated_at" @@ -1059,12 +1223,12 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "subscriptions", ["subscribable_id", "subscribable_type", "user_id"], name: "subscriptions_user_id_and_ref_fields", unique: true, using: :btree create_table "taggings", force: :cascade do |t| - t.integer "tag_id" - t.integer "taggable_id" - t.string "taggable_type" - t.integer "tagger_id" - t.string "tagger_type" - t.string "context" + t.integer "tag_id", limit: 4 + t.integer "taggable_id", limit: 4 + t.string "taggable_type", limit: 255 + t.integer "tagger_id", limit: 4 + t.string "tagger_type", limit: 255 + t.string "context", limit: 255 t.datetime "created_at" end @@ -1072,24 +1236,24 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "tags", force: :cascade do |t| - t.string "name" - t.integer "taggings_count", default: 0 + t.string "name", limit: 255 + t.integer "taggings_count", limit: 4, default: 0 end add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree create_table "todos", force: :cascade do |t| - t.integer "user_id", null: false - t.integer "project_id", null: false - t.integer "target_id" - t.string "target_type", null: false - t.integer "author_id" - t.integer "action", null: false - t.string "state", null: false + t.integer "user_id", limit: 4, null: false + t.integer "project_id", limit: 4, null: false + t.integer "target_id", limit: 4 + t.string "target_type", limit: 255, null: false + t.integer "author_id", limit: 4 + t.integer "action", limit: 4, null: false + t.string "state", limit: 255, null: false t.datetime "created_at" t.datetime "updated_at" - t.integer "note_id" - t.string "commit_id" + t.integer "note_id", limit: 4 + t.string "commit_id", limit: 255 end add_index "todos", ["author_id"], name: "index_todos_on_author_id", using: :btree @@ -1100,95 +1264,104 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "todos", ["user_id"], name: "index_todos_on_user_id", using: :btree create_table "trending_projects", force: :cascade do |t| - t.integer "project_id", null: false + t.integer "project_id", limit: 4, null: false end add_index "trending_projects", ["project_id"], name: "index_trending_projects_on_project_id", using: :btree create_table "u2f_registrations", force: :cascade do |t| - t.text "certificate" - t.string "key_handle" - t.string "public_key" - t.integer "counter" - t.integer "user_id" + t.text "certificate", limit: 65535 + t.string "key_handle", limit: 255 + t.string "public_key", limit: 255 + t.integer "counter", limit: 4 + t.integer "user_id", limit: 4 t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "name" + t.string "name", limit: 255 end add_index "u2f_registrations", ["key_handle"], name: "index_u2f_registrations_on_key_handle", using: :btree add_index "u2f_registrations", ["user_id"], name: "index_u2f_registrations_on_user_id", using: :btree + create_table "user_activities", force: :cascade do |t| + t.integer "user_id", limit: 4 + t.datetime "last_activity_at", null: false + end + + add_index "user_activities", ["user_id"], name: "index_user_activities_on_user_id", unique: true, using: :btree + create_table "user_agent_details", force: :cascade do |t| - t.string "user_agent", null: false - t.string "ip_address", null: false - t.integer "subject_id", null: false - t.string "subject_type", null: false + t.string "user_agent", limit: 255, null: false + t.string "ip_address", limit: 255, null: false + t.integer "subject_id", limit: 4, null: false + t.string "subject_type", limit: 255, null: false t.boolean "submitted", default: false, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" + t.string "email", limit: 255, default: "", null: false + t.string "encrypted_password", limit: 255, default: "", null: false + t.string "reset_password_token", limit: 255 t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0 + t.integer "sign_in_count", limit: 4, default: 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" - t.string "current_sign_in_ip" - t.string "last_sign_in_ip" + t.string "current_sign_in_ip", limit: 255 + t.string "last_sign_in_ip", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.string "name" + t.string "name", limit: 255 t.boolean "admin", default: false, null: false - t.integer "projects_limit", default: 10 - t.string "skype", default: "", null: false - t.string "linkedin", default: "", null: false - t.string "twitter", default: "", null: false - t.string "authentication_token" - t.integer "theme_id", default: 1, null: false - t.string "bio" - t.integer "failed_attempts", default: 0 + t.integer "projects_limit", limit: 4, default: 10 + t.string "skype", limit: 255, default: "", null: false + t.string "linkedin", limit: 255, default: "", null: false + t.string "twitter", limit: 255, default: "", null: false + t.string "authentication_token", limit: 255 + t.integer "theme_id", limit: 4, default: 1, null: false + t.string "bio", limit: 255 + t.integer "failed_attempts", limit: 4, default: 0 t.datetime "locked_at" - t.string "username" + t.string "username", limit: 255 t.boolean "can_create_group", default: true, null: false t.boolean "can_create_team", default: true, null: false - t.string "state" - t.integer "color_scheme_id", default: 1, null: false + t.string "state", limit: 255 + t.integer "color_scheme_id", limit: 4, default: 1, null: false t.datetime "password_expires_at" - t.integer "created_by_id" + t.integer "created_by_id", limit: 4 t.datetime "last_credential_check_at" - t.string "avatar" - t.string "confirmation_token" + t.string "avatar", limit: 255 + t.string "confirmation_token", limit: 255 t.datetime "confirmed_at" t.datetime "confirmation_sent_at" - t.string "unconfirmed_email" + t.string "unconfirmed_email", limit: 255 t.boolean "hide_no_ssh_key", default: false - t.string "website_url", default: "", null: false - t.string "notification_email" + t.string "website_url", limit: 255, default: "", null: false + t.datetime "admin_email_unsubscribed_at" + t.string "notification_email", limit: 255 t.boolean "hide_no_password", default: false t.boolean "password_automatically_set", default: false - t.string "location" - t.string "encrypted_otp_secret" - t.string "encrypted_otp_secret_iv" - t.string "encrypted_otp_secret_salt" + t.string "location", limit: 255 + t.string "encrypted_otp_secret", limit: 255 + t.string "encrypted_otp_secret_iv", limit: 255 + t.string "encrypted_otp_secret_salt", limit: 255 t.boolean "otp_required_for_login", default: false, null: false - t.text "otp_backup_codes" - t.string "public_email", default: "", null: false - t.integer "dashboard", default: 0 - t.integer "project_view", default: 0 - t.integer "consumed_timestep" - t.integer "layout", default: 0 + t.text "otp_backup_codes", limit: 65535 + t.string "public_email", limit: 255, default: "", null: false + t.integer "dashboard", limit: 4, default: 0 + t.integer "project_view", limit: 4, default: 0 + t.integer "consumed_timestep", limit: 4 + t.integer "layout", limit: 4, default: 0 t.boolean "hide_project_limit", default: false - t.string "unlock_token" + t.text "note", limit: 65535 + t.string "unlock_token", limit: 255 t.datetime "otp_grace_period_started_at" t.boolean "ldap_email", default: false, null: false t.boolean "external", default: false - t.string "organization" - t.string "incoming_email_token" + t.string "organization", limit: 255 + t.string "incoming_email_token", limit: 255 end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree @@ -1197,18 +1370,15 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "users", ["created_at"], name: "index_users_on_created_at", using: :btree add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree - add_index "users", ["email"], name: "index_users_on_email_trigram", using: :gin, opclasses: {"email"=>"gin_trgm_ops"} add_index "users", ["incoming_email_token"], name: "index_users_on_incoming_email_token", using: :btree add_index "users", ["name"], name: "index_users_on_name", using: :btree - add_index "users", ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree add_index "users", ["state"], name: "index_users_on_state", using: :btree add_index "users", ["username"], name: "index_users_on_username", using: :btree - add_index "users", ["username"], name: "index_users_on_username_trigram", using: :gin, opclasses: {"username"=>"gin_trgm_ops"} create_table "users_star_projects", force: :cascade do |t| - t.integer "project_id", null: false - t.integer "user_id", null: false + t.integer "project_id", limit: 4, null: false + t.integer "user_id", limit: 4, null: false t.datetime "created_at" t.datetime "updated_at" end @@ -1219,26 +1389,28 @@ ActiveRecord::Schema.define(version: 20161109150329) do create_table "web_hooks", force: :cascade do |t| t.string "url", limit: 2000 - t.integer "project_id" + t.integer "project_id", limit: 4 t.datetime "created_at" t.datetime "updated_at" - t.string "type", default: "ProjectHook" - t.integer "service_id" + t.string "type", limit: 255, default: "ProjectHook" + t.integer "service_id", limit: 4 t.boolean "push_events", default: true, null: false t.boolean "issues_events", default: false, null: false t.boolean "merge_requests_events", default: false, null: false t.boolean "tag_push_events", default: false + t.integer "group_id", limit: 4 t.boolean "note_events", default: false, null: false t.boolean "enable_ssl_verification", default: true t.boolean "build_events", default: false, null: false + t.string "token", limit: 255 t.boolean "wiki_page_events", default: false, null: false - t.string "token" t.boolean "pipeline_events", default: false, null: false t.boolean "confidential_issues_events", default: false, null: false end add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree + add_foreign_key "approver_groups", "namespaces", column: "group_id", on_delete: :cascade add_foreign_key "boards", "projects" add_foreign_key "issue_metrics", "issues", on_delete: :cascade add_foreign_key "label_priorities", "labels", on_delete: :cascade @@ -1250,9 +1422,17 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_foreign_key "merge_request_metrics", "merge_requests", on_delete: :cascade add_foreign_key "merge_requests_closing_issues", "issues", on_delete: :cascade add_foreign_key "merge_requests_closing_issues", "merge_requests", on_delete: :cascade + add_foreign_key "path_locks", "projects" + add_foreign_key "path_locks", "users" add_foreign_key "personal_access_tokens", "users" + add_foreign_key "protected_branch_merge_access_levels", "namespaces", column: "group_id" add_foreign_key "protected_branch_merge_access_levels", "protected_branches" + add_foreign_key "protected_branch_merge_access_levels", "users" + add_foreign_key "protected_branch_push_access_levels", "namespaces", column: "group_id" add_foreign_key "protected_branch_push_access_levels", "protected_branches" + add_foreign_key "protected_branch_push_access_levels", "users" + add_foreign_key "remote_mirrors", "projects" add_foreign_key "trending_projects", "projects", on_delete: :cascade add_foreign_key "u2f_registrations", "users" + add_foreign_key "user_activities", "users", on_delete: :cascade end diff --git a/lib/gitlab/cycle_analytics/base_config.rb b/lib/gitlab/cycle_analytics/base_config.rb deleted file mode 100644 index 06ae9a15a7a..00000000000 --- a/lib/gitlab/cycle_analytics/base_config.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Gitlab - module CycleAnalytics - class BaseConfig - extend MetricsFetcher - - class << self - attr_reader :start_time_attrs, :end_time_attrs, :projections - end - - def self.order - @order || @start_time_attrs - end - - def self.query(base_query); end - end - end -end diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb new file mode 100644 index 00000000000..254e3621575 --- /dev/null +++ b/lib/gitlab/cycle_analytics/base_event.rb @@ -0,0 +1,27 @@ +module Gitlab + module CycleAnalytics + class BaseEvent + extend MetricsTables + + class << self + attr_reader :stage, :start_time_attrs, :end_time_attrs, :projections + + def order + @order || @start_time_attrs + end + + def query(base_query); end + + def fetch(query) + query.execute(self).map { |event| serialize(event, query) } + end + + private + + def serialize(event, query) + raise NotImplementedError.new("Expected #{self.name} to implement serialize(event)") + end + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/code_config.rb b/lib/gitlab/cycle_analytics/code_config.rb deleted file mode 100644 index c27419310db..00000000000 --- a/lib/gitlab/cycle_analytics/code_config.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Gitlab - module CycleAnalytics - class CodeConfig < BaseConfig - @start_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at] - - @end_time_attrs = mr_table[:created_at] - - @projections = [mr_table[:title], - mr_table[:iid], - mr_table[:id], - mr_table[:created_at], - mr_table[:state], - mr_table[:author_id]] - - @order = mr_table[:created_at] - end - end -end diff --git a/lib/gitlab/cycle_analytics/code_event.rb b/lib/gitlab/cycle_analytics/code_event.rb new file mode 100644 index 00000000000..609b45579b4 --- /dev/null +++ b/lib/gitlab/cycle_analytics/code_event.rb @@ -0,0 +1,25 @@ +module Gitlab + module CycleAnalytics + class CodeEvent < BaseEvent + @stage = :code + @start_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at] + + @end_time_attrs = mr_table[:created_at] + + @projections = [mr_table[:title], + mr_table[:iid], + mr_table[:id], + mr_table[:created_at], + mr_table[:state], + mr_table[:author_id]] + + @order = mr_table[:created_at] + + def self.serialize(event, query) + event['author'] = User.find(event.delete('author_id')) + + AnalyticsMergeRequestSerializer.new(project: query.project).represent(event).as_json + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/event_config.rb b/lib/gitlab/cycle_analytics/event_config.rb new file mode 100644 index 00000000000..2e1400c41b9 --- /dev/null +++ b/lib/gitlab/cycle_analytics/event_config.rb @@ -0,0 +1,16 @@ +module Gitlab + module CycleAnalytics + class TestEvent < BaseEvent + @start_time_attrs = mr_table[:created_at] + + @end_time_attrs = mr_metrics_table[:merged_at] + + @projections = [mr_table[:title], + mr_table[:iid], + mr_table[:id], + mr_table[:created_at], + mr_table[:state], + mr_table[:author_id]] + end + end +end diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 8e6e6b2f4a3..6580c73128c 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -3,73 +3,35 @@ module Gitlab class Events def initialize(project:, options:) @project = project - @fetcher = EventsFetcher.new(project: project, options: options) + @query = EventsQuery.new(project: project, options: options) end def issue_events - @fetcher.fetch(stage: :issue).map { |event| serialize_event(event) } + IssueEvent.fetch(@query) end def plan_events - @fetcher.fetch(stage: :plan).map do |event| - st_commit = first_time_reference_commit(event.delete('commits'), event) - - next unless st_commit - - serialize_commit(event, st_commit) - end + PlanEvent.fetch(@query) end def code_events - @fetcher.fetch(stage: :code).map { |event| serialize_event(event, entity: :merge_request) } + CodeEvent.fetch(@query) end def test_events - @fetcher.fetch(stage: :test).map { |event| serialize_build_event(event) } + TestEvent.fetch(@query) end def review_events - @fetcher.fetch(stage: :review).map { |event| serialize_event(event, entity: :merge_request) } + ReviewEvent.fetch(@query) end def staging_events - @fetcher.fetch(stage: :staging).map { |event| serialize_build_event(event) } + StagingEvent.fetch(@query) end def production_events - @fetcher.fetch(stage: :production).map { |event| serialize_event(event) } - end - - private - - def serialize_event(event, entity: :issue) - event['author'] = User.find(event.delete('author_id')) - - AnalyticsGenericSerializer.new(project: @project, entity: entity).represent(event).as_json - end - - def serialize_build_event(event) - build = ::Ci::Build.find(event['id']) - - AnalyticsBuildSerializer.new.represent(build).as_json - end - - def first_time_reference_commit(commits, event) - YAML.load(commits).find do |commit| - next unless commit[:committed_date] && event['first_mentioned_in_commit_at'] - - commit[:committed_date].to_i == DateTime.parse(event['first_mentioned_in_commit_at'].to_s).to_i - end - end - - def serialize_commit(event, st_commit) - commit = Commit.new(Gitlab::Git::Commit.new(st_commit), @project) - - AnalyticsCommitSerializer.new(project: @project, total_time: event['total_time']).represent(commit).as_json - end - - def interval_in_words(diff) - "#{distance_of_time_in_words(diff.to_f)} ago" + ProductionEvent.fetch(@query) end end end diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index 4c1d19774f5..813db46f058 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -6,9 +6,7 @@ module Gitlab end def fetch(stage:) - @query.execute(stage) do |stage_class, base_query| - stage_class.query(base_query) - end + @query.execute(stage) end end end diff --git a/lib/gitlab/cycle_analytics/events_query.rb b/lib/gitlab/cycle_analytics/events_query.rb index b61320fc561..7ecdc07d17f 100644 --- a/lib/gitlab/cycle_analytics/events_query.rb +++ b/lib/gitlab/cycle_analytics/events_query.rb @@ -1,30 +1,30 @@ module Gitlab module CycleAnalytics class EventsQuery - include MetricsFetcher + attr_reader :project def initialize(project:, options: {}) @project = project @from = options[:from] @branch = options[:branch] + @fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, from: @from, branch: @branch) end - def execute(stage, &block) - @stage = stage - query = build_query(&block) + def execute(stage_class) + @stage_class = stage_class ActiveRecord::Base.connection.exec_query(query.to_sql) end private - def build_query - base_query = base_query_for(@stage) - diff_fn = subtract_datetimes_diff(base_query, stage_class.start_time_attrs, stage_class.end_time_attrs) + def query + base_query = @fetcher.base_query_for(@stage_class.stage) + diff_fn = @fetcher.subtract_datetimes_diff(base_query, @stage_class.start_time_attrs, @stage_class.end_time_attrs) - yield(stage_class, base_query) if block_given? + @stage_class.query(base_query) - base_query.project(extract_epoch(diff_fn).as('total_time'), *stage_class.projections).order(stage_class.order.desc) + base_query.project(extract_epoch(diff_fn).as('total_time'), *@stage_class.projections).order(@stage_class.order.desc) end def extract_epoch(arel_attribute) @@ -32,10 +32,6 @@ module Gitlab Arel.sql(%Q{EXTRACT(EPOCH FROM (#{arel_attribute.to_sql}))}) end - - def stage_class - @stage_class ||= "Gitlab::CycleAnalytics::#{@stage.to_s.camelize}Config".constantize - end end end end diff --git a/lib/gitlab/cycle_analytics/issue_config.rb b/lib/gitlab/cycle_analytics/issue_config.rb deleted file mode 100644 index 985ac76feb2..00000000000 --- a/lib/gitlab/cycle_analytics/issue_config.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Gitlab - module CycleAnalytics - class IssueConfig < BaseConfig - @start_time_attrs = issue_table[:created_at] - - @end_time_attrs = [issue_metrics_table[:first_associated_with_milestone_at], - issue_metrics_table[:first_added_to_board_at]] - - @projections = [issue_table[:title], - issue_table[:iid], - issue_table[:id], - issue_table[:created_at], - issue_table[:author_id]] - end - end -end diff --git a/lib/gitlab/cycle_analytics/issue_event.rb b/lib/gitlab/cycle_analytics/issue_event.rb new file mode 100644 index 00000000000..9cfcdc9b20e --- /dev/null +++ b/lib/gitlab/cycle_analytics/issue_event.rb @@ -0,0 +1,23 @@ +module Gitlab + module CycleAnalytics + class IssueEvent < BaseEvent + @stage = :issue + @start_time_attrs = issue_table[:created_at] + + @end_time_attrs = [issue_metrics_table[:first_associated_with_milestone_at], + issue_metrics_table[:first_added_to_board_at]] + + @projections = [issue_table[:title], + issue_table[:iid], + issue_table[:id], + issue_table[:created_at], + issue_table[:author_id]] + + def self.serialize(event, query) + event['author'] = User.find(event.delete('author_id')) + + AnalyticsIssueSerializer.new(project: query.project).represent(event).as_json + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index 6743cbcc9e0..b71e8735e27 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -1,12 +1,18 @@ module Gitlab module CycleAnalytics - module MetricsFetcher + class MetricsFetcher include Gitlab::Database::Median include Gitlab::Database::DateTime + include MetricsTables DEPLOYMENT_METRIC_STAGES = %i[production staging] - private + def initialize(project:, from:, branch:) + @project = project + @project = project + @from = from + @branch = branch + end def calculate_metric(name, start_time_attrs, end_time_attrs) cte_table = Arel::Table.new("cte_table_for_#{name}") @@ -49,38 +55,6 @@ module Gitlab query end - - def mr_metrics_table - MergeRequest::Metrics.arel_table - end - - def mr_table - MergeRequest.arel_table - end - - def mr_diff_table - MergeRequestDiff.arel_table - end - - def mr_closing_issues_table - MergeRequestsClosingIssues.arel_table - end - - def issue_table - Issue.arel_table - end - - def issue_metrics_table - Issue::Metrics.arel_table - end - - def user_table - User.arel_table - end - - def build_table - ::CommitStatus.arel_table - end end end end diff --git a/lib/gitlab/cycle_analytics/metrics_tables.rb b/lib/gitlab/cycle_analytics/metrics_tables.rb new file mode 100644 index 00000000000..9d25ef078e8 --- /dev/null +++ b/lib/gitlab/cycle_analytics/metrics_tables.rb @@ -0,0 +1,37 @@ +module Gitlab + module CycleAnalytics + module MetricsTables + def mr_metrics_table + MergeRequest::Metrics.arel_table + end + + def mr_table + MergeRequest.arel_table + end + + def mr_diff_table + MergeRequestDiff.arel_table + end + + def mr_closing_issues_table + MergeRequestsClosingIssues.arel_table + end + + def issue_table + Issue.arel_table + end + + def issue_metrics_table + Issue::Metrics.arel_table + end + + def user_table + User.arel_table + end + + def build_table + ::CommitStatus.arel_table + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/plan_config.rb b/lib/gitlab/cycle_analytics/plan_config.rb deleted file mode 100644 index 453ee26b4c9..00000000000 --- a/lib/gitlab/cycle_analytics/plan_config.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Gitlab - module CycleAnalytics - class PlanConfig < BaseConfig - @start_time_attrs = issue_metrics_table[:first_associated_with_milestone_at] - - @end_time_attrs = [issue_metrics_table[:first_added_to_board_at], - issue_metrics_table[:first_mentioned_in_commit_at]] - - @projections = [mr_diff_table[:st_commits].as('commits'), - issue_metrics_table[:first_mentioned_in_commit_at]] - - def self.query(base_query) - base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/plan_event.rb b/lib/gitlab/cycle_analytics/plan_event.rb new file mode 100644 index 00000000000..b8107b06228 --- /dev/null +++ b/lib/gitlab/cycle_analytics/plan_event.rb @@ -0,0 +1,44 @@ +module Gitlab + module CycleAnalytics + class PlanEvent < BaseEvent + @stage = :plan + @start_time_attrs = issue_metrics_table[:first_associated_with_milestone_at] + + @end_time_attrs = [issue_metrics_table[:first_added_to_board_at], + issue_metrics_table[:first_mentioned_in_commit_at]] + + @projections = [mr_diff_table[:st_commits].as('commits'), + issue_metrics_table[:first_mentioned_in_commit_at]] + + class << self + def query(base_query) + base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) + end + + private + + def serialize(event, query) + st_commit = first_time_reference_commit(event.delete('commits'), event) + + return unless st_commit + + serialize_commit(event, st_commit, query) + end + + def first_time_reference_commit(commits, event) + YAML.load(commits).find do |commit| + next unless commit[:committed_date] && event['first_mentioned_in_commit_at'] + + commit[:committed_date].to_i == DateTime.parse(event['first_mentioned_in_commit_at'].to_s).to_i + end + end + + def serialize_commit(event, st_commit, query) + commit = Commit.new(Gitlab::Git::Commit.new(st_commit), @project) + + AnalyticsCommitSerializer.new(project: query.project, total_time: event['total_time']).represent(commit).as_json + end + end + end + end +end \ No newline at end of file diff --git a/lib/gitlab/cycle_analytics/production_config.rb b/lib/gitlab/cycle_analytics/production_config.rb deleted file mode 100644 index dc9f1d5d059..00000000000 --- a/lib/gitlab/cycle_analytics/production_config.rb +++ /dev/null @@ -1,15 +0,0 @@ -module Gitlab - module CycleAnalytics - class ProductionConfig < BaseConfig - @start_time_attrs = issue_table[:created_at] - - @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] - - @projections = [issue_table[:title], - issue_table[:iid], - issue_table[:id], - issue_table[:created_at], - issue_table[:author_id]] - end - end -end diff --git a/lib/gitlab/cycle_analytics/production_event.rb b/lib/gitlab/cycle_analytics/production_event.rb new file mode 100644 index 00000000000..b0b2d94f7e7 --- /dev/null +++ b/lib/gitlab/cycle_analytics/production_event.rb @@ -0,0 +1,22 @@ +module Gitlab + module CycleAnalytics + class ProductionEvent < BaseEvent + @stage = :production + @start_time_attrs = issue_table[:created_at] + + @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] + + @projections = [issue_table[:title], + issue_table[:iid], + issue_table[:id], + issue_table[:created_at], + issue_table[:author_id]] + + def self.serialize(event, query) + event['author'] = User.find(event.delete('author_id')) + + AnalyticsIssueSerializer.new(project: query.project).represent(event).as_json + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/review_config.rb b/lib/gitlab/cycle_analytics/review_config.rb deleted file mode 100644 index e91dc1ea104..00000000000 --- a/lib/gitlab/cycle_analytics/review_config.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Gitlab - module CycleAnalytics - class ReviewConfig < BaseConfig - @start_time_attrs = mr_table[:created_at] - - @end_time_attrs = mr_metrics_table[:merged_at] - - @projections = [mr_table[:title], - mr_table[:iid], - mr_table[:id], - mr_table[:created_at], - mr_table[:state], - mr_table[:author_id]] - end - end -end diff --git a/lib/gitlab/cycle_analytics/review_event.rb b/lib/gitlab/cycle_analytics/review_event.rb new file mode 100644 index 00000000000..dcd138eaa92 --- /dev/null +++ b/lib/gitlab/cycle_analytics/review_event.rb @@ -0,0 +1,21 @@ +module Gitlab + module CycleAnalytics + class ReviewEvent < BaseEvent + @stage = :review + @start_time_attrs = mr_table[:created_at] + @end_time_attrs = mr_metrics_table[:merged_at] + @projections = [mr_table[:title], + mr_table[:iid], + mr_table[:id], + mr_table[:created_at], + mr_table[:state], + mr_table[:author_id]] + + def self.serialize(event, _query) + event['author'] = User.find(event.delete('author_id')) + + AnalyticsMergeRequestSerializer.new(project: query.project).represent(event).as_json + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/staging_config.rb b/lib/gitlab/cycle_analytics/staging_config.rb deleted file mode 100644 index f6f471286a0..00000000000 --- a/lib/gitlab/cycle_analytics/staging_config.rb +++ /dev/null @@ -1,14 +0,0 @@ -module Gitlab - module CycleAnalytics - class StagingConfig < BaseConfig - @start_time_attrs = mr_metrics_table[:merged_at] - @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] - @projections = [build_table[:id]] - @order = build_table[:created_at] - - def self.query(base_query) - base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/staging_event.rb b/lib/gitlab/cycle_analytics/staging_event.rb new file mode 100644 index 00000000000..a8872b50cfd --- /dev/null +++ b/lib/gitlab/cycle_analytics/staging_event.rb @@ -0,0 +1,21 @@ +module Gitlab + module CycleAnalytics + class StagingEvent < BaseEvent + @stage = :staging + @start_time_attrs = mr_metrics_table[:merged_at] + @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] + @projections = [build_table[:id]] + @order = build_table[:created_at] + + def self.query(base_query) + base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) + end + + def self.serialize(event, _query) + build = ::Ci::Build.find(event['id']) + + AnalyticsBuildSerializer.new.represent(build).as_json + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/test_config.rb b/lib/gitlab/cycle_analytics/test_config.rb deleted file mode 100644 index 6cb80f9a62c..00000000000 --- a/lib/gitlab/cycle_analytics/test_config.rb +++ /dev/null @@ -1,14 +0,0 @@ -module Gitlab - module CycleAnalytics - class TestConfig < BaseConfig - @start_time_attrs = mr_metrics_table[:latest_build_started_at] - @end_time_attrs = mr_metrics_table[:latest_build_finished_at] - @projections = [build_table[:id]] - @order = build_table[:created_at] - - def self.query(base_query) - base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/test_event.rb b/lib/gitlab/cycle_analytics/test_event.rb new file mode 100644 index 00000000000..c91d3f47da0 --- /dev/null +++ b/lib/gitlab/cycle_analytics/test_event.rb @@ -0,0 +1,21 @@ +module Gitlab + module CycleAnalytics + class TestEvent < BaseEvent + @stage = :test + @start_time_attrs = mr_metrics_table[:latest_build_started_at] + @end_time_attrs = mr_metrics_table[:latest_build_finished_at] + @projections = [build_table[:id]] + @order = build_table[:created_at] + + def self.query(base_query) + base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) + end + + def self.serialize(event, _query) + build = ::Ci::Build.find(event['id']) + + AnalyticsBuildSerializer.new.represent(build).as_json + end + end + end +end diff --git a/spec/lib/gitlab/cycle_analytics/code_config_spec.rb b/spec/lib/gitlab/cycle_analytics/code_config_spec.rb deleted file mode 100644 index 7b89fdd76aa..00000000000 --- a/spec/lib/gitlab/cycle_analytics/code_config_spec.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'spec_helper' -require 'lib/gitlab/cycle_analytics/shared_config_spec' - -describe Gitlab::CycleAnalytics::CodeConfig do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(described_class.order).not_to eq(described_class.start_time_attrs) - end -end diff --git a/spec/lib/gitlab/cycle_analytics/code_event_spec.rb b/spec/lib/gitlab/cycle_analytics/code_event_spec.rb new file mode 100644 index 00000000000..7b854d7fea1 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/code_event_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_event_spec' + +describe Gitlab::CycleAnalytics::CodeEvent do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).not_to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/issue_config_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_config_spec.rb deleted file mode 100644 index ff8cd1e8068..00000000000 --- a/spec/lib/gitlab/cycle_analytics/issue_config_spec.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'spec_helper' -require 'lib/gitlab/cycle_analytics/shared_config_spec' - -describe Gitlab::CycleAnalytics::IssueConfig do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(described_class.order).to eq(described_class.start_time_attrs) - end -end diff --git a/spec/lib/gitlab/cycle_analytics/issue_event_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_event_spec.rb new file mode 100644 index 00000000000..2d7e7b1e0a5 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/issue_event_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_event_spec' + +describe Gitlab::CycleAnalytics::IssueEvent do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/plan_config_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_config_spec.rb deleted file mode 100644 index 92698defa5d..00000000000 --- a/spec/lib/gitlab/cycle_analytics/plan_config_spec.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'spec_helper' -require 'lib/gitlab/cycle_analytics/shared_config_spec' - -describe Gitlab::CycleAnalytics::PlanConfig do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(described_class.order).to eq(described_class.start_time_attrs) - end -end diff --git a/spec/lib/gitlab/cycle_analytics/plan_event_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_event_spec.rb new file mode 100644 index 00000000000..3452c146584 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/plan_event_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_event_spec' + +describe Gitlab::CycleAnalytics::PlanEvent do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/production_config_spec.rb b/spec/lib/gitlab/cycle_analytics/production_config_spec.rb deleted file mode 100644 index 92485777334..00000000000 --- a/spec/lib/gitlab/cycle_analytics/production_config_spec.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'spec_helper' -require 'lib/gitlab/cycle_analytics/shared_config_spec' - -describe Gitlab::CycleAnalytics::ProductionConfig do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(described_class.order).to eq(described_class.start_time_attrs) - end -end diff --git a/spec/lib/gitlab/cycle_analytics/production_event_spec.rb b/spec/lib/gitlab/cycle_analytics/production_event_spec.rb new file mode 100644 index 00000000000..069c84efa09 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/production_event_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_event_spec' + +describe Gitlab::CycleAnalytics::ProductionEvent do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/review_config_spec.rb b/spec/lib/gitlab/cycle_analytics/review_config_spec.rb deleted file mode 100644 index 59cb18ac16d..00000000000 --- a/spec/lib/gitlab/cycle_analytics/review_config_spec.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'spec_helper' -require 'lib/gitlab/cycle_analytics/shared_config_spec' - -describe Gitlab::CycleAnalytics::ReviewConfig do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(described_class.order).to eq(described_class.start_time_attrs) - end -end diff --git a/spec/lib/gitlab/cycle_analytics/review_event_spec.rb b/spec/lib/gitlab/cycle_analytics/review_event_spec.rb new file mode 100644 index 00000000000..e9aca82ecc9 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/review_event_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_event_spec' + +describe Gitlab::CycleAnalytics::ReviewEvent do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/shared_config_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_config_spec.rb deleted file mode 100644 index 060a59151d8..00000000000 --- a/spec/lib/gitlab/cycle_analytics/shared_config_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -shared_examples 'default query config' do - it 'has the start attributes' do - expect(described_class.start_time_attrs).not_to be_nil - end - - it 'has the end attributes' do - expect(described_class.end_time_attrs ).not_to be_nil - end - - it 'has the projection attributes' do - expect(described_class.projections).not_to be_nil - end -end diff --git a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb new file mode 100644 index 00000000000..b15af783dfb --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +shared_examples 'default query config' do + it 'has the start attributes' do + expect(described_class.start_time_attrs).not_to be_nil + end + + it 'has the end attributes' do + expect(described_class.end_time_attrs ).not_to be_nil + end + + it 'has the projection attributes' do + expect(described_class.projections).not_to be_nil + end + + it 'implements the fetch method' do + expect(described_class.fetch).not_to raise_error + end +end diff --git a/spec/lib/gitlab/cycle_analytics/staging_config_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_config_spec.rb deleted file mode 100644 index f13885d7f48..00000000000 --- a/spec/lib/gitlab/cycle_analytics/staging_config_spec.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'spec_helper' -require 'lib/gitlab/cycle_analytics/shared_config_spec' - -describe Gitlab::CycleAnalytics::StagingConfig do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(described_class.order).not_to eq(described_class.start_time_attrs) - end -end diff --git a/spec/lib/gitlab/cycle_analytics/staging_event_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_event_spec.rb new file mode 100644 index 00000000000..9c1bec3edb1 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/staging_event_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_event_spec' + +describe Gitlab::CycleAnalytics::StagingEvent do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).not_to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/test_config_spec.rb b/spec/lib/gitlab/cycle_analytics/test_config_spec.rb deleted file mode 100644 index 59421b99513..00000000000 --- a/spec/lib/gitlab/cycle_analytics/test_config_spec.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'spec_helper' -require 'lib/gitlab/cycle_analytics/shared_config_spec' - -describe Gitlab::CycleAnalytics::TestConfig do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(described_class.order).not_to eq(described_class.start_time_attrs) - end -end diff --git a/spec/lib/gitlab/cycle_analytics/test_event_spec.rb b/spec/lib/gitlab/cycle_analytics/test_event_spec.rb new file mode 100644 index 00000000000..7238b9a34b3 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/test_event_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_event_spec' + +describe Gitlab::CycleAnalytics::TestEvent do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).not_to eq(described_class.start_time_attrs) + end +end diff --git a/spec/serializers/analytics_generic_entity_spec.rb b/spec/serializers/analytics_generic_entity_spec.rb index 3bb15cb9475..68086216ba9 100644 --- a/spec/serializers/analytics_generic_entity_spec.rb +++ b/spec/serializers/analytics_generic_entity_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe AnalyticsGenericEntity do +describe AnalyticsIssueEntity do let(:user) { create(:user) } let(:entity_hash) do { -- cgit v1.2.1 From af80fcb477d13c240d8f58abab7d2b88a11ca7b5 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 17 Nov 2016 14:14:23 +0100 Subject: fix specs --- lib/gitlab/cycle_analytics/base_event.rb | 4 ++-- lib/gitlab/cycle_analytics/issue_event.rb | 2 +- lib/gitlab/cycle_analytics/plan_event.rb | 2 +- lib/gitlab/cycle_analytics/review_event.rb | 2 +- spec/lib/gitlab/cycle_analytics/shared_event_spec.rb | 10 +++++----- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb index 254e3621575..3c43fb24c61 100644 --- a/lib/gitlab/cycle_analytics/base_event.rb +++ b/lib/gitlab/cycle_analytics/base_event.rb @@ -10,7 +10,7 @@ module Gitlab @order || @start_time_attrs end - def query(base_query); end + def query(_base_query); end def fetch(query) query.execute(self).map { |event| serialize(event, query) } @@ -18,7 +18,7 @@ module Gitlab private - def serialize(event, query) + def serialize(_event, _query) raise NotImplementedError.new("Expected #{self.name} to implement serialize(event)") end end diff --git a/lib/gitlab/cycle_analytics/issue_event.rb b/lib/gitlab/cycle_analytics/issue_event.rb index 9cfcdc9b20e..9ae2c481158 100644 --- a/lib/gitlab/cycle_analytics/issue_event.rb +++ b/lib/gitlab/cycle_analytics/issue_event.rb @@ -13,7 +13,7 @@ module Gitlab issue_table[:created_at], issue_table[:author_id]] - def self.serialize(event, query) + def self.serialize(event, query)# event['author'] = User.find(event.delete('author_id')) AnalyticsIssueSerializer.new(project: query.project).represent(event).as_json diff --git a/lib/gitlab/cycle_analytics/plan_event.rb b/lib/gitlab/cycle_analytics/plan_event.rb index b8107b06228..4980a7835eb 100644 --- a/lib/gitlab/cycle_analytics/plan_event.rb +++ b/lib/gitlab/cycle_analytics/plan_event.rb @@ -41,4 +41,4 @@ module Gitlab end end end -end \ No newline at end of file +end diff --git a/lib/gitlab/cycle_analytics/review_event.rb b/lib/gitlab/cycle_analytics/review_event.rb index dcd138eaa92..82095b23b71 100644 --- a/lib/gitlab/cycle_analytics/review_event.rb +++ b/lib/gitlab/cycle_analytics/review_event.rb @@ -11,7 +11,7 @@ module Gitlab mr_table[:state], mr_table[:author_id]] - def self.serialize(event, _query) + def self.serialize(event, query) event['author'] = User.find(event.delete('author_id')) AnalyticsMergeRequestSerializer.new(project: query.project).represent(event).as_json diff --git a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb index b15af783dfb..1e76edce633 100644 --- a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb @@ -5,15 +5,15 @@ shared_examples 'default query config' do expect(described_class.start_time_attrs).not_to be_nil end + it 'has the stage attribute' do + expect(described_class.stage).not_to be_nil + end + it 'has the end attributes' do - expect(described_class.end_time_attrs ).not_to be_nil + expect(described_class.end_time_attrs).not_to be_nil end it 'has the projection attributes' do expect(described_class.projections).not_to be_nil end - - it 'implements the fetch method' do - expect(described_class.fetch).not_to raise_error - end end -- cgit v1.2.1 From 37a35050191b41cb5150d14292887d2fe88dad37 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 17 Nov 2016 15:00:01 +0100 Subject: fix integration spec --- app/controllers/projects/cycle_analytics/events_controller.rb | 2 +- lib/gitlab/cycle_analytics/issue_event.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 81b59b4804b..7b6214d71b5 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -19,7 +19,7 @@ module Projects end def test - @options[:branch] = events_params[:branch_name] + options[:branch] = events_params[:branch_name] render_events(events.test_events) end diff --git a/lib/gitlab/cycle_analytics/issue_event.rb b/lib/gitlab/cycle_analytics/issue_event.rb index 9ae2c481158..9cfcdc9b20e 100644 --- a/lib/gitlab/cycle_analytics/issue_event.rb +++ b/lib/gitlab/cycle_analytics/issue_event.rb @@ -13,7 +13,7 @@ module Gitlab issue_table[:created_at], issue_table[:author_id]] - def self.serialize(event, query)# + def self.serialize(event, query) event['author'] = User.find(event.delete('author_id')) AnalyticsIssueSerializer.new(project: query.project).represent(event).as_json -- cgit v1.2.1 From e22b5cca02d2885b36e6636ba3e6dcf78ada6f8b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 17 Nov 2016 15:00:56 +0100 Subject: remove class no longer required --- lib/gitlab/cycle_analytics/events_fetcher.rb | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 lib/gitlab/cycle_analytics/events_fetcher.rb diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb deleted file mode 100644 index 813db46f058..00000000000 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Gitlab - module CycleAnalytics - class EventsFetcher - def initialize(project:, options:) - @query = EventsQuery.new(project: project, options: options) - end - - def fetch(stage:) - @query.execute(stage) - end - end - end -end -- cgit v1.2.1 From 9c995725ea6eb8d97f4b7dfbd129e3530b9b1235 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 17 Nov 2016 15:05:13 +0100 Subject: fix typo in error message --- lib/gitlab/cycle_analytics/base_event.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb index 3c43fb24c61..c415b93c80f 100644 --- a/lib/gitlab/cycle_analytics/base_event.rb +++ b/lib/gitlab/cycle_analytics/base_event.rb @@ -19,7 +19,7 @@ module Gitlab private def serialize(_event, _query) - raise NotImplementedError.new("Expected #{self.name} to implement serialize(event)") + raise NotImplementedError.new("Expected #{self.name} to implement serialize(event, query)") end end end -- cgit v1.2.1 From d5b673da79c7c350090dc9982a9a2477fac56eb5 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 17 Nov 2016 18:00:37 +0100 Subject: more refactoring and added some auth checks --- .../projects/cycle_analytics/events_controller.rb | 2 +- db/schema.rb | 1194 +++++++++----------- lib/gitlab/cycle_analytics/base_event.rb | 36 +- lib/gitlab/cycle_analytics/code_event.rb | 33 +- lib/gitlab/cycle_analytics/event_config.rb | 16 - lib/gitlab/cycle_analytics/events.rb | 16 +- lib/gitlab/cycle_analytics/events_query.rb | 2 +- lib/gitlab/cycle_analytics/issue_event.rb | 30 +- lib/gitlab/cycle_analytics/plan_event.rb | 54 +- lib/gitlab/cycle_analytics/production_event.rb | 28 +- lib/gitlab/cycle_analytics/review_event.rb | 30 +- lib/gitlab/cycle_analytics/staging_event.rb | 20 +- lib/gitlab/cycle_analytics/test_event.rb | 20 +- spec/lib/gitlab/cycle_analytics/events_spec.rb | 2 +- 14 files changed, 670 insertions(+), 813 deletions(-) delete mode 100644 lib/gitlab/cycle_analytics/event_config.rb diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 7b6214d71b5..8c2fb98a120 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -50,7 +50,7 @@ module Projects end def options - @options ||= { from: start_date(events_params) } + @options ||= { from: start_date(events_params), current_user: current_user } end def events_params diff --git a/db/schema.rb b/db/schema.rb index 633107f7db7..22318612796 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -13,142 +13,107 @@ ActiveRecord::Schema.define(version: 20161109150329) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + enable_extension "pg_trgm" + create_table "abuse_reports", force: :cascade do |t| - t.integer "reporter_id", limit: 4 - t.integer "user_id", limit: 4 - t.text "message", limit: 65535 + t.integer "reporter_id" + t.integer "user_id" + t.text "message" t.datetime "created_at" t.datetime "updated_at" - t.text "message_html", limit: 65535 + t.text "message_html" end create_table "appearances", force: :cascade do |t| - t.string "title", limit: 255 - t.text "description", limit: 65535 - t.string "logo", limit: 255 - t.integer "updated_by", limit: 4 - t.datetime "created_at" - t.datetime "updated_at" - t.string "header_logo", limit: 255 - t.text "description_html", limit: 65535 + t.string "title" + t.text "description" + t.string "header_logo" + t.string "logo" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.text "description_html" end create_table "application_settings", force: :cascade do |t| - t.integer "default_projects_limit", limit: 4 + t.integer "default_projects_limit" t.boolean "signup_enabled" t.boolean "signin_enabled" t.boolean "gravatar_enabled" - t.text "sign_in_text", limit: 65535 + t.text "sign_in_text" t.datetime "created_at" t.datetime "updated_at" - t.string "home_page_url", limit: 255 - t.integer "default_branch_protection", limit: 4, default: 2 - t.text "help_text", limit: 65535 - t.text "restricted_visibility_levels", limit: 65535 + t.string "home_page_url" + t.integer "default_branch_protection", default: 2 + t.text "restricted_visibility_levels" t.boolean "version_check_enabled", default: true - t.integer "max_attachment_size", limit: 4, default: 10, null: false - t.integer "default_project_visibility", limit: 4 - t.integer "default_snippet_visibility", limit: 4 - t.text "domain_whitelist", limit: 65535 + t.integer "max_attachment_size", default: 10, null: false + t.integer "default_project_visibility" + t.integer "default_snippet_visibility" + t.text "domain_whitelist" t.boolean "user_oauth_applications", default: true - t.string "after_sign_out_path", limit: 255 - t.integer "session_expire_delay", limit: 4, default: 10080, null: false - t.text "import_sources", limit: 65535 - t.text "help_page_text", limit: 65535 - t.string "admin_notification_email", limit: 255 + t.string "after_sign_out_path" + t.integer "session_expire_delay", default: 10080, null: false + t.text "import_sources" + t.text "help_page_text" + t.string "admin_notification_email" t.boolean "shared_runners_enabled", default: true, null: false - t.integer "max_artifacts_size", limit: 4, default: 100, null: false - t.string "runners_registration_token", limit: 255 - t.integer "max_pages_size", limit: 4, default: 100, null: false + t.integer "max_artifacts_size", default: 100, null: false + t.string "runners_registration_token" t.boolean "require_two_factor_authentication", default: false - t.integer "two_factor_grace_period", limit: 4, default: 48 + t.integer "two_factor_grace_period", default: 48 t.boolean "metrics_enabled", default: false - t.string "metrics_host", limit: 255, default: "localhost" - t.integer "metrics_pool_size", limit: 4, default: 16 - t.integer "metrics_timeout", limit: 4, default: 10 - t.integer "metrics_method_call_threshold", limit: 4, default: 10 + t.string "metrics_host", default: "localhost" + t.integer "metrics_pool_size", default: 16 + t.integer "metrics_timeout", default: 10 + t.integer "metrics_method_call_threshold", default: 10 t.boolean "recaptcha_enabled", default: false - t.string "recaptcha_site_key", limit: 255 - t.string "recaptcha_private_key", limit: 255 - t.integer "metrics_port", limit: 4, default: 8089 + t.string "recaptcha_site_key" + t.string "recaptcha_private_key" + t.integer "metrics_port", default: 8089 t.boolean "akismet_enabled", default: false - t.string "akismet_api_key", limit: 255 - t.integer "metrics_sample_interval", limit: 4, default: 15 + t.string "akismet_api_key" + t.integer "metrics_sample_interval", default: 15 t.boolean "sentry_enabled", default: false - t.string "sentry_dsn", limit: 255 + t.string "sentry_dsn" t.boolean "email_author_in_body", default: false - t.integer "default_group_visibility", limit: 4 + t.integer "default_group_visibility" t.boolean "repository_checks_enabled", default: false - t.text "shared_runners_text", limit: 65535 - t.integer "metrics_packet_size", limit: 4, default: 1 - t.text "disabled_oauth_sign_in_sources", limit: 65535 - t.string "health_check_access_token", limit: 255 + t.text "shared_runners_text" + t.integer "metrics_packet_size", default: 1 + t.text "disabled_oauth_sign_in_sources" + t.string "health_check_access_token" t.boolean "send_user_confirmation_email", default: false - t.integer "container_registry_token_expire_delay", limit: 4, default: 5 - t.text "after_sign_up_text", limit: 65535 + t.integer "container_registry_token_expire_delay", default: 5 + t.text "after_sign_up_text" t.boolean "user_default_external", default: false, null: false - t.boolean "elasticsearch_indexing", default: false, null: false - t.boolean "elasticsearch_search", default: false, null: false - t.string "elasticsearch_host", limit: 255, default: "localhost" - t.string "elasticsearch_port", limit: 255, default: "9200" - t.string "repository_storages", limit: 255, default: "default" - t.string "enabled_git_access_protocol", limit: 255 + t.string "repository_storages", default: "default" + t.string "enabled_git_access_protocol" t.boolean "domain_blacklist_enabled", default: false - t.text "domain_blacklist", limit: 65535 - t.boolean "usage_ping_enabled", default: true, null: false + t.text "domain_blacklist" t.boolean "koding_enabled" - t.string "koding_url", limit: 255 - t.integer "repository_size_limit", limit: 4, default: 0 - t.text "sign_in_text_html", limit: 65535 - t.text "help_page_text_html", limit: 65535 - t.text "shared_runners_text_html", limit: 65535 - t.text "after_sign_up_text_html", limit: 65535 - t.boolean "user_activity_enabled", default: true, null: false + t.string "koding_url" + t.text "sign_in_text_html" + t.text "help_page_text_html" + t.text "shared_runners_text_html" + t.text "after_sign_up_text_html" t.boolean "housekeeping_enabled", default: true, null: false t.boolean "housekeeping_bitmaps_enabled", default: true, null: false - t.integer "housekeeping_incremental_repack_period", limit: 4, default: 10, null: false - t.integer "housekeeping_full_repack_period", limit: 4, default: 50, null: false - t.integer "housekeeping_gc_period", limit: 4, default: 200, null: false + t.integer "housekeeping_incremental_repack_period", default: 10, null: false + t.integer "housekeeping_full_repack_period", default: 50, null: false + t.integer "housekeeping_gc_period", default: 200, null: false t.boolean "sidekiq_throttling_enabled", default: false - t.string "sidekiq_throttling_queues", limit: 255 - t.decimal "sidekiq_throttling_factor", precision: 10 - end - - create_table "approvals", force: :cascade do |t| - t.integer "merge_request_id", limit: 4, null: false - t.integer "user_id", limit: 4, null: false - t.datetime "created_at" - t.datetime "updated_at" - end - - create_table "approver_groups", force: :cascade do |t| - t.integer "target_id", limit: 4, null: false - t.string "target_type", limit: 255, null: false - t.integer "group_id", limit: 4, null: false - t.datetime "created_at" - t.datetime "updated_at" - end - - add_index "approver_groups", ["group_id"], name: "index_approver_groups_on_group_id", using: :btree - add_index "approver_groups", ["target_id", "target_type"], name: "index_approver_groups_on_target_id_and_target_type", using: :btree - - create_table "approvers", force: :cascade do |t| - t.integer "target_id", limit: 4, null: false - t.string "target_type", limit: 255 - t.integer "user_id", limit: 4, null: false - t.datetime "created_at" - t.datetime "updated_at" + t.string "sidekiq_throttling_queues" + t.decimal "sidekiq_throttling_factor" end - add_index "approvers", ["target_id", "target_type"], name: "index_approvers_on_target_id_and_target_type", using: :btree - add_index "approvers", ["user_id"], name: "index_approvers_on_user_id", using: :btree - create_table "audit_events", force: :cascade do |t| - t.integer "author_id", limit: 4, null: false - t.string "type", limit: 255, null: false - t.integer "entity_id", limit: 4, null: false - t.string "entity_type", limit: 255, null: false - t.text "details", limit: 65535 + t.integer "author_id", null: false + t.string "type", null: false + t.integer "entity_id", null: false + t.string "entity_type", null: false + t.text "details" t.datetime "created_at" t.datetime "updated_at" end @@ -156,10 +121,10 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "audit_events", ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree create_table "award_emoji", force: :cascade do |t| - t.string "name", limit: 255 - t.integer "user_id", limit: 4 - t.integer "awardable_id", limit: 4 - t.string "awardable_type", limit: 255 + t.string "name" + t.integer "user_id" + t.integer "awardable_id" + t.string "awardable_type" t.datetime "created_at" t.datetime "updated_at" end @@ -169,23 +134,22 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "award_emoji", ["user_id"], name: "index_award_emoji_on_user_id", using: :btree create_table "boards", force: :cascade do |t| - t.integer "project_id", limit: 4, null: false + t.integer "project_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "name", limit: 255, default: "Development", null: false end add_index "boards", ["project_id"], name: "index_boards_on_project_id", using: :btree create_table "broadcast_messages", force: :cascade do |t| - t.text "message", limit: 65535, null: false + t.text "message", null: false t.datetime "starts_at" t.datetime "ends_at" t.datetime "created_at" t.datetime "updated_at" - t.string "color", limit: 255 - t.string "font", limit: 255 - t.text "message_html", limit: 65535 + t.string "color" + t.string "font" + t.text "message_html" end create_table "ci_application_settings", force: :cascade do |t| @@ -196,44 +160,44 @@ ActiveRecord::Schema.define(version: 20161109150329) do end create_table "ci_builds", force: :cascade do |t| - t.integer "project_id", limit: 4 - t.string "status", limit: 255 + t.integer "project_id" + t.string "status" t.datetime "finished_at" - t.text "trace", limit: 65535 + t.text "trace" t.datetime "created_at" t.datetime "updated_at" t.datetime "started_at" - t.integer "runner_id", limit: 4 - t.float "coverage", limit: 24 - t.integer "commit_id", limit: 4 - t.text "commands", limit: 65535 - t.integer "job_id", limit: 4 - t.string "name", limit: 255 + t.integer "runner_id" + t.float "coverage" + t.integer "commit_id" + t.text "commands" + t.integer "job_id" + t.string "name" t.boolean "deploy", default: false - t.text "options", limit: 65535 + t.text "options" t.boolean "allow_failure", default: false, null: false - t.string "stage", limit: 255 - t.integer "trigger_request_id", limit: 4 - t.integer "stage_idx", limit: 4 + t.string "stage" + t.integer "trigger_request_id" + t.integer "stage_idx" t.boolean "tag" - t.string "ref", limit: 255 - t.integer "user_id", limit: 4 - t.string "type", limit: 255 - t.string "target_url", limit: 255 - t.string "description", limit: 255 - t.text "artifacts_file", limit: 65535 - t.integer "gl_project_id", limit: 4 - t.text "artifacts_metadata", limit: 65535 - t.integer "erased_by_id", limit: 4 + t.string "ref" + t.integer "user_id" + t.string "type" + t.string "target_url" + t.string "description" + t.text "artifacts_file" + t.integer "gl_project_id" + t.text "artifacts_metadata" + t.integer "erased_by_id" t.datetime "erased_at" t.datetime "artifacts_expire_at" - t.string "environment", limit: 255 + t.string "environment" t.integer "artifacts_size", limit: 8 - t.string "when", limit: 255 - t.text "yaml_variables", limit: 65535 + t.string "when" + t.text "yaml_variables" t.datetime "queued_at" - t.string "token", limit: 255 - t.integer "lock_version", limit: 4 + t.string "token" + t.integer "lock_version" end add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree @@ -248,23 +212,23 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "ci_builds", ["token"], name: "index_ci_builds_on_token", unique: true, using: :btree create_table "ci_commits", force: :cascade do |t| - t.integer "project_id", limit: 4 - t.string "ref", limit: 255 - t.string "sha", limit: 255 - t.string "before_sha", limit: 255 - t.text "push_data", limit: 65535 + t.integer "project_id" + t.string "ref" + t.string "sha" + t.string "before_sha" + t.text "push_data" t.datetime "created_at" t.datetime "updated_at" t.boolean "tag", default: false - t.text "yaml_errors", limit: 65535 + t.text "yaml_errors" t.datetime "committed_at" - t.integer "gl_project_id", limit: 4 - t.string "status", limit: 255 + t.integer "gl_project_id" + t.string "status" t.datetime "started_at" t.datetime "finished_at" - t.integer "duration", limit: 4 - t.integer "user_id", limit: 4 - t.integer "lock_version", limit: 4 + t.integer "duration" + t.integer "user_id" + t.integer "lock_version" end add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree @@ -274,75 +238,75 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "ci_commits", ["user_id"], name: "index_ci_commits_on_user_id", using: :btree create_table "ci_events", force: :cascade do |t| - t.integer "project_id", limit: 4 - t.integer "user_id", limit: 4 - t.integer "is_admin", limit: 4 - t.text "description", limit: 65535 + t.integer "project_id" + t.integer "user_id" + t.integer "is_admin" + t.text "description" t.datetime "created_at" t.datetime "updated_at" end create_table "ci_jobs", force: :cascade do |t| - t.integer "project_id", limit: 4, null: false - t.text "commands", limit: 65535 + t.integer "project_id", null: false + t.text "commands" t.boolean "active", default: true, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "name", limit: 255 + t.string "name" t.boolean "build_branches", default: true, null: false t.boolean "build_tags", default: false, null: false - t.string "job_type", limit: 255, default: "parallel" - t.string "refs", limit: 255 + t.string "job_type", default: "parallel" + t.string "refs" t.datetime "deleted_at" end create_table "ci_projects", force: :cascade do |t| - t.string "name", limit: 255 - t.integer "timeout", limit: 4, default: 3600, null: false + t.string "name" + t.integer "timeout", default: 3600, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "token", limit: 255 - t.string "default_ref", limit: 255 - t.string "path", limit: 255 + t.string "token" + t.string "default_ref" + t.string "path" t.boolean "always_build", default: false, null: false - t.integer "polling_interval", limit: 4 + t.integer "polling_interval" t.boolean "public", default: false, null: false - t.string "ssh_url_to_repo", limit: 255 - t.integer "gitlab_id", limit: 4 + t.string "ssh_url_to_repo" + t.integer "gitlab_id" t.boolean "allow_git_fetch", default: true, null: false - t.string "email_recipients", limit: 255, default: "", null: false + t.string "email_recipients", default: "", null: false t.boolean "email_add_pusher", default: true, null: false t.boolean "email_only_broken_builds", default: true, null: false - t.string "skip_refs", limit: 255 - t.string "coverage_regex", limit: 255 + t.string "skip_refs" + t.string "coverage_regex" t.boolean "shared_runners_enabled", default: false - t.text "generated_yaml_config", limit: 65535 + t.text "generated_yaml_config" end create_table "ci_runner_projects", force: :cascade do |t| - t.integer "runner_id", limit: 4, null: false - t.integer "project_id", limit: 4 + t.integer "runner_id", null: false + t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.integer "gl_project_id", limit: 4 + t.integer "gl_project_id" end add_index "ci_runner_projects", ["gl_project_id"], name: "index_ci_runner_projects_on_gl_project_id", using: :btree add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree create_table "ci_runners", force: :cascade do |t| - t.string "token", limit: 255 + t.string "token" t.datetime "created_at" t.datetime "updated_at" - t.string "description", limit: 255 + t.string "description" t.datetime "contacted_at" t.boolean "active", default: true, null: false t.boolean "is_shared", default: false - t.string "name", limit: 255 - t.string "version", limit: 255 - t.string "revision", limit: 255 - t.string "platform", limit: 255 - t.string "architecture", limit: 255 + t.string "name" + t.string "version" + t.string "revision" + t.string "platform" + t.string "architecture" t.boolean "run_untagged", default: true, null: false t.boolean "locked", default: false, null: false end @@ -351,18 +315,18 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree create_table "ci_sessions", force: :cascade do |t| - t.string "session_id", limit: 255, null: false - t.text "data", limit: 65535 + t.string "session_id", null: false + t.text "data" t.datetime "created_at" t.datetime "updated_at" end create_table "ci_taggings", force: :cascade do |t| - t.integer "tag_id", limit: 4 - t.integer "taggable_id", limit: 4 - t.string "taggable_type", limit: 255 - t.integer "tagger_id", limit: 4 - t.string "tagger_type", limit: 255 + t.integer "tag_id" + t.integer "taggable_id" + t.string "taggable_type" + t.integer "tagger_id" + t.string "tagger_type" t.string "context", limit: 128 t.datetime "created_at" end @@ -370,44 +334,44 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "ci_tags", force: :cascade do |t| - t.string "name", limit: 255 - t.integer "taggings_count", limit: 4, default: 0 + t.string "name" + t.integer "taggings_count", default: 0 end create_table "ci_trigger_requests", force: :cascade do |t| - t.integer "trigger_id", limit: 4, null: false - t.text "variables", limit: 65535 + t.integer "trigger_id", null: false + t.text "variables" t.datetime "created_at" t.datetime "updated_at" - t.integer "commit_id", limit: 4 + t.integer "commit_id" end create_table "ci_triggers", force: :cascade do |t| - t.string "token", limit: 255 - t.integer "project_id", limit: 4 + t.string "token" + t.integer "project_id" t.datetime "deleted_at" t.datetime "created_at" t.datetime "updated_at" - t.integer "gl_project_id", limit: 4 + t.integer "gl_project_id" end add_index "ci_triggers", ["gl_project_id"], name: "index_ci_triggers_on_gl_project_id", using: :btree create_table "ci_variables", force: :cascade do |t| - t.integer "project_id", limit: 4 - t.string "key", limit: 255 - t.text "value", limit: 65535 - t.text "encrypted_value", limit: 65535 - t.string "encrypted_value_salt", limit: 255 - t.string "encrypted_value_iv", limit: 255 - t.integer "gl_project_id", limit: 4 + t.integer "project_id" + t.string "key" + t.text "value" + t.text "encrypted_value" + t.string "encrypted_value_salt" + t.string "encrypted_value_iv" + t.integer "gl_project_id" end add_index "ci_variables", ["gl_project_id"], name: "index_ci_variables_on_gl_project_id", using: :btree create_table "deploy_keys_projects", force: :cascade do |t| - t.integer "deploy_key_id", limit: 4, null: false - t.integer "project_id", limit: 4, null: false + t.integer "deploy_key_id", null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" end @@ -415,18 +379,18 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree create_table "deployments", force: :cascade do |t| - t.integer "iid", limit: 4, null: false - t.integer "project_id", limit: 4, null: false - t.integer "environment_id", limit: 4, null: false - t.string "ref", limit: 255, null: false + t.integer "iid", null: false + t.integer "project_id", null: false + t.integer "environment_id", null: false + t.string "ref", null: false t.boolean "tag", null: false - t.string "sha", limit: 255, null: false - t.integer "user_id", limit: 4 - t.integer "deployable_id", limit: 4 - t.string "deployable_type", limit: 255 + t.string "sha", null: false + t.integer "user_id" + t.integer "deployable_id" + t.string "deployable_type" t.datetime "created_at" t.datetime "updated_at" - t.string "on_stop", limit: 255 + t.string "on_stop" end add_index "deployments", ["project_id", "environment_id", "iid"], name: "index_deployments_on_project_id_and_environment_id_and_iid", using: :btree @@ -435,8 +399,8 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "deployments", ["project_id"], name: "index_deployments_on_project_id", using: :btree create_table "emails", force: :cascade do |t| - t.integer "user_id", limit: 4, null: false - t.string "email", limit: 255, null: false + t.integer "user_id", null: false + t.string "email", null: false t.datetime "created_at" t.datetime "updated_at" end @@ -445,27 +409,27 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree create_table "environments", force: :cascade do |t| - t.integer "project_id", limit: 4 - t.string "name", limit: 255, null: false + t.integer "project_id" + t.string "name", null: false t.datetime "created_at" t.datetime "updated_at" - t.string "external_url", limit: 255 - t.string "environment_type", limit: 255 - t.string "state", limit: 255, default: "available", null: false + t.string "external_url" + t.string "environment_type" + t.string "state", default: "available", null: false end add_index "environments", ["project_id", "name"], name: "index_environments_on_project_id_and_name", using: :btree create_table "events", force: :cascade do |t| - t.string "target_type", limit: 255 - t.integer "target_id", limit: 4 - t.string "title", limit: 255 - t.text "data", limit: 4294967295 - t.integer "project_id", limit: 4 + t.string "target_type" + t.integer "target_id" + t.string "title" + t.text "data" + t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.integer "action", limit: 4 - t.integer "author_id", limit: 4 + t.integer "action" + t.integer "author_id" end add_index "events", ["action"], name: "index_events_on_action", using: :btree @@ -476,59 +440,26 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "events", ["target_type"], name: "index_events_on_target_type", using: :btree create_table "forked_project_links", force: :cascade do |t| - t.integer "forked_to_project_id", limit: 4, null: false - t.integer "forked_from_project_id", limit: 4, null: false + t.integer "forked_to_project_id", null: false + t.integer "forked_from_project_id", null: false t.datetime "created_at" t.datetime "updated_at" end add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree - create_table "geo_nodes", force: :cascade do |t| - t.string "schema", limit: 255 - t.string "host", limit: 255 - t.integer "port", limit: 4 - t.string "relative_url_root", limit: 255 - t.boolean "primary" - t.integer "geo_node_key_id", limit: 4 - t.integer "oauth_application_id", limit: 4 - t.integer "system_hook_id", limit: 4 - end - - add_index "geo_nodes", ["host"], name: "index_geo_nodes_on_host", using: :btree - add_index "geo_nodes", ["primary"], name: "index_geo_nodes_on_primary", using: :btree - - create_table "historical_data", force: :cascade do |t| - t.date "date", null: false - t.integer "active_user_count", limit: 4 - t.datetime "created_at" - t.datetime "updated_at" - end - create_table "identities", force: :cascade do |t| - t.string "extern_uid", limit: 255 - t.string "provider", limit: 255 - t.integer "user_id", limit: 4 + t.string "extern_uid" + t.string "provider" + t.integer "user_id" t.datetime "created_at" t.datetime "updated_at" - t.string "secondary_extern_uid", limit: 255 end add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree - create_table "index_statuses", force: :cascade do |t| - t.integer "project_id", limit: 4, null: false - t.datetime "indexed_at" - t.text "note", limit: 65535 - t.string "last_commit", limit: 255 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - - add_index "index_statuses", ["project_id"], name: "index_index_statuses_on_project_id", unique: true, using: :btree - create_table "issue_metrics", force: :cascade do |t| - t.integer "issue_id", limit: 4, null: false + t.integer "issue_id", null: false t.datetime "first_mentioned_in_commit_at" t.datetime "first_associated_with_milestone_at" t.datetime "first_added_to_board_at" @@ -539,27 +470,26 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "issue_metrics", ["issue_id"], name: "index_issue_metrics", using: :btree create_table "issues", force: :cascade do |t| - t.string "title", limit: 255 - t.integer "assignee_id", limit: 4 - t.integer "author_id", limit: 4 - t.integer "project_id", limit: 4 + t.string "title" + t.integer "assignee_id" + t.integer "author_id" + t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.integer "position", limit: 4, default: 0 - t.string "branch_name", limit: 255 - t.text "description", limit: 65535 - t.integer "milestone_id", limit: 4 - t.string "state", limit: 255 - t.integer "iid", limit: 4 - t.integer "updated_by_id", limit: 4 - t.integer "weight", limit: 4 + t.integer "position", default: 0 + t.string "branch_name" + t.text "description" + t.integer "milestone_id" + t.string "state" + t.integer "iid" + t.integer "updated_by_id" t.boolean "confidential", default: false t.datetime "deleted_at" t.date "due_date" - t.integer "moved_to_id", limit: 4 - t.integer "lock_version", limit: 4 - t.text "title_html", limit: 65535 - t.text "description_html", limit: 65535 + t.integer "moved_to_id" + t.integer "lock_version" + t.text "title_html" + t.text "description_html" end add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree @@ -567,19 +497,21 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "issues", ["confidential"], name: "index_issues_on_confidential", using: :btree add_index "issues", ["created_at"], name: "index_issues_on_created_at", using: :btree add_index "issues", ["deleted_at"], name: "index_issues_on_deleted_at", using: :btree + add_index "issues", ["description"], name: "index_issues_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "issues", ["due_date"], name: "index_issues_on_due_date", using: :btree add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree add_index "issues", ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true, using: :btree add_index "issues", ["state"], name: "index_issues_on_state", using: :btree + add_index "issues", ["title"], name: "index_issues_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"} create_table "keys", force: :cascade do |t| - t.integer "user_id", limit: 4 + t.integer "user_id" t.datetime "created_at" t.datetime "updated_at" - t.text "key", limit: 65535 - t.string "title", limit: 255 - t.string "type", limit: 255 - t.string "fingerprint", limit: 255 + t.text "key" + t.string "title" + t.string "type" + t.string "fingerprint" t.boolean "public", default: false, null: false end @@ -587,9 +519,9 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree create_table "label_links", force: :cascade do |t| - t.integer "label_id", limit: 4 - t.integer "target_id", limit: 4 - t.string "target_type", limit: 255 + t.integer "label_id" + t.integer "target_id" + t.string "target_type" t.datetime "created_at" t.datetime "updated_at" end @@ -598,72 +530,56 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "label_links", ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree create_table "label_priorities", force: :cascade do |t| - t.integer "project_id", limit: 4, null: false - t.integer "label_id", limit: 4, null: false - t.integer "priority", limit: 4, null: false + t.integer "project_id", null: false + t.integer "label_id", null: false + t.integer "priority", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end - add_index "label_priorities", ["label_id"], name: "fk_rails_e161058b0f", using: :btree add_index "label_priorities", ["priority"], name: "index_label_priorities_on_priority", using: :btree add_index "label_priorities", ["project_id", "label_id"], name: "index_label_priorities_on_project_id_and_label_id", unique: true, using: :btree create_table "labels", force: :cascade do |t| - t.string "title", limit: 255 - t.string "color", limit: 255 - t.integer "project_id", limit: 4 + t.string "title" + t.string "color" + t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" t.boolean "template", default: false - t.string "description", limit: 255 - t.text "description_html", limit: 65535 - t.string "type", limit: 255 - t.integer "group_id", limit: 4 + t.string "description" + t.text "description_html" + t.string "type" + t.integer "group_id" end add_index "labels", ["group_id", "project_id", "title"], name: "index_labels_on_group_id_and_project_id_and_title", unique: true, using: :btree add_index "labels", ["group_id"], name: "index_labels_on_group_id", using: :btree - create_table "ldap_group_links", force: :cascade do |t| - t.string "cn", limit: 255, null: false - t.integer "group_access", limit: 4, null: false - t.integer "group_id", limit: 4, null: false - t.datetime "created_at" - t.datetime "updated_at" - t.string "provider", limit: 255 - end - create_table "lfs_objects", force: :cascade do |t| - t.string "oid", limit: 255, null: false + t.string "oid", null: false t.integer "size", limit: 8, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "file", limit: 255 + t.string "file" end add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree create_table "lfs_objects_projects", force: :cascade do |t| - t.integer "lfs_object_id", limit: 4, null: false - t.integer "project_id", limit: 4, null: false + t.integer "lfs_object_id", null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" end add_index "lfs_objects_projects", ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree - create_table "licenses", force: :cascade do |t| - t.text "data", limit: 65535, null: false - t.datetime "created_at" - t.datetime "updated_at" - end - create_table "lists", force: :cascade do |t| - t.integer "board_id", limit: 4, null: false - t.integer "label_id", limit: 4 - t.integer "list_type", limit: 4, default: 1, null: false - t.integer "position", limit: 4 + t.integer "board_id", null: false + t.integer "label_id" + t.integer "list_type", default: 1, null: false + t.integer "position" t.datetime "created_at", null: false t.datetime "updated_at", null: false end @@ -673,22 +589,20 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "lists", ["label_id"], name: "index_lists_on_label_id", using: :btree create_table "members", force: :cascade do |t| - t.integer "access_level", limit: 4, null: false - t.integer "source_id", limit: 4, null: false - t.string "source_type", limit: 255, null: false - t.integer "user_id", limit: 4 - t.integer "notification_level", limit: 4, null: false - t.string "type", limit: 255 + t.integer "access_level", null: false + t.integer "source_id", null: false + t.string "source_type", null: false + t.integer "user_id" + t.integer "notification_level", null: false + t.string "type" t.datetime "created_at" t.datetime "updated_at" - t.integer "created_by_id", limit: 4 - t.string "invite_email", limit: 255 - t.string "invite_token", limit: 255 + t.integer "created_by_id" + t.string "invite_email" + t.string "invite_token" t.datetime "invite_accepted_at" t.datetime "requested_at" t.date "expires_at" - t.boolean "ldap", default: false, null: false - t.boolean "override", default: false, null: false end add_index "members", ["access_level"], name: "index_members_on_access_level", using: :btree @@ -698,29 +612,29 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree create_table "merge_request_diffs", force: :cascade do |t| - t.string "state", limit: 255 - t.text "st_commits", limit: 4294967295 - t.text "st_diffs", limit: 4294967295 - t.integer "merge_request_id", limit: 4, null: false + t.string "state" + t.text "st_commits" + t.text "st_diffs" + t.integer "merge_request_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.string "base_commit_sha", limit: 255 - t.string "real_size", limit: 255 - t.string "head_commit_sha", limit: 255 - t.string "start_commit_sha", limit: 255 + t.string "base_commit_sha" + t.string "real_size" + t.string "head_commit_sha" + t.string "start_commit_sha" end add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", using: :btree create_table "merge_request_metrics", force: :cascade do |t| - t.integer "merge_request_id", limit: 4, null: false + t.integer "merge_request_id", null: false t.datetime "latest_build_started_at" t.datetime "latest_build_finished_at" t.datetime "first_deployed_to_production_at" t.datetime "merged_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "pipeline_id", limit: 4 + t.integer "pipeline_id" end add_index "merge_request_metrics", ["first_deployed_to_production_at"], name: "index_merge_request_metrics_on_first_deployed_to_production_at", using: :btree @@ -728,51 +642,51 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "merge_request_metrics", ["pipeline_id"], name: "index_merge_request_metrics_on_pipeline_id", using: :btree create_table "merge_requests", force: :cascade do |t| - t.string "target_branch", limit: 255, null: false - t.string "source_branch", limit: 255, null: false - t.integer "source_project_id", limit: 4, null: false - t.integer "author_id", limit: 4 - t.integer "assignee_id", limit: 4 - t.string "title", limit: 255 + t.string "target_branch", null: false + t.string "source_branch", null: false + t.integer "source_project_id", null: false + t.integer "author_id" + t.integer "assignee_id" + t.string "title" t.datetime "created_at" t.datetime "updated_at" - t.integer "milestone_id", limit: 4 - t.string "state", limit: 255 - t.string "merge_status", limit: 255 - t.integer "target_project_id", limit: 4, null: false - t.integer "iid", limit: 4 - t.text "description", limit: 65535 - t.integer "position", limit: 4, default: 0 + t.integer "milestone_id" + t.string "state" + t.string "merge_status" + t.integer "target_project_id", null: false + t.integer "iid" + t.text "description" + t.integer "position", default: 0 t.datetime "locked_at" - t.integer "updated_by_id", limit: 4 - t.text "merge_error", limit: 65535 - t.text "merge_params", limit: 65535 + t.integer "updated_by_id" + t.text "merge_error" + t.text "merge_params" t.boolean "merge_when_build_succeeds", default: false, null: false - t.integer "merge_user_id", limit: 4 - t.string "merge_commit_sha", limit: 255 + t.integer "merge_user_id" + t.string "merge_commit_sha" t.datetime "deleted_at" - t.integer "approvals_before_merge", limit: 4 - t.string "rebase_commit_sha", limit: 255 - t.string "in_progress_merge_commit_sha", limit: 255 - t.integer "lock_version", limit: 4 - t.text "title_html", limit: 65535 - t.text "description_html", limit: 65535 + t.string "in_progress_merge_commit_sha" + t.integer "lock_version" + t.text "title_html" + t.text "description_html" end add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree add_index "merge_requests", ["author_id"], name: "index_merge_requests_on_author_id", using: :btree add_index "merge_requests", ["created_at"], name: "index_merge_requests_on_created_at", using: :btree add_index "merge_requests", ["deleted_at"], name: "index_merge_requests_on_deleted_at", using: :btree + add_index "merge_requests", ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree add_index "merge_requests", ["source_project_id"], name: "index_merge_requests_on_source_project_id", using: :btree add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree add_index "merge_requests", ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true, using: :btree add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree + add_index "merge_requests", ["title"], name: "index_merge_requests_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"} create_table "merge_requests_closing_issues", force: :cascade do |t| - t.integer "merge_request_id", limit: 4, null: false - t.integer "issue_id", limit: 4, null: false + t.integer "merge_request_id", null: false + t.integer "issue_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end @@ -781,78 +695,73 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "merge_requests_closing_issues", ["merge_request_id"], name: "index_merge_requests_closing_issues_on_merge_request_id", using: :btree create_table "milestones", force: :cascade do |t| - t.string "title", limit: 255, null: false - t.integer "project_id", limit: 4, null: false - t.text "description", limit: 65535 + t.string "title", null: false + t.integer "project_id", null: false + t.text "description" t.date "due_date" t.datetime "created_at" t.datetime "updated_at" - t.string "state", limit: 255 - t.integer "iid", limit: 4 - t.text "title_html", limit: 65535 - t.text "description_html", limit: 65535 + t.string "state" + t.integer "iid" + t.text "title_html" + t.text "description_html" end + add_index "milestones", ["description"], name: "index_milestones_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree add_index "milestones", ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree add_index "milestones", ["title"], name: "index_milestones_on_title", using: :btree + add_index "milestones", ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"} create_table "namespaces", force: :cascade do |t| - t.string "name", limit: 255, null: false - t.string "path", limit: 255, null: false - t.integer "owner_id", limit: 4 + t.string "name", null: false + t.string "path", null: false + t.integer "owner_id" t.datetime "created_at" t.datetime "updated_at" - t.string "type", limit: 255 - t.string "description", limit: 255, default: "", null: false - t.string "avatar", limit: 255 - t.boolean "membership_lock", default: false + t.string "type" + t.string "description", default: "", null: false + t.string "avatar" t.boolean "share_with_group_lock", default: false - t.integer "visibility_level", limit: 4, default: 20, null: false + t.integer "visibility_level", default: 20, null: false t.boolean "request_access_enabled", default: false, null: false t.datetime "deleted_at" - t.string "ldap_sync_status", limit: 255, default: "ready", null: false - t.string "ldap_sync_error", limit: 255 - t.datetime "ldap_sync_last_update_at" - t.datetime "ldap_sync_last_successful_update_at" - t.datetime "ldap_sync_last_sync_at" t.boolean "lfs_enabled" - t.integer "repository_size_limit", limit: 4 - t.text "description_html", limit: 65535 + t.text "description_html" end add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree add_index "namespaces", ["deleted_at"], name: "index_namespaces_on_deleted_at", using: :btree - add_index "namespaces", ["ldap_sync_last_successful_update_at"], name: "index_namespaces_on_ldap_sync_last_successful_update_at", using: :btree - add_index "namespaces", ["ldap_sync_last_update_at"], name: "index_namespaces_on_ldap_sync_last_update_at", using: :btree add_index "namespaces", ["name"], name: "index_namespaces_on_name", unique: true, using: :btree + add_index "namespaces", ["name"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree + add_index "namespaces", ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"} add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree create_table "notes", force: :cascade do |t| - t.text "note", limit: 65535 - t.string "noteable_type", limit: 255 - t.integer "author_id", limit: 4 + t.text "note" + t.string "noteable_type" + t.integer "author_id" t.datetime "created_at" t.datetime "updated_at" - t.integer "project_id", limit: 4 - t.string "attachment", limit: 255 - t.string "line_code", limit: 255 - t.string "commit_id", limit: 255 - t.integer "noteable_id", limit: 4 + t.integer "project_id" + t.string "attachment" + t.string "line_code" + t.string "commit_id" + t.integer "noteable_id" t.boolean "system", default: false, null: false - t.text "st_diff", limit: 4294967295 - t.integer "updated_by_id", limit: 4 - t.string "type", limit: 255 - t.text "position", limit: 65535 - t.text "original_position", limit: 65535 + t.text "st_diff" + t.integer "updated_by_id" + t.string "type" + t.text "position" + t.text "original_position" t.datetime "resolved_at" - t.integer "resolved_by_id", limit: 4 - t.string "discussion_id", limit: 255 - t.string "original_discussion_id", limit: 255 - t.text "note_html", limit: 65535 + t.integer "resolved_by_id" + t.string "discussion_id" + t.string "original_discussion_id" + t.text "note_html" end add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree @@ -860,6 +769,7 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree add_index "notes", ["discussion_id"], name: "index_notes_on_discussion_id", using: :btree add_index "notes", ["line_code"], name: "index_notes_on_line_code", using: :btree + add_index "notes", ["note"], name: "index_notes_on_note_trigram", using: :gin, opclasses: {"note"=>"gin_trgm_ops"} add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree @@ -867,13 +777,13 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree create_table "notification_settings", force: :cascade do |t| - t.integer "user_id", limit: 4, null: false - t.integer "source_id", limit: 4 - t.string "source_type", limit: 255 - t.integer "level", limit: 4, default: 0, null: false + t.integer "user_id", null: false + t.integer "source_id" + t.string "source_type" + t.integer "level", default: 0, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.text "events", limit: 65535 + t.text "events" end add_index "notification_settings", ["source_id", "source_type"], name: "index_notification_settings_on_source_id_and_source_type", using: :btree @@ -881,27 +791,27 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "notification_settings", ["user_id"], name: "index_notification_settings_on_user_id", using: :btree create_table "oauth_access_grants", force: :cascade do |t| - t.integer "resource_owner_id", limit: 4, null: false - t.integer "application_id", limit: 4, null: false - t.string "token", limit: 255, null: false - t.integer "expires_in", limit: 4, null: false - t.text "redirect_uri", limit: 65535, null: false + t.integer "resource_owner_id", null: false + t.integer "application_id", null: false + t.string "token", null: false + t.integer "expires_in", null: false + t.text "redirect_uri", null: false t.datetime "created_at", null: false t.datetime "revoked_at" - t.string "scopes", limit: 255 + t.string "scopes" end add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree create_table "oauth_access_tokens", force: :cascade do |t| - t.integer "resource_owner_id", limit: 4 - t.integer "application_id", limit: 4 - t.string "token", limit: 255, null: false - t.string "refresh_token", limit: 255 - t.integer "expires_in", limit: 4 + t.integer "resource_owner_id" + t.integer "application_id" + t.string "token", null: false + t.string "refresh_token" + t.integer "expires_in" t.datetime "revoked_at" t.datetime "created_at", null: false - t.string "scopes", limit: 255 + t.string "scopes" end add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree @@ -909,47 +819,24 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree create_table "oauth_applications", force: :cascade do |t| - t.string "name", limit: 255, null: false - t.string "uid", limit: 255, null: false - t.string "secret", limit: 255, null: false - t.text "redirect_uri", limit: 65535, null: false - t.string "scopes", limit: 255, default: "", null: false + t.string "name", null: false + t.string "uid", null: false + t.string "secret", null: false + t.text "redirect_uri", null: false + t.string "scopes", default: "", null: false t.datetime "created_at" t.datetime "updated_at" - t.integer "owner_id", limit: 4 - t.string "owner_type", limit: 255 + t.integer "owner_id" + t.string "owner_type" end add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree - create_table "pages_domains", force: :cascade do |t| - t.integer "project_id", limit: 4 - t.text "certificate", limit: 65535 - t.text "encrypted_key", limit: 65535 - t.string "encrypted_key_iv", limit: 255 - t.string "encrypted_key_salt", limit: 255 - t.string "domain", limit: 255 - end - - add_index "pages_domains", ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree - - create_table "path_locks", force: :cascade do |t| - t.string "path", limit: 255, null: false - t.integer "project_id", limit: 4 - t.integer "user_id", limit: 4 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - - add_index "path_locks", ["path"], name: "index_path_locks_on_path", using: :btree - add_index "path_locks", ["project_id"], name: "index_path_locks_on_project_id", using: :btree - add_index "path_locks", ["user_id"], name: "index_path_locks_on_user_id", using: :btree - create_table "personal_access_tokens", force: :cascade do |t| - t.integer "user_id", limit: 4, null: false - t.string "token", limit: 255, null: false - t.string "name", limit: 255, null: false + t.integer "user_id", null: false + t.string "token", null: false + t.string "name", null: false t.boolean "revoked", default: false t.datetime "expires_at" t.datetime "created_at", null: false @@ -960,75 +847,64 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "personal_access_tokens", ["user_id"], name: "index_personal_access_tokens_on_user_id", using: :btree create_table "project_features", force: :cascade do |t| - t.integer "project_id", limit: 4 - t.integer "merge_requests_access_level", limit: 4 - t.integer "issues_access_level", limit: 4 - t.integer "wiki_access_level", limit: 4 - t.integer "snippets_access_level", limit: 4 - t.integer "builds_access_level", limit: 4 + t.integer "project_id" + t.integer "merge_requests_access_level" + t.integer "issues_access_level" + t.integer "wiki_access_level" + t.integer "snippets_access_level" + t.integer "builds_access_level" t.datetime "created_at" t.datetime "updated_at" - t.integer "repository_access_level", limit: 4, default: 20, null: false + t.integer "repository_access_level", default: 20, null: false end add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", using: :btree create_table "project_group_links", force: :cascade do |t| - t.integer "project_id", limit: 4, null: false - t.integer "group_id", limit: 4, null: false + t.integer "project_id", null: false + t.integer "group_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.integer "group_access", limit: 4, default: 30, null: false + t.integer "group_access", default: 30, null: false t.date "expires_at" end create_table "project_import_data", force: :cascade do |t| - t.integer "project_id", limit: 4 - t.text "data", limit: 65535 - t.text "encrypted_credentials", limit: 65535 - t.string "encrypted_credentials_iv", limit: 255 - t.string "encrypted_credentials_salt", limit: 255 + t.integer "project_id" + t.text "data" + t.text "encrypted_credentials" + t.string "encrypted_credentials_iv" + t.string "encrypted_credentials_salt" end add_index "project_import_data", ["project_id"], name: "index_project_import_data_on_project_id", using: :btree create_table "projects", force: :cascade do |t| - t.string "name", limit: 255 - t.string "path", limit: 255 - t.text "description", limit: 65535 + t.string "name" + t.string "path" + t.text "description" t.datetime "created_at" t.datetime "updated_at" - t.integer "creator_id", limit: 4 - t.integer "namespace_id", limit: 4 + t.integer "creator_id" + t.integer "namespace_id" t.datetime "last_activity_at" - t.string "import_url", limit: 255 - t.integer "visibility_level", limit: 4, default: 0, null: false + t.string "import_url" + t.integer "visibility_level", default: 0, null: false t.boolean "archived", default: false, null: false - t.string "avatar", limit: 255 - t.string "import_status", limit: 255 - t.float "repository_size", limit: 24, default: 0.0 - t.text "merge_requests_template", limit: 65535 - t.integer "star_count", limit: 4, default: 0, null: false - t.boolean "merge_requests_rebase_enabled", default: false - t.string "import_type", limit: 255 - t.string "import_source", limit: 255 - t.integer "approvals_before_merge", limit: 4, default: 0, null: false - t.boolean "reset_approvals_on_push", default: true - t.integer "commit_count", limit: 4, default: 0 - t.boolean "merge_requests_ff_only_enabled", default: false - t.text "issues_template", limit: 65535 - t.boolean "mirror", default: false, null: false - t.datetime "mirror_last_update_at" - t.datetime "mirror_last_successful_update_at" - t.integer "mirror_user_id", limit: 4 - t.text "import_error", limit: 65535 - t.integer "ci_id", limit: 4 + t.string "avatar" + t.string "import_status" + t.float "repository_size", default: 0.0 + t.integer "star_count", default: 0, null: false + t.string "import_type" + t.string "import_source" + t.integer "commit_count", default: 0 + t.text "import_error" + t.integer "ci_id" t.boolean "shared_runners_enabled", default: true, null: false - t.string "runners_token", limit: 255 - t.string "build_coverage_regex", limit: 255 + t.string "runners_token" + t.string "build_coverage_regex" t.boolean "build_allow_git_fetch", default: true, null: false - t.integer "build_timeout", limit: 4, default: 3600, null: false - t.boolean "mirror_trigger_builds", default: false, null: false + t.integer "build_timeout", default: 3600, null: false t.boolean "pending_delete", default: false t.boolean "public_builds", default: true, null: false t.boolean "last_repository_check_failed" @@ -1036,132 +912,90 @@ ActiveRecord::Schema.define(version: 20161109150329) do t.boolean "container_registry_enabled" t.boolean "only_allow_merge_if_build_succeeds", default: false, null: false t.boolean "has_external_issue_tracker" - t.string "repository_storage", limit: 255, default: "default", null: false + t.string "repository_storage", default: "default", null: false t.boolean "request_access_enabled", default: false, null: false t.boolean "has_external_wiki" - t.boolean "repository_read_only" t.boolean "lfs_enabled" - t.integer "repository_size_limit", limit: 4 - t.text "description_html", limit: 65535 + t.text "description_html" t.boolean "only_allow_merge_if_all_discussions_are_resolved", default: false, null: false end add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree add_index "projects", ["created_at"], name: "index_projects_on_created_at", using: :btree add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree + add_index "projects", ["description"], name: "index_projects_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree add_index "projects", ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed", using: :btree + add_index "projects", ["name"], name: "index_projects_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree add_index "projects", ["path"], name: "index_projects_on_path", using: :btree + add_index "projects", ["path"], name: "index_projects_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"} add_index "projects", ["pending_delete"], name: "index_projects_on_pending_delete", using: :btree add_index "projects", ["runners_token"], name: "index_projects_on_runners_token", using: :btree add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree create_table "protected_branch_merge_access_levels", force: :cascade do |t| - t.integer "protected_branch_id", limit: 4, null: false - t.integer "access_level", limit: 4, default: 40 + t.integer "protected_branch_id", null: false + t.integer "access_level", default: 40, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "user_id", limit: 4 - t.integer "group_id", limit: 4 end - add_index "protected_branch_merge_access_levels", ["group_id"], name: "fk_rails_98f3d044fe", using: :btree add_index "protected_branch_merge_access_levels", ["protected_branch_id"], name: "index_protected_branch_merge_access", using: :btree - add_index "protected_branch_merge_access_levels", ["user_id"], name: "index_protected_branch_merge_access_levels_on_user_id", using: :btree create_table "protected_branch_push_access_levels", force: :cascade do |t| - t.integer "protected_branch_id", limit: 4, null: false - t.integer "access_level", limit: 4, default: 40 + t.integer "protected_branch_id", null: false + t.integer "access_level", default: 40, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "user_id", limit: 4 - t.integer "group_id", limit: 4 end - add_index "protected_branch_push_access_levels", ["group_id"], name: "fk_rails_7111b68cdb", using: :btree add_index "protected_branch_push_access_levels", ["protected_branch_id"], name: "index_protected_branch_push_access", using: :btree - add_index "protected_branch_push_access_levels", ["user_id"], name: "index_protected_branch_push_access_levels_on_user_id", using: :btree create_table "protected_branches", force: :cascade do |t| - t.integer "project_id", limit: 4, null: false - t.string "name", limit: 255, null: false + t.integer "project_id", null: false + t.string "name", null: false t.datetime "created_at" t.datetime "updated_at" end add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree - create_table "push_rules", force: :cascade do |t| - t.string "force_push_regex", limit: 255 - t.string "delete_branch_regex", limit: 255 - t.string "commit_message_regex", limit: 255 - t.boolean "deny_delete_tag" - t.integer "project_id", limit: 4 - t.datetime "created_at" - t.datetime "updated_at" - t.string "author_email_regex", limit: 255 - t.boolean "member_check", default: false, null: false - t.string "file_name_regex", limit: 255 - t.boolean "is_sample", default: false - t.integer "max_file_size", limit: 4, default: 0, null: false - t.boolean "prevent_secrets", default: false, null: false - end - - add_index "push_rules", ["project_id"], name: "index_push_rules_on_project_id", using: :btree - create_table "releases", force: :cascade do |t| - t.string "tag", limit: 255 - t.text "description", limit: 65535 - t.integer "project_id", limit: 4 + t.string "tag" + t.text "description" + t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.text "description_html", limit: 65535 + t.text "description_html" end add_index "releases", ["project_id", "tag"], name: "index_releases_on_project_id_and_tag", using: :btree add_index "releases", ["project_id"], name: "index_releases_on_project_id", using: :btree - create_table "remote_mirrors", force: :cascade do |t| - t.integer "project_id", limit: 4 - t.string "url", limit: 255 - t.boolean "enabled", default: false - t.string "update_status", limit: 255 - t.datetime "last_update_at" - t.datetime "last_successful_update_at" - t.string "last_error", limit: 255 - t.text "encrypted_credentials", limit: 65535 - t.string "encrypted_credentials_iv", limit: 255 - t.string "encrypted_credentials_salt", limit: 255 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - - add_index "remote_mirrors", ["project_id"], name: "index_remote_mirrors_on_project_id", using: :btree - create_table "sent_notifications", force: :cascade do |t| - t.integer "project_id", limit: 4 - t.integer "noteable_id", limit: 4 - t.string "noteable_type", limit: 255 - t.integer "recipient_id", limit: 4 - t.string "commit_id", limit: 255 - t.string "reply_key", limit: 255, null: false - t.string "line_code", limit: 255 - t.string "note_type", limit: 255 - t.text "position", limit: 65535 + t.integer "project_id" + t.integer "noteable_id" + t.string "noteable_type" + t.integer "recipient_id" + t.string "commit_id" + t.string "reply_key", null: false + t.string "line_code" + t.string "note_type" + t.text "position" end add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree create_table "services", force: :cascade do |t| - t.string "type", limit: 255 - t.string "title", limit: 255 - t.integer "project_id", limit: 4 + t.string "type" + t.string "title" + t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" t.boolean "active", default: false, null: false - t.text "properties", limit: 65535 + t.text "properties" t.boolean "template", default: false t.boolean "push_events", default: true t.boolean "issues_events", default: true @@ -1169,7 +1003,7 @@ ActiveRecord::Schema.define(version: 20161109150329) do t.boolean "tag_push_events", default: true t.boolean "note_events", default: true, null: false t.boolean "build_events", default: false, null: false - t.string "category", limit: 255, default: "common", null: false + t.string "category", default: "common", null: false t.boolean "default", default: false t.boolean "wiki_page_events", default: true t.boolean "pipeline_events", default: false, null: false @@ -1180,41 +1014,43 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "services", ["template"], name: "index_services_on_template", using: :btree create_table "snippets", force: :cascade do |t| - t.string "title", limit: 255 - t.text "content", limit: 4294967295 - t.integer "author_id", limit: 4, null: false - t.integer "project_id", limit: 4 + t.string "title" + t.text "content" + t.integer "author_id", null: false + t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.string "file_name", limit: 255 - t.string "type", limit: 255 - t.integer "visibility_level", limit: 4, default: 0, null: false - t.text "title_html", limit: 65535 - t.text "content_html", limit: 65535 + t.string "file_name" + t.string "type" + t.integer "visibility_level", default: 0, null: false + t.text "title_html" + t.text "content_html" end add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree + add_index "snippets", ["file_name"], name: "index_snippets_on_file_name_trigram", using: :gin, opclasses: {"file_name"=>"gin_trgm_ops"} add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree + add_index "snippets", ["title"], name: "index_snippets_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"} add_index "snippets", ["updated_at"], name: "index_snippets_on_updated_at", using: :btree add_index "snippets", ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree create_table "spam_logs", force: :cascade do |t| - t.integer "user_id", limit: 4 - t.string "source_ip", limit: 255 - t.string "user_agent", limit: 255 + t.integer "user_id" + t.string "source_ip" + t.string "user_agent" t.boolean "via_api" - t.string "noteable_type", limit: 255 - t.string "title", limit: 255 - t.text "description", limit: 65535 + t.string "noteable_type" + t.string "title" + t.text "description" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.boolean "submitted_as_ham", default: false, null: false end create_table "subscriptions", force: :cascade do |t| - t.integer "user_id", limit: 4 - t.integer "subscribable_id", limit: 4 - t.string "subscribable_type", limit: 255 + t.integer "user_id" + t.integer "subscribable_id" + t.string "subscribable_type" t.boolean "subscribed" t.datetime "created_at" t.datetime "updated_at" @@ -1223,12 +1059,12 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "subscriptions", ["subscribable_id", "subscribable_type", "user_id"], name: "subscriptions_user_id_and_ref_fields", unique: true, using: :btree create_table "taggings", force: :cascade do |t| - t.integer "tag_id", limit: 4 - t.integer "taggable_id", limit: 4 - t.string "taggable_type", limit: 255 - t.integer "tagger_id", limit: 4 - t.string "tagger_type", limit: 255 - t.string "context", limit: 255 + t.integer "tag_id" + t.integer "taggable_id" + t.string "taggable_type" + t.integer "tagger_id" + t.string "tagger_type" + t.string "context" t.datetime "created_at" end @@ -1236,24 +1072,24 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "tags", force: :cascade do |t| - t.string "name", limit: 255 - t.integer "taggings_count", limit: 4, default: 0 + t.string "name" + t.integer "taggings_count", default: 0 end add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree create_table "todos", force: :cascade do |t| - t.integer "user_id", limit: 4, null: false - t.integer "project_id", limit: 4, null: false - t.integer "target_id", limit: 4 - t.string "target_type", limit: 255, null: false - t.integer "author_id", limit: 4 - t.integer "action", limit: 4, null: false - t.string "state", limit: 255, null: false + t.integer "user_id", null: false + t.integer "project_id", null: false + t.integer "target_id" + t.string "target_type", null: false + t.integer "author_id" + t.integer "action", null: false + t.string "state", null: false t.datetime "created_at" t.datetime "updated_at" - t.integer "note_id", limit: 4 - t.string "commit_id", limit: 255 + t.integer "note_id" + t.string "commit_id" end add_index "todos", ["author_id"], name: "index_todos_on_author_id", using: :btree @@ -1264,104 +1100,95 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "todos", ["user_id"], name: "index_todos_on_user_id", using: :btree create_table "trending_projects", force: :cascade do |t| - t.integer "project_id", limit: 4, null: false + t.integer "project_id", null: false end add_index "trending_projects", ["project_id"], name: "index_trending_projects_on_project_id", using: :btree create_table "u2f_registrations", force: :cascade do |t| - t.text "certificate", limit: 65535 - t.string "key_handle", limit: 255 - t.string "public_key", limit: 255 - t.integer "counter", limit: 4 - t.integer "user_id", limit: 4 + t.text "certificate" + t.string "key_handle" + t.string "public_key" + t.integer "counter" + t.integer "user_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "name", limit: 255 + t.string "name" end add_index "u2f_registrations", ["key_handle"], name: "index_u2f_registrations_on_key_handle", using: :btree add_index "u2f_registrations", ["user_id"], name: "index_u2f_registrations_on_user_id", using: :btree - create_table "user_activities", force: :cascade do |t| - t.integer "user_id", limit: 4 - t.datetime "last_activity_at", null: false - end - - add_index "user_activities", ["user_id"], name: "index_user_activities_on_user_id", unique: true, using: :btree - create_table "user_agent_details", force: :cascade do |t| - t.string "user_agent", limit: 255, null: false - t.string "ip_address", limit: 255, null: false - t.integer "subject_id", limit: 4, null: false - t.string "subject_type", limit: 255, null: false + t.string "user_agent", null: false + t.string "ip_address", null: false + t.integer "subject_id", null: false + t.string "subject_type", null: false t.boolean "submitted", default: false, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end create_table "users", force: :cascade do |t| - t.string "email", limit: 255, default: "", null: false - t.string "encrypted_password", limit: 255, default: "", null: false - t.string "reset_password_token", limit: 255 + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", limit: 4, default: 0 + t.integer "sign_in_count", default: 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" - t.string "current_sign_in_ip", limit: 255 - t.string "last_sign_in_ip", limit: 255 + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" t.datetime "created_at" t.datetime "updated_at" - t.string "name", limit: 255 + t.string "name" t.boolean "admin", default: false, null: false - t.integer "projects_limit", limit: 4, default: 10 - t.string "skype", limit: 255, default: "", null: false - t.string "linkedin", limit: 255, default: "", null: false - t.string "twitter", limit: 255, default: "", null: false - t.string "authentication_token", limit: 255 - t.integer "theme_id", limit: 4, default: 1, null: false - t.string "bio", limit: 255 - t.integer "failed_attempts", limit: 4, default: 0 + t.integer "projects_limit", default: 10 + t.string "skype", default: "", null: false + t.string "linkedin", default: "", null: false + t.string "twitter", default: "", null: false + t.string "authentication_token" + t.integer "theme_id", default: 1, null: false + t.string "bio" + t.integer "failed_attempts", default: 0 t.datetime "locked_at" - t.string "username", limit: 255 + t.string "username" t.boolean "can_create_group", default: true, null: false t.boolean "can_create_team", default: true, null: false - t.string "state", limit: 255 - t.integer "color_scheme_id", limit: 4, default: 1, null: false + t.string "state" + t.integer "color_scheme_id", default: 1, null: false t.datetime "password_expires_at" - t.integer "created_by_id", limit: 4 + t.integer "created_by_id" t.datetime "last_credential_check_at" - t.string "avatar", limit: 255 - t.string "confirmation_token", limit: 255 + t.string "avatar" + t.string "confirmation_token" t.datetime "confirmed_at" t.datetime "confirmation_sent_at" - t.string "unconfirmed_email", limit: 255 + t.string "unconfirmed_email" t.boolean "hide_no_ssh_key", default: false - t.string "website_url", limit: 255, default: "", null: false - t.datetime "admin_email_unsubscribed_at" - t.string "notification_email", limit: 255 + t.string "website_url", default: "", null: false + t.string "notification_email" t.boolean "hide_no_password", default: false t.boolean "password_automatically_set", default: false - t.string "location", limit: 255 - t.string "encrypted_otp_secret", limit: 255 - t.string "encrypted_otp_secret_iv", limit: 255 - t.string "encrypted_otp_secret_salt", limit: 255 + t.string "location" + t.string "encrypted_otp_secret" + t.string "encrypted_otp_secret_iv" + t.string "encrypted_otp_secret_salt" t.boolean "otp_required_for_login", default: false, null: false - t.text "otp_backup_codes", limit: 65535 - t.string "public_email", limit: 255, default: "", null: false - t.integer "dashboard", limit: 4, default: 0 - t.integer "project_view", limit: 4, default: 0 - t.integer "consumed_timestep", limit: 4 - t.integer "layout", limit: 4, default: 0 + t.text "otp_backup_codes" + t.string "public_email", default: "", null: false + t.integer "dashboard", default: 0 + t.integer "project_view", default: 0 + t.integer "consumed_timestep" + t.integer "layout", default: 0 t.boolean "hide_project_limit", default: false - t.text "note", limit: 65535 - t.string "unlock_token", limit: 255 + t.string "unlock_token" t.datetime "otp_grace_period_started_at" t.boolean "ldap_email", default: false, null: false t.boolean "external", default: false - t.string "organization", limit: 255 - t.string "incoming_email_token", limit: 255 + t.string "organization" + t.string "incoming_email_token" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree @@ -1370,15 +1197,18 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_index "users", ["created_at"], name: "index_users_on_created_at", using: :btree add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree + add_index "users", ["email"], name: "index_users_on_email_trigram", using: :gin, opclasses: {"email"=>"gin_trgm_ops"} add_index "users", ["incoming_email_token"], name: "index_users_on_incoming_email_token", using: :btree add_index "users", ["name"], name: "index_users_on_name", using: :btree + add_index "users", ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree add_index "users", ["state"], name: "index_users_on_state", using: :btree add_index "users", ["username"], name: "index_users_on_username", using: :btree + add_index "users", ["username"], name: "index_users_on_username_trigram", using: :gin, opclasses: {"username"=>"gin_trgm_ops"} create_table "users_star_projects", force: :cascade do |t| - t.integer "project_id", limit: 4, null: false - t.integer "user_id", limit: 4, null: false + t.integer "project_id", null: false + t.integer "user_id", null: false t.datetime "created_at" t.datetime "updated_at" end @@ -1389,28 +1219,26 @@ ActiveRecord::Schema.define(version: 20161109150329) do create_table "web_hooks", force: :cascade do |t| t.string "url", limit: 2000 - t.integer "project_id", limit: 4 + t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.string "type", limit: 255, default: "ProjectHook" - t.integer "service_id", limit: 4 + t.string "type", default: "ProjectHook" + t.integer "service_id" t.boolean "push_events", default: true, null: false t.boolean "issues_events", default: false, null: false t.boolean "merge_requests_events", default: false, null: false t.boolean "tag_push_events", default: false - t.integer "group_id", limit: 4 t.boolean "note_events", default: false, null: false t.boolean "enable_ssl_verification", default: true t.boolean "build_events", default: false, null: false - t.string "token", limit: 255 t.boolean "wiki_page_events", default: false, null: false + t.string "token" t.boolean "pipeline_events", default: false, null: false t.boolean "confidential_issues_events", default: false, null: false end add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree - add_foreign_key "approver_groups", "namespaces", column: "group_id", on_delete: :cascade add_foreign_key "boards", "projects" add_foreign_key "issue_metrics", "issues", on_delete: :cascade add_foreign_key "label_priorities", "labels", on_delete: :cascade @@ -1422,17 +1250,9 @@ ActiveRecord::Schema.define(version: 20161109150329) do add_foreign_key "merge_request_metrics", "merge_requests", on_delete: :cascade add_foreign_key "merge_requests_closing_issues", "issues", on_delete: :cascade add_foreign_key "merge_requests_closing_issues", "merge_requests", on_delete: :cascade - add_foreign_key "path_locks", "projects" - add_foreign_key "path_locks", "users" add_foreign_key "personal_access_tokens", "users" - add_foreign_key "protected_branch_merge_access_levels", "namespaces", column: "group_id" add_foreign_key "protected_branch_merge_access_levels", "protected_branches" - add_foreign_key "protected_branch_merge_access_levels", "users" - add_foreign_key "protected_branch_push_access_levels", "namespaces", column: "group_id" add_foreign_key "protected_branch_push_access_levels", "protected_branches" - add_foreign_key "protected_branch_push_access_levels", "users" - add_foreign_key "remote_mirrors", "projects" add_foreign_key "trending_projects", "projects", on_delete: :cascade add_foreign_key "u2f_registrations", "users" - add_foreign_key "user_activities", "users", on_delete: :cascade end diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb index c415b93c80f..1e76fbec855 100644 --- a/lib/gitlab/cycle_analytics/base_event.rb +++ b/lib/gitlab/cycle_analytics/base_event.rb @@ -1,26 +1,36 @@ module Gitlab module CycleAnalytics class BaseEvent - extend MetricsTables + include MetricsTables - class << self - attr_reader :stage, :start_time_attrs, :end_time_attrs, :projections + attr_reader :stage, :start_time_attrs, :end_time_attrs, :projections, :query - def order - @order || @start_time_attrs + def initialize(project:, options:) + @query = EventsQuery.new(project: project, options: options) + @project = project + @options = options + end + + def fetch + @query.execute(self).map do |event| + serialize(event) if has_permission?(event['id']) end + end - def query(_base_query); end + def custom_query(_base_query); end - def fetch(query) - query.execute(self).map { |event| serialize(event, query) } - end + def order + @order || @start_time_attrs + end - private + private - def serialize(_event, _query) - raise NotImplementedError.new("Expected #{self.name} to implement serialize(event, query)") - end + def serialize(_event) + raise NotImplementedError.new("Expected #{self.name} to implement serialize(event)") + end + + def has_permission?(_id) + true end end end diff --git a/lib/gitlab/cycle_analytics/code_event.rb b/lib/gitlab/cycle_analytics/code_event.rb index 609b45579b4..29f62cb22aa 100644 --- a/lib/gitlab/cycle_analytics/code_event.rb +++ b/lib/gitlab/cycle_analytics/code_event.rb @@ -1,24 +1,31 @@ module Gitlab module CycleAnalytics class CodeEvent < BaseEvent - @stage = :code - @start_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at] + def initialize(*args) + @stage = :code + @start_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at] + @end_time_attrs = mr_table[:created_at] + @projections = [mr_table[:title], + mr_table[:iid], + mr_table[:id], + mr_table[:created_at], + mr_table[:state], + mr_table[:author_id]] + @order = mr_table[:created_at] - @end_time_attrs = mr_table[:created_at] - - @projections = [mr_table[:title], - mr_table[:iid], - mr_table[:id], - mr_table[:created_at], - mr_table[:state], - mr_table[:author_id]] + super(*args) + end - @order = mr_table[:created_at] + private - def self.serialize(event, query) + def serialize(event) event['author'] = User.find(event.delete('author_id')) - AnalyticsMergeRequestSerializer.new(project: query.project).represent(event).as_json + AnalyticsMergeRequestSerializer.new(project: @project).represent(event).as_json + end + + def has_permission?(id) + @options[:current_user].can?(:read_merge_request, MergeRequest.find(id)) end end end diff --git a/lib/gitlab/cycle_analytics/event_config.rb b/lib/gitlab/cycle_analytics/event_config.rb deleted file mode 100644 index 2e1400c41b9..00000000000 --- a/lib/gitlab/cycle_analytics/event_config.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Gitlab - module CycleAnalytics - class TestEvent < BaseEvent - @start_time_attrs = mr_table[:created_at] - - @end_time_attrs = mr_metrics_table[:merged_at] - - @projections = [mr_table[:title], - mr_table[:iid], - mr_table[:id], - mr_table[:created_at], - mr_table[:state], - mr_table[:author_id]] - end - end -end diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb index 6580c73128c..2d703d76cbb 100644 --- a/lib/gitlab/cycle_analytics/events.rb +++ b/lib/gitlab/cycle_analytics/events.rb @@ -3,35 +3,35 @@ module Gitlab class Events def initialize(project:, options:) @project = project - @query = EventsQuery.new(project: project, options: options) + @options = options end def issue_events - IssueEvent.fetch(@query) + IssueEvent.new(project: @project, options: @options).fetch end def plan_events - PlanEvent.fetch(@query) + PlanEvent.new(project: @project, options: @options).fetch end def code_events - CodeEvent.fetch(@query) + CodeEvent.new(project: @project, options: @options).fetch end def test_events - TestEvent.fetch(@query) + TestEvent.new(project: @project, options: @options).fetch end def review_events - ReviewEvent.fetch(@query) + ReviewEvent.new(project: @project, options: @options).fetch end def staging_events - StagingEvent.fetch(@query) + StagingEvent.new(project: @project, options: @options).fetch end def production_events - ProductionEvent.fetch(@query) + ProductionEvent.new(project: @project, options: @options).fetch end end end diff --git a/lib/gitlab/cycle_analytics/events_query.rb b/lib/gitlab/cycle_analytics/events_query.rb index 7ecdc07d17f..2418832ccc2 100644 --- a/lib/gitlab/cycle_analytics/events_query.rb +++ b/lib/gitlab/cycle_analytics/events_query.rb @@ -22,7 +22,7 @@ module Gitlab base_query = @fetcher.base_query_for(@stage_class.stage) diff_fn = @fetcher.subtract_datetimes_diff(base_query, @stage_class.start_time_attrs, @stage_class.end_time_attrs) - @stage_class.query(base_query) + @stage_class.custom_query(base_query) base_query.project(extract_epoch(diff_fn).as('total_time'), *@stage_class.projections).order(@stage_class.order.desc) end diff --git a/lib/gitlab/cycle_analytics/issue_event.rb b/lib/gitlab/cycle_analytics/issue_event.rb index 9cfcdc9b20e..70c015df419 100644 --- a/lib/gitlab/cycle_analytics/issue_event.rb +++ b/lib/gitlab/cycle_analytics/issue_event.rb @@ -1,22 +1,30 @@ module Gitlab module CycleAnalytics class IssueEvent < BaseEvent - @stage = :issue - @start_time_attrs = issue_table[:created_at] + def initialize(*args) + @stage = :issue + @start_time_attrs = issue_table[:created_at] + @end_time_attrs = [issue_metrics_table[:first_associated_with_milestone_at], + issue_metrics_table[:first_added_to_board_at]] + @projections = [issue_table[:title], + issue_table[:iid], + issue_table[:id], + issue_table[:created_at], + issue_table[:author_id]] - @end_time_attrs = [issue_metrics_table[:first_associated_with_milestone_at], - issue_metrics_table[:first_added_to_board_at]] + super(*args) + end - @projections = [issue_table[:title], - issue_table[:iid], - issue_table[:id], - issue_table[:created_at], - issue_table[:author_id]] + private - def self.serialize(event, query) + def serialize(event) event['author'] = User.find(event.delete('author_id')) - AnalyticsIssueSerializer.new(project: query.project).represent(event).as_json + AnalyticsIssueSerializer.new(project: @project).represent(event).as_json + end + + def has_permission?(id) + @options[:current_user].can?(:read_issue, Issue.find(id)) end end end diff --git a/lib/gitlab/cycle_analytics/plan_event.rb b/lib/gitlab/cycle_analytics/plan_event.rb index 4980a7835eb..b1ae215f348 100644 --- a/lib/gitlab/cycle_analytics/plan_event.rb +++ b/lib/gitlab/cycle_analytics/plan_event.rb @@ -1,43 +1,43 @@ module Gitlab module CycleAnalytics class PlanEvent < BaseEvent - @stage = :plan - @start_time_attrs = issue_metrics_table[:first_associated_with_milestone_at] - - @end_time_attrs = [issue_metrics_table[:first_added_to_board_at], - issue_metrics_table[:first_mentioned_in_commit_at]] - - @projections = [mr_diff_table[:st_commits].as('commits'), - issue_metrics_table[:first_mentioned_in_commit_at]] + def initialize(*args) + @stage = :plan + @start_time_attrs = issue_metrics_table[:first_associated_with_milestone_at] + @end_time_attrs = [issue_metrics_table[:first_added_to_board_at], + issue_metrics_table[:first_mentioned_in_commit_at]] + @projections = [mr_diff_table[:st_commits].as('commits'), + issue_metrics_table[:first_mentioned_in_commit_at]] + + super(*args) + end - class << self - def query(base_query) - base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) - end + def custom_query(base_query) + base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) + end - private + private - def serialize(event, query) - st_commit = first_time_reference_commit(event.delete('commits'), event) + def serialize(event) + st_commit = first_time_reference_commit(event.delete('commits'), event) - return unless st_commit + return unless st_commit - serialize_commit(event, st_commit, query) - end + serialize_commit(event, st_commit, query) + end - def first_time_reference_commit(commits, event) - YAML.load(commits).find do |commit| - next unless commit[:committed_date] && event['first_mentioned_in_commit_at'] + def first_time_reference_commit(commits, event) + YAML.load(commits).find do |commit| + next unless commit[:committed_date] && event['first_mentioned_in_commit_at'] - commit[:committed_date].to_i == DateTime.parse(event['first_mentioned_in_commit_at'].to_s).to_i - end + commit[:committed_date].to_i == DateTime.parse(event['first_mentioned_in_commit_at'].to_s).to_i end + end - def serialize_commit(event, st_commit, query) - commit = Commit.new(Gitlab::Git::Commit.new(st_commit), @project) + def serialize_commit(event, st_commit, query) + commit = Commit.new(Gitlab::Git::Commit.new(st_commit), @project) - AnalyticsCommitSerializer.new(project: query.project, total_time: event['total_time']).represent(commit).as_json - end + AnalyticsCommitSerializer.new(project: @project, total_time: event['total_time']).represent(commit).as_json end end end diff --git a/lib/gitlab/cycle_analytics/production_event.rb b/lib/gitlab/cycle_analytics/production_event.rb index b0b2d94f7e7..80c0d08c039 100644 --- a/lib/gitlab/cycle_analytics/production_event.rb +++ b/lib/gitlab/cycle_analytics/production_event.rb @@ -1,21 +1,29 @@ module Gitlab module CycleAnalytics class ProductionEvent < BaseEvent - @stage = :production - @start_time_attrs = issue_table[:created_at] + def initialize(*args) + @stage = :production + @start_time_attrs = issue_table[:created_at] + @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] + @projections = [issue_table[:title], + issue_table[:iid], + issue_table[:id], + issue_table[:created_at], + issue_table[:author_id]] - @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] + super(*args) + end - @projections = [issue_table[:title], - issue_table[:iid], - issue_table[:id], - issue_table[:created_at], - issue_table[:author_id]] + private - def self.serialize(event, query) + def serialize(event) event['author'] = User.find(event.delete('author_id')) - AnalyticsIssueSerializer.new(project: query.project).represent(event).as_json + AnalyticsIssueSerializer.new(project: @project).represent(event).as_json + end + + def has_permission?(id) + @options[:current_user].can?(:read_issue, Issue.find(id)) end end end diff --git a/lib/gitlab/cycle_analytics/review_event.rb b/lib/gitlab/cycle_analytics/review_event.rb index 82095b23b71..cc89ef68be0 100644 --- a/lib/gitlab/cycle_analytics/review_event.rb +++ b/lib/gitlab/cycle_analytics/review_event.rb @@ -1,20 +1,28 @@ module Gitlab module CycleAnalytics class ReviewEvent < BaseEvent - @stage = :review - @start_time_attrs = mr_table[:created_at] - @end_time_attrs = mr_metrics_table[:merged_at] - @projections = [mr_table[:title], - mr_table[:iid], - mr_table[:id], - mr_table[:created_at], - mr_table[:state], - mr_table[:author_id]] + def initialize(*args) + @stage = :review + @start_time_attrs = mr_table[:created_at] + @end_time_attrs = mr_metrics_table[:merged_at] + @projections = [mr_table[:title], + mr_table[:iid], + mr_table[:id], + mr_table[:created_at], + mr_table[:state], + mr_table[:author_id]] - def self.serialize(event, query) + super(*args) + end + + def serialize(event) event['author'] = User.find(event.delete('author_id')) - AnalyticsMergeRequestSerializer.new(project: query.project).represent(event).as_json + AnalyticsMergeRequestSerializer.new(project: @project).represent(event).as_json + end + + def has_permission?(id) + @options[:current_user].can?(:read_merge_request, MergeRequest.find(id)) end end end diff --git a/lib/gitlab/cycle_analytics/staging_event.rb b/lib/gitlab/cycle_analytics/staging_event.rb index a8872b50cfd..800b2b786dd 100644 --- a/lib/gitlab/cycle_analytics/staging_event.rb +++ b/lib/gitlab/cycle_analytics/staging_event.rb @@ -1,17 +1,23 @@ module Gitlab module CycleAnalytics class StagingEvent < BaseEvent - @stage = :staging - @start_time_attrs = mr_metrics_table[:merged_at] - @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] - @projections = [build_table[:id]] - @order = build_table[:created_at] + def initialize(*args) + @stage = :staging + @start_time_attrs = mr_metrics_table[:merged_at] + @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] + @projections = [build_table[:id]] + @order = build_table[:created_at] - def self.query(base_query) + super(*args) + end + + def custom_query(base_query) base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) end - def self.serialize(event, _query) + private + + def serialize(event) build = ::Ci::Build.find(event['id']) AnalyticsBuildSerializer.new.represent(build).as_json diff --git a/lib/gitlab/cycle_analytics/test_event.rb b/lib/gitlab/cycle_analytics/test_event.rb index c91d3f47da0..00404d7f6b0 100644 --- a/lib/gitlab/cycle_analytics/test_event.rb +++ b/lib/gitlab/cycle_analytics/test_event.rb @@ -1,17 +1,23 @@ module Gitlab module CycleAnalytics class TestEvent < BaseEvent - @stage = :test - @start_time_attrs = mr_metrics_table[:latest_build_started_at] - @end_time_attrs = mr_metrics_table[:latest_build_finished_at] - @projections = [build_table[:id]] - @order = build_table[:created_at] + def initialize(*args) + @stage = :test + @start_time_attrs = mr_metrics_table[:latest_build_started_at] + @end_time_attrs = mr_metrics_table[:latest_build_finished_at] + @projections = [build_table[:id]] + @order = build_table[:created_at] - def self.query(base_query) + super(*args) + end + + def custom_query(base_query) base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) end - def self.serialize(event, _query) + private + + def serialize(event) build = ::Ci::Build.find(event['id']) AnalyticsBuildSerializer.new.represent(build).as_json diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 8e2d2f8b5bd..9aeaa6b3ee8 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -6,7 +6,7 @@ describe Gitlab::CycleAnalytics::Events do let(:user) { create(:user, :admin) } let!(:context) { create(:issue, project: project, created_at: 2.days.ago) } - subject { described_class.new(project: project, options: { from: from_date }) } + subject { described_class.new(project: project, options: { from: from_date, current_user: user }) } before do allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([context]) -- cgit v1.2.1 From de96f295a2c08ee2888de91b910d714a15204fb2 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 17 Nov 2016 18:10:28 +0100 Subject: fix schema.rb --- db/schema.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 22318612796..983e1c90682 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -98,14 +98,14 @@ ActiveRecord::Schema.define(version: 20161109150329) do t.text "help_page_text_html" t.text "shared_runners_text_html" t.text "after_sign_up_text_html" + t.boolean "sidekiq_throttling_enabled", default: false + t.string "sidekiq_throttling_queues" + t.decimal "sidekiq_throttling_factor" t.boolean "housekeeping_enabled", default: true, null: false t.boolean "housekeeping_bitmaps_enabled", default: true, null: false t.integer "housekeeping_incremental_repack_period", default: 10, null: false t.integer "housekeeping_full_repack_period", default: 50, null: false t.integer "housekeeping_gc_period", default: 200, null: false - t.boolean "sidekiq_throttling_enabled", default: false - t.string "sidekiq_throttling_queues" - t.decimal "sidekiq_throttling_factor" end create_table "audit_events", force: :cascade do |t| @@ -917,7 +917,7 @@ ActiveRecord::Schema.define(version: 20161109150329) do t.boolean "has_external_wiki" t.boolean "lfs_enabled" t.text "description_html" - t.boolean "only_allow_merge_if_all_discussions_are_resolved", default: false, null: false + t.boolean "only_allow_merge_if_all_discussions_are_resolved" end add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree -- cgit v1.2.1 From 98773ef9745ae64da953a62fd4fbcbf290a37ea7 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 17 Nov 2016 20:42:18 +0100 Subject: preload ids or objects for users, merge request and issues --- lib/gitlab/cycle_analytics/author_updater.rb | 27 ++++++++++++++++++++++++++ lib/gitlab/cycle_analytics/base_event.rb | 24 ++++++++++++++++++++--- lib/gitlab/cycle_analytics/code_event.rb | 6 ++---- lib/gitlab/cycle_analytics/issue_event.rb | 6 ++---- lib/gitlab/cycle_analytics/production_event.rb | 6 ++---- lib/gitlab/cycle_analytics/review_event.rb | 6 ++---- 6 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 lib/gitlab/cycle_analytics/author_updater.rb diff --git a/lib/gitlab/cycle_analytics/author_updater.rb b/lib/gitlab/cycle_analytics/author_updater.rb new file mode 100644 index 00000000000..ab18f292bc3 --- /dev/null +++ b/lib/gitlab/cycle_analytics/author_updater.rb @@ -0,0 +1,27 @@ +module Gitlab + module CycleAnalytics + class AuthorUpdater + def self.update!(*args) + new(*args).update! + end + + def initialize(event_result) + @event_result = event_result + end + + def update! + @event_result.each do |event| + event['author'] = users[event.delete('author_id').to_i].first + end + end + + def user_ids + @event_result.map { |event| event['author_id'] } + end + + def users + @users ||= User.find(user_ids).group_by { |user| user['id'] } + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb index 1e76fbec855..7395561a3fc 100644 --- a/lib/gitlab/cycle_analytics/base_event.rb +++ b/lib/gitlab/cycle_analytics/base_event.rb @@ -12,7 +12,9 @@ module Gitlab end def fetch - @query.execute(self).map do |event| + update_author! if event_result.first['author_id'] + + event_result.map do |event| serialize(event) if has_permission?(event['id']) end end @@ -25,12 +27,28 @@ module Gitlab private + def update_author! + AuthorUpdater.update!(event_result) + end + + def event_result + @event_result ||= @query.execute(self).to_a + end + def serialize(_event) raise NotImplementedError.new("Expected #{self.name} to implement serialize(event)") end - def has_permission?(_id) - true + def has_permission?(id) + allowed_ids.nil? || allowed_ids.include?(id.to_i) + end + + def allowed_ids + nil + end + + def event_result_ids + event_result.map { |event| event['id'] } end end end diff --git a/lib/gitlab/cycle_analytics/code_event.rb b/lib/gitlab/cycle_analytics/code_event.rb index 29f62cb22aa..02a3a44d544 100644 --- a/lib/gitlab/cycle_analytics/code_event.rb +++ b/lib/gitlab/cycle_analytics/code_event.rb @@ -19,13 +19,11 @@ module Gitlab private def serialize(event) - event['author'] = User.find(event.delete('author_id')) - AnalyticsMergeRequestSerializer.new(project: @project).represent(event).as_json end - def has_permission?(id) - @options[:current_user].can?(:read_merge_request, MergeRequest.find(id)) + def allowed_ids + @allowed_ids ||= MergeRequestsFinder.new(@options[:current_user], project_id: @project.id).execute.where(id: event_result_ids).pluck(:id) end end end diff --git a/lib/gitlab/cycle_analytics/issue_event.rb b/lib/gitlab/cycle_analytics/issue_event.rb index 70c015df419..36a990d6222 100644 --- a/lib/gitlab/cycle_analytics/issue_event.rb +++ b/lib/gitlab/cycle_analytics/issue_event.rb @@ -18,13 +18,11 @@ module Gitlab private def serialize(event) - event['author'] = User.find(event.delete('author_id')) - AnalyticsIssueSerializer.new(project: @project).represent(event).as_json end - def has_permission?(id) - @options[:current_user].can?(:read_issue, Issue.find(id)) + def allowed_ids + @allowed_ids ||= IssuesFinder.new(@options[:current_user], project_id: @project.id).execute.where(id: event_result_ids).pluck(:id) end end end diff --git a/lib/gitlab/cycle_analytics/production_event.rb b/lib/gitlab/cycle_analytics/production_event.rb index 80c0d08c039..fcf2dbe3490 100644 --- a/lib/gitlab/cycle_analytics/production_event.rb +++ b/lib/gitlab/cycle_analytics/production_event.rb @@ -17,13 +17,11 @@ module Gitlab private def serialize(event) - event['author'] = User.find(event.delete('author_id')) - AnalyticsIssueSerializer.new(project: @project).represent(event).as_json end - def has_permission?(id) - @options[:current_user].can?(:read_issue, Issue.find(id)) + def allowed_ids + @allowed_ids ||= IssuesFinder.new(@options[:current_user], project_id: @project.id).execute.where(id: event_result_ids).pluck(:id) end end end diff --git a/lib/gitlab/cycle_analytics/review_event.rb b/lib/gitlab/cycle_analytics/review_event.rb index cc89ef68be0..30650537afe 100644 --- a/lib/gitlab/cycle_analytics/review_event.rb +++ b/lib/gitlab/cycle_analytics/review_event.rb @@ -16,13 +16,11 @@ module Gitlab end def serialize(event) - event['author'] = User.find(event.delete('author_id')) - AnalyticsMergeRequestSerializer.new(project: @project).represent(event).as_json end - def has_permission?(id) - @options[:current_user].can?(:read_merge_request, MergeRequest.find(id)) + def allowed_ids + @allowed_ids ||= MergeRequestsFinder.new(@options[:current_user], project_id: @project.id).execute.where(id: event_result_ids).pluck(:id) end end end -- cgit v1.2.1 From 24c2aa39ad0f55321ef2c28ce10d740bf6b78980 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 17 Nov 2016 20:54:02 +0100 Subject: fix blank state error --- lib/gitlab/cycle_analytics/base_event.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb index 7395561a3fc..cedc73142fa 100644 --- a/lib/gitlab/cycle_analytics/base_event.rb +++ b/lib/gitlab/cycle_analytics/base_event.rb @@ -12,7 +12,7 @@ module Gitlab end def fetch - update_author! if event_result.first['author_id'] + update_author! event_result.map do |event| serialize(event) if has_permission?(event['id']) @@ -28,6 +28,8 @@ module Gitlab private def update_author! + return unless event_result.any? && event_result.first['author_id'] + AuthorUpdater.update!(event_result) end -- cgit v1.2.1 From e56c305be003ee33b093f9ba42622fba0da903f2 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 18 Nov 2016 08:13:34 +0100 Subject: fix spec failures --- spec/lib/gitlab/cycle_analytics/code_event_spec.rb | 8 ++--- .../lib/gitlab/cycle_analytics/issue_event_spec.rb | 8 ++--- spec/lib/gitlab/cycle_analytics/plan_event_spec.rb | 8 ++--- .../cycle_analytics/production_event_spec.rb | 2 +- .../gitlab/cycle_analytics/review_event_spec.rb | 2 +- .../gitlab/cycle_analytics/shared_event_spec.rb | 10 ++++--- .../gitlab/cycle_analytics/staging_event_spec.rb | 8 ++--- spec/lib/gitlab/cycle_analytics/test_event_spec.rb | 8 ++--- .../analytics_generic_serializer_spec.rb | 33 --------------------- .../serializers/analytics_issue_serializer_spec.rb | 33 +++++++++++++++++++++ .../analytics_merge_request_serializer_spec.rb | 34 ++++++++++++++++++++++ 11 files changed, 95 insertions(+), 59 deletions(-) delete mode 100644 spec/serializers/analytics_generic_serializer_spec.rb create mode 100644 spec/serializers/analytics_issue_serializer_spec.rb create mode 100644 spec/serializers/analytics_merge_request_serializer_spec.rb diff --git a/spec/lib/gitlab/cycle_analytics/code_event_spec.rb b/spec/lib/gitlab/cycle_analytics/code_event_spec.rb index 7b854d7fea1..43f42d1bde8 100644 --- a/spec/lib/gitlab/cycle_analytics/code_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/code_event_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' require 'lib/gitlab/cycle_analytics/shared_event_spec' describe Gitlab::CycleAnalytics::CodeEvent do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(described_class.order).not_to eq(described_class.start_time_attrs) + it_behaves_like 'default query config' do + it 'does not have the default order' do + expect(event.order).not_to eq(event.start_time_attrs) + end end end diff --git a/spec/lib/gitlab/cycle_analytics/issue_event_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_event_spec.rb index 2d7e7b1e0a5..1c5c308da7d 100644 --- a/spec/lib/gitlab/cycle_analytics/issue_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/issue_event_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' require 'lib/gitlab/cycle_analytics/shared_event_spec' describe Gitlab::CycleAnalytics::IssueEvent do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(described_class.order).to eq(described_class.start_time_attrs) + it_behaves_like 'default query config' do + it 'has the default order' do + expect(event.order).to eq(event.start_time_attrs) + end end end diff --git a/spec/lib/gitlab/cycle_analytics/plan_event_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_event_spec.rb index 3452c146584..d76a255acf5 100644 --- a/spec/lib/gitlab/cycle_analytics/plan_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/plan_event_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' require 'lib/gitlab/cycle_analytics/shared_event_spec' describe Gitlab::CycleAnalytics::PlanEvent do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(described_class.order).to eq(described_class.start_time_attrs) + it_behaves_like 'default query config' do + it 'has the default order' do + expect(event.order).to eq(event.start_time_attrs) + end end end diff --git a/spec/lib/gitlab/cycle_analytics/production_event_spec.rb b/spec/lib/gitlab/cycle_analytics/production_event_spec.rb index 069c84efa09..6ee54f246a7 100644 --- a/spec/lib/gitlab/cycle_analytics/production_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/production_event_spec.rb @@ -5,6 +5,6 @@ describe Gitlab::CycleAnalytics::ProductionEvent do it_behaves_like 'default query config' it 'has the default order' do - expect(described_class.order).to eq(described_class.start_time_attrs) + expect(event.order).to eq(event.start_time_attrs) end end diff --git a/spec/lib/gitlab/cycle_analytics/review_event_spec.rb b/spec/lib/gitlab/cycle_analytics/review_event_spec.rb index e9aca82ecc9..890e3ab3078 100644 --- a/spec/lib/gitlab/cycle_analytics/review_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/review_event_spec.rb @@ -5,6 +5,6 @@ describe Gitlab::CycleAnalytics::ReviewEvent do it_behaves_like 'default query config' it 'has the default order' do - expect(described_class.order).to eq(described_class.start_time_attrs) + expect(event.order).to eq(event.start_time_attrs) end end diff --git a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb index 1e76edce633..7019e4c3351 100644 --- a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb @@ -1,19 +1,21 @@ require 'spec_helper' shared_examples 'default query config' do + let(:event) { described_class.new(project: double, options: {}) } + it 'has the start attributes' do - expect(described_class.start_time_attrs).not_to be_nil + expect(event.start_time_attrs).not_to be_nil end it 'has the stage attribute' do - expect(described_class.stage).not_to be_nil + expect(event.stage).not_to be_nil end it 'has the end attributes' do - expect(described_class.end_time_attrs).not_to be_nil + expect(event.end_time_attrs).not_to be_nil end it 'has the projection attributes' do - expect(described_class.projections).not_to be_nil + expect(event.projections).not_to be_nil end end diff --git a/spec/lib/gitlab/cycle_analytics/staging_event_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_event_spec.rb index 9c1bec3edb1..4862d4765f2 100644 --- a/spec/lib/gitlab/cycle_analytics/staging_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/staging_event_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' require 'lib/gitlab/cycle_analytics/shared_event_spec' describe Gitlab::CycleAnalytics::StagingEvent do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(described_class.order).not_to eq(described_class.start_time_attrs) + it_behaves_like 'default query config' do + it 'does not have the default order' do + expect(event.order).not_to eq(event.start_time_attrs) + end end end diff --git a/spec/lib/gitlab/cycle_analytics/test_event_spec.rb b/spec/lib/gitlab/cycle_analytics/test_event_spec.rb index 7238b9a34b3..e249db69fc6 100644 --- a/spec/lib/gitlab/cycle_analytics/test_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/test_event_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' require 'lib/gitlab/cycle_analytics/shared_event_spec' describe Gitlab::CycleAnalytics::TestEvent do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(described_class.order).not_to eq(described_class.start_time_attrs) + it_behaves_like 'default query config' do + it 'does not have the default order' do + expect(event.order).not_to eq(event.start_time_attrs) + end end end diff --git a/spec/serializers/analytics_generic_serializer_spec.rb b/spec/serializers/analytics_generic_serializer_spec.rb deleted file mode 100644 index 99f2254d22c..00000000000 --- a/spec/serializers/analytics_generic_serializer_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'spec_helper' - -describe AnalyticsGenericSerializer do - let(:serializer) do - described_class - .new(project: project, entity: :merge_request) - .represent(resource) - end - - let(:user) { create(:user) } - let(:json) { serializer.as_json } - let(:project) { create(:project) } - let(:resource) do - { - total_time: "172802.724419", - title: "Eos voluptatem inventore in sed.", - iid: "1", - id: "1", - created_at: "2016-11-12 15:04:02.948604", - author: user, - } - end - - context 'when there is a single object provided' do - it 'it generates payload for single object' do - expect(json).to be_an_instance_of Hash - end - - it 'contains important elements of analyticsBuild' do - expect(json).to include(:title, :iid, :created_at, :total_time, :url, :author) - end - end -end diff --git a/spec/serializers/analytics_issue_serializer_spec.rb b/spec/serializers/analytics_issue_serializer_spec.rb new file mode 100644 index 00000000000..2842e1ba52f --- /dev/null +++ b/spec/serializers/analytics_issue_serializer_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe AnalyticsIssueSerializer do + let(:serializer) do + described_class + .new(project: project, entity: :merge_request) + .represent(resource) + end + + let(:user) { create(:user) } + let(:json) { serializer.as_json } + let(:project) { create(:project) } + let(:resource) do + { + total_time: "172802.724419", + title: "Eos voluptatem inventore in sed.", + iid: "1", + id: "1", + created_at: "2016-11-12 15:04:02.948604", + author: user, + } + end + + context 'when there is a single object provided' do + it 'it generates payload for single object' do + expect(json).to be_an_instance_of Hash + end + + it 'contains important elements of the issue' do + expect(json).to include(:title, :iid, :created_at, :total_time, :url, :author) + end + end +end diff --git a/spec/serializers/analytics_merge_request_serializer_spec.rb b/spec/serializers/analytics_merge_request_serializer_spec.rb new file mode 100644 index 00000000000..564207984df --- /dev/null +++ b/spec/serializers/analytics_merge_request_serializer_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe AnalyticsMergeRequestSerializer do + let(:serializer) do + described_class + .new(project: project, entity: :merge_request) + .represent(resource) + end + + let(:user) { create(:user) } + let(:json) { serializer.as_json } + let(:project) { create(:project) } + let(:resource) do + { + total_time: "172802.724419", + title: "Eos voluptatem inventore in sed.", + iid: "1", + id: "1", + state: 'open', + created_at: "2016-11-12 15:04:02.948604", + author: user + } + end + + context 'when there is a single object provided' do + it 'it generates payload for single object' do + expect(json).to be_an_instance_of Hash + end + + it 'contains important elements of the merge request' do + expect(json).to include(:title, :iid, :created_at, :total_time, :url, :author, :state) + end + end +end -- cgit v1.2.1 From cd222357ade5f47f8d52f22dea66965c580a5843 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 18 Nov 2016 10:13:42 +0100 Subject: refactor events --- lib/gitlab/cycle_analytics/test_event.rb | 20 +++----------------- .../gitlab/cycle_analytics/production_event_spec.rb | 10 +++++----- spec/lib/gitlab/cycle_analytics/review_event_spec.rb | 8 ++++---- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/lib/gitlab/cycle_analytics/test_event.rb b/lib/gitlab/cycle_analytics/test_event.rb index 00404d7f6b0..d553d0b5aec 100644 --- a/lib/gitlab/cycle_analytics/test_event.rb +++ b/lib/gitlab/cycle_analytics/test_event.rb @@ -1,26 +1,12 @@ module Gitlab module CycleAnalytics - class TestEvent < BaseEvent + class TestEvent < StagingEvent def initialize(*args) + super(*args) + @stage = :test @start_time_attrs = mr_metrics_table[:latest_build_started_at] @end_time_attrs = mr_metrics_table[:latest_build_finished_at] - @projections = [build_table[:id]] - @order = build_table[:created_at] - - super(*args) - end - - def custom_query(base_query) - base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) - end - - private - - def serialize(event) - build = ::Ci::Build.find(event['id']) - - AnalyticsBuildSerializer.new.represent(build).as_json end end end diff --git a/spec/lib/gitlab/cycle_analytics/production_event_spec.rb b/spec/lib/gitlab/cycle_analytics/production_event_spec.rb index 6ee54f246a7..5d12365c430 100644 --- a/spec/lib/gitlab/cycle_analytics/production_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/production_event_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' require 'lib/gitlab/cycle_analytics/shared_event_spec' describe Gitlab::CycleAnalytics::ProductionEvent do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(event.order).to eq(event.start_time_attrs) + it_behaves_like 'default query config' do + it 'has the default order' do + expect(event.order).to eq(event.start_time_attrs) + end end -end +end \ No newline at end of file diff --git a/spec/lib/gitlab/cycle_analytics/review_event_spec.rb b/spec/lib/gitlab/cycle_analytics/review_event_spec.rb index 890e3ab3078..1ff53aa0227 100644 --- a/spec/lib/gitlab/cycle_analytics/review_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/review_event_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' require 'lib/gitlab/cycle_analytics/shared_event_spec' describe Gitlab::CycleAnalytics::ReviewEvent do - it_behaves_like 'default query config' - - it 'has the default order' do - expect(event.order).to eq(event.start_time_attrs) + it_behaves_like 'default query config' do + it 'has the default order' do + expect(event.order).to eq(event.start_time_attrs) + end end end -- cgit v1.2.1 From c76ef847817ac6346b2f367be63f482ee2df382a Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 18 Nov 2016 10:50:47 +0100 Subject: run pipeline worker in cycle analytics dev setup --- db/fixtures/development/17_cycle_analytics.rb | 2 ++ spec/lib/gitlab/cycle_analytics/production_event_spec.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb index e882a492757..916ee8dbac8 100644 --- a/db/fixtures/development/17_cycle_analytics.rb +++ b/db/fixtures/development/17_cycle_analytics.rb @@ -203,6 +203,8 @@ class Gitlab::Seeder::CycleAnalytics pipeline.run! Timecop.travel rand(1..6).hours.from_now pipeline.succeed! + + PipelineMetricsWorker.new.perform(pipeline.id) end end diff --git a/spec/lib/gitlab/cycle_analytics/production_event_spec.rb b/spec/lib/gitlab/cycle_analytics/production_event_spec.rb index 5d12365c430..ac17e3b4287 100644 --- a/spec/lib/gitlab/cycle_analytics/production_event_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/production_event_spec.rb @@ -7,4 +7,4 @@ describe Gitlab::CycleAnalytics::ProductionEvent do expect(event.order).to eq(event.start_time_attrs) end end -end \ No newline at end of file +end -- cgit v1.2.1 From cb353d655bd8802c14a1e12e0fe78a1f7cc2e4ed Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 18 Nov 2016 12:05:29 +0100 Subject: added new build updater, specs and refactored allowed_ids --- .../projects/cycle_analytics/events_controller.rb | 10 ++++++-- lib/gitlab/cycle_analytics/author_updater.rb | 24 +++-------------- lib/gitlab/cycle_analytics/build_updater.rb | 9 +++++++ lib/gitlab/cycle_analytics/code_event.rb | 6 ++--- lib/gitlab/cycle_analytics/issue_allowed.rb | 9 +++++++ lib/gitlab/cycle_analytics/issue_event.rb | 6 ++--- .../cycle_analytics/merge_request_allowed.rb | 9 +++++++ lib/gitlab/cycle_analytics/production_event.rb | 6 ++--- lib/gitlab/cycle_analytics/review_event.rb | 6 ++--- lib/gitlab/cycle_analytics/staging_event.rb | 10 +++++--- lib/gitlab/cycle_analytics/updater.rb | 30 ++++++++++++++++++++++ .../gitlab/cycle_analytics/author_updater_spec.rb | 12 +++++++++ .../gitlab/cycle_analytics/build_updater_spec.rb | 12 +++++++++ 13 files changed, 107 insertions(+), 42 deletions(-) create mode 100644 lib/gitlab/cycle_analytics/build_updater.rb create mode 100644 lib/gitlab/cycle_analytics/issue_allowed.rb create mode 100644 lib/gitlab/cycle_analytics/merge_request_allowed.rb create mode 100644 lib/gitlab/cycle_analytics/updater.rb create mode 100644 spec/lib/gitlab/cycle_analytics/author_updater_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/build_updater_spec.rb diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 8c2fb98a120..d5e98228ddc 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -5,7 +5,9 @@ module Projects before_action :authorize_read_cycle_analytics! before_action :authorize_builds!, only: [:test, :staging] - + before_action :authorize_read_issue!, only: [:issue, :production] + before_action :authorize_read_merge_request!, only: [:code, :review] + def issue render_events(events.issue_events) end @@ -60,7 +62,11 @@ module Projects end def authorize_builds! - return access_denied! unless current_user.can?(:read_build, project) + return access_denied! unless can?(current_user, :read_build, project) + end + + def authorize_read_issue! + return access_denied! unless can?(current_user, :read_issue, project) end end end diff --git a/lib/gitlab/cycle_analytics/author_updater.rb b/lib/gitlab/cycle_analytics/author_updater.rb index ab18f292bc3..87d26701d3a 100644 --- a/lib/gitlab/cycle_analytics/author_updater.rb +++ b/lib/gitlab/cycle_analytics/author_updater.rb @@ -1,26 +1,8 @@ module Gitlab module CycleAnalytics - class AuthorUpdater - def self.update!(*args) - new(*args).update! - end - - def initialize(event_result) - @event_result = event_result - end - - def update! - @event_result.each do |event| - event['author'] = users[event.delete('author_id').to_i].first - end - end - - def user_ids - @event_result.map { |event| event['author_id'] } - end - - def users - @users ||= User.find(user_ids).group_by { |user| user['id'] } + class AuthorUpdater < Updater + def self.update!(event_result) + new(event_result, User, :author).update! end end end diff --git a/lib/gitlab/cycle_analytics/build_updater.rb b/lib/gitlab/cycle_analytics/build_updater.rb new file mode 100644 index 00000000000..c1190d9e7a9 --- /dev/null +++ b/lib/gitlab/cycle_analytics/build_updater.rb @@ -0,0 +1,9 @@ +module Gitlab + module CycleAnalytics + class BuildUpdater < Updater + def self.update!(event_result) + new(event_result, ::Ci::Build, :build, 'id').update! + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/code_event.rb b/lib/gitlab/cycle_analytics/code_event.rb index 02a3a44d544..2afdf0b8518 100644 --- a/lib/gitlab/cycle_analytics/code_event.rb +++ b/lib/gitlab/cycle_analytics/code_event.rb @@ -1,6 +1,8 @@ module Gitlab module CycleAnalytics class CodeEvent < BaseEvent + include MergeRequestAllowed + def initialize(*args) @stage = :code @start_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at] @@ -21,10 +23,6 @@ module Gitlab def serialize(event) AnalyticsMergeRequestSerializer.new(project: @project).represent(event).as_json end - - def allowed_ids - @allowed_ids ||= MergeRequestsFinder.new(@options[:current_user], project_id: @project.id).execute.where(id: event_result_ids).pluck(:id) - end end end end diff --git a/lib/gitlab/cycle_analytics/issue_allowed.rb b/lib/gitlab/cycle_analytics/issue_allowed.rb new file mode 100644 index 00000000000..a7652a70641 --- /dev/null +++ b/lib/gitlab/cycle_analytics/issue_allowed.rb @@ -0,0 +1,9 @@ +module Gitlab + module CycleAnalytics + module IssueAllowed + def allowed_ids + @allowed_ids ||= IssuesFinder.new(@options[:current_user], project_id: @project.id).execute.where(id: event_result_ids).pluck(:id) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/issue_event.rb b/lib/gitlab/cycle_analytics/issue_event.rb index 36a990d6222..705b7e5ce24 100644 --- a/lib/gitlab/cycle_analytics/issue_event.rb +++ b/lib/gitlab/cycle_analytics/issue_event.rb @@ -1,6 +1,8 @@ module Gitlab module CycleAnalytics class IssueEvent < BaseEvent + include IssueAllowed + def initialize(*args) @stage = :issue @start_time_attrs = issue_table[:created_at] @@ -20,10 +22,6 @@ module Gitlab def serialize(event) AnalyticsIssueSerializer.new(project: @project).represent(event).as_json end - - def allowed_ids - @allowed_ids ||= IssuesFinder.new(@options[:current_user], project_id: @project.id).execute.where(id: event_result_ids).pluck(:id) - end end end end diff --git a/lib/gitlab/cycle_analytics/merge_request_allowed.rb b/lib/gitlab/cycle_analytics/merge_request_allowed.rb new file mode 100644 index 00000000000..28f6db44759 --- /dev/null +++ b/lib/gitlab/cycle_analytics/merge_request_allowed.rb @@ -0,0 +1,9 @@ +module Gitlab + module CycleAnalytics + module MergeRequestAllowed + def allowed_ids + @allowed_ids ||= MergeRequestsFinder.new(@options[:current_user], project_id: @project.id).execute.where(id: event_result_ids).pluck(:id) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/production_event.rb b/lib/gitlab/cycle_analytics/production_event.rb index fcf2dbe3490..4868c3c6237 100644 --- a/lib/gitlab/cycle_analytics/production_event.rb +++ b/lib/gitlab/cycle_analytics/production_event.rb @@ -1,6 +1,8 @@ module Gitlab module CycleAnalytics class ProductionEvent < BaseEvent + include IssueAllowed + def initialize(*args) @stage = :production @start_time_attrs = issue_table[:created_at] @@ -19,10 +21,6 @@ module Gitlab def serialize(event) AnalyticsIssueSerializer.new(project: @project).represent(event).as_json end - - def allowed_ids - @allowed_ids ||= IssuesFinder.new(@options[:current_user], project_id: @project.id).execute.where(id: event_result_ids).pluck(:id) - end end end end diff --git a/lib/gitlab/cycle_analytics/review_event.rb b/lib/gitlab/cycle_analytics/review_event.rb index 30650537afe..b394a02cc52 100644 --- a/lib/gitlab/cycle_analytics/review_event.rb +++ b/lib/gitlab/cycle_analytics/review_event.rb @@ -1,6 +1,8 @@ module Gitlab module CycleAnalytics class ReviewEvent < BaseEvent + include MergeRequestAllowed + def initialize(*args) @stage = :review @start_time_attrs = mr_table[:created_at] @@ -18,10 +20,6 @@ module Gitlab def serialize(event) AnalyticsMergeRequestSerializer.new(project: @project).represent(event).as_json end - - def allowed_ids - @allowed_ids ||= MergeRequestsFinder.new(@options[:current_user], project_id: @project.id).execute.where(id: event_result_ids).pluck(:id) - end end end end diff --git a/lib/gitlab/cycle_analytics/staging_event.rb b/lib/gitlab/cycle_analytics/staging_event.rb index 800b2b786dd..6c42b1a3a60 100644 --- a/lib/gitlab/cycle_analytics/staging_event.rb +++ b/lib/gitlab/cycle_analytics/staging_event.rb @@ -11,6 +11,12 @@ module Gitlab super(*args) end + def fetch + BuildUpdater.update!(event_result) + + super + end + def custom_query(base_query) base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) end @@ -18,9 +24,7 @@ module Gitlab private def serialize(event) - build = ::Ci::Build.find(event['id']) - - AnalyticsBuildSerializer.new.represent(build).as_json + AnalyticsBuildSerializer.new.represent(event['build']).as_json end end end diff --git a/lib/gitlab/cycle_analytics/updater.rb b/lib/gitlab/cycle_analytics/updater.rb new file mode 100644 index 00000000000..38cbfa03030 --- /dev/null +++ b/lib/gitlab/cycle_analytics/updater.rb @@ -0,0 +1,30 @@ +module Gitlab + module CycleAnalytics + class Updater + def self.update!(*args) + new(*args).update! + end + + def initialize(event_result, update_klass, update_key, column = nil) + @event_result = event_result + @update_klass = update_klass + @update_key = update_key.to_s + @column = column || "#{@update_key}_id" + end + + def update! + @event_result.each do |event| + event[@update_key] = items[event.delete(@column).to_i].first + end + end + + def result_ids + @event_result.map { |event| event[@column] } + end + + def items + @items ||= @update_klass.find(result_ids).group_by { |item| item['id'] } + end + end + end +end diff --git a/spec/lib/gitlab/cycle_analytics/author_updater_spec.rb b/spec/lib/gitlab/cycle_analytics/author_updater_spec.rb new file mode 100644 index 00000000000..f9e4d1714e6 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/author_updater_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe Gitlab::CycleAnalytics::AuthorUpdater do + let(:user) { create(:user) } + let(:events) { [{ 'author_id' => user.id }] } + + it 'maps the correct user' do + described_class.update!(events) + + expect(events.first['author']).to eq(user) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/build_updater_spec.rb b/spec/lib/gitlab/cycle_analytics/build_updater_spec.rb new file mode 100644 index 00000000000..8e461f6c6ea --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/build_updater_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe Gitlab::CycleAnalytics::BuildUpdater do + let(:build) { create(:ci_build) } + let(:events) { [{ 'id' => build.id }] } + + it 'maps the correct user' do + described_class.update!(events) + + expect(events.first['build']).to eq(build) + end +end -- cgit v1.2.1 From 0aa477884c6ac3298f79f62e08e63294d81735a3 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 18 Nov 2016 12:09:36 +0100 Subject: fix small typo --- spec/lib/gitlab/cycle_analytics/build_updater_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/gitlab/cycle_analytics/build_updater_spec.rb b/spec/lib/gitlab/cycle_analytics/build_updater_spec.rb index 8e461f6c6ea..70886d7b453 100644 --- a/spec/lib/gitlab/cycle_analytics/build_updater_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/build_updater_spec.rb @@ -4,7 +4,7 @@ describe Gitlab::CycleAnalytics::BuildUpdater do let(:build) { create(:ci_build) } let(:events) { [{ 'id' => build.id }] } - it 'maps the correct user' do + it 'maps the correct build' do described_class.update!(events) expect(events.first['build']).to eq(build) -- cgit v1.2.1 From f5b792e22eb7bd4ecafcd2ad3bc7a388abb36ffc Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 18 Nov 2016 13:00:38 +0100 Subject: refactored updater and updated specs --- .../projects/cycle_analytics/events_controller.rb | 10 +-------- lib/gitlab/cycle_analytics/author_updater.rb | 9 -------- lib/gitlab/cycle_analytics/base_event.rb | 2 +- lib/gitlab/cycle_analytics/build_updater.rb | 9 -------- lib/gitlab/cycle_analytics/staging_event.rb | 2 +- lib/gitlab/cycle_analytics/updater.rb | 14 ++++++------ .../gitlab/cycle_analytics/author_updater_spec.rb | 12 ----------- .../gitlab/cycle_analytics/build_updater_spec.rb | 12 ----------- spec/lib/gitlab/cycle_analytics/updater_spec.rb | 25 ++++++++++++++++++++++ 9 files changed, 35 insertions(+), 60 deletions(-) delete mode 100644 lib/gitlab/cycle_analytics/author_updater.rb delete mode 100644 lib/gitlab/cycle_analytics/build_updater.rb delete mode 100644 spec/lib/gitlab/cycle_analytics/author_updater_spec.rb delete mode 100644 spec/lib/gitlab/cycle_analytics/build_updater_spec.rb create mode 100644 spec/lib/gitlab/cycle_analytics/updater_spec.rb diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index d5e98228ddc..13b3eec761f 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -4,7 +4,7 @@ module Projects include CycleAnalyticsParams before_action :authorize_read_cycle_analytics! - before_action :authorize_builds!, only: [:test, :staging] + before_action :authorize_read_build!, only: [:test, :staging] before_action :authorize_read_issue!, only: [:issue, :production] before_action :authorize_read_merge_request!, only: [:code, :review] @@ -60,14 +60,6 @@ module Projects params[:events].slice(:start_date, :branch_name) end - - def authorize_builds! - return access_denied! unless can?(current_user, :read_build, project) - end - - def authorize_read_issue! - return access_denied! unless can?(current_user, :read_issue, project) - end end end end diff --git a/lib/gitlab/cycle_analytics/author_updater.rb b/lib/gitlab/cycle_analytics/author_updater.rb deleted file mode 100644 index 87d26701d3a..00000000000 --- a/lib/gitlab/cycle_analytics/author_updater.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Gitlab - module CycleAnalytics - class AuthorUpdater < Updater - def self.update!(event_result) - new(event_result, User, :author).update! - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb index cedc73142fa..486139b1687 100644 --- a/lib/gitlab/cycle_analytics/base_event.rb +++ b/lib/gitlab/cycle_analytics/base_event.rb @@ -30,7 +30,7 @@ module Gitlab def update_author! return unless event_result.any? && event_result.first['author_id'] - AuthorUpdater.update!(event_result) + Updater.update!(event_result, from: 'author_id', to: 'author', klass: User) end def event_result diff --git a/lib/gitlab/cycle_analytics/build_updater.rb b/lib/gitlab/cycle_analytics/build_updater.rb deleted file mode 100644 index c1190d9e7a9..00000000000 --- a/lib/gitlab/cycle_analytics/build_updater.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Gitlab - module CycleAnalytics - class BuildUpdater < Updater - def self.update!(event_result) - new(event_result, ::Ci::Build, :build, 'id').update! - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/staging_event.rb b/lib/gitlab/cycle_analytics/staging_event.rb index 6c42b1a3a60..a1f30b716f6 100644 --- a/lib/gitlab/cycle_analytics/staging_event.rb +++ b/lib/gitlab/cycle_analytics/staging_event.rb @@ -12,7 +12,7 @@ module Gitlab end def fetch - BuildUpdater.update!(event_result) + Updater.update!(event_result, from: 'id', to: 'build', klass: ::Ci::Build) super end diff --git a/lib/gitlab/cycle_analytics/updater.rb b/lib/gitlab/cycle_analytics/updater.rb index 38cbfa03030..953268ebd46 100644 --- a/lib/gitlab/cycle_analytics/updater.rb +++ b/lib/gitlab/cycle_analytics/updater.rb @@ -5,25 +5,25 @@ module Gitlab new(*args).update! end - def initialize(event_result, update_klass, update_key, column = nil) + def initialize(event_result, from:, to:, klass:) @event_result = event_result - @update_klass = update_klass - @update_key = update_key.to_s - @column = column || "#{@update_key}_id" + @klass = klass + @from = from + @to = to end def update! @event_result.each do |event| - event[@update_key] = items[event.delete(@column).to_i].first + event[@to] = items[event.delete(@from).to_i].first end end def result_ids - @event_result.map { |event| event[@column] } + @event_result.map { |event| event[@from] } end def items - @items ||= @update_klass.find(result_ids).group_by { |item| item['id'] } + @items ||= @klass.find(result_ids).group_by { |item| item['id'] } end end end diff --git a/spec/lib/gitlab/cycle_analytics/author_updater_spec.rb b/spec/lib/gitlab/cycle_analytics/author_updater_spec.rb deleted file mode 100644 index f9e4d1714e6..00000000000 --- a/spec/lib/gitlab/cycle_analytics/author_updater_spec.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'spec_helper' - -describe Gitlab::CycleAnalytics::AuthorUpdater do - let(:user) { create(:user) } - let(:events) { [{ 'author_id' => user.id }] } - - it 'maps the correct user' do - described_class.update!(events) - - expect(events.first['author']).to eq(user) - end -end diff --git a/spec/lib/gitlab/cycle_analytics/build_updater_spec.rb b/spec/lib/gitlab/cycle_analytics/build_updater_spec.rb deleted file mode 100644 index 70886d7b453..00000000000 --- a/spec/lib/gitlab/cycle_analytics/build_updater_spec.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'spec_helper' - -describe Gitlab::CycleAnalytics::BuildUpdater do - let(:build) { create(:ci_build) } - let(:events) { [{ 'id' => build.id }] } - - it 'maps the correct build' do - described_class.update!(events) - - expect(events.first['build']).to eq(build) - end -end diff --git a/spec/lib/gitlab/cycle_analytics/updater_spec.rb b/spec/lib/gitlab/cycle_analytics/updater_spec.rb new file mode 100644 index 00000000000..eff54cd3692 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/updater_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe Gitlab::CycleAnalytics::Updater do + describe 'updates authors' do + let(:user) { create(:user) } + let(:events) { [{ 'author_id' => user.id }] } + + it 'maps the correct user' do + described_class.update!(events, from: 'author_id', to: 'author', klass: User) + + expect(events.first['author']).to eq(user) + end + end + + describe 'updates builds' do + let(:build) { create(:ci_build) } + let(:events) { [{ 'id' => build.id }] } + + it 'maps the correct build' do + described_class.update!(events, from: 'id', to: 'build', klass: ::Ci::Build) + + expect(events.first['build']).to eq(build) + end + end +end -- cgit v1.2.1