From 17196a2ff31c4eb65fa9ecff6f7208171e26059b Mon Sep 17 00:00:00 2001 From: Ruben Davila Date: Fri, 23 Dec 2016 00:44:02 -0500 Subject: Backport backend work for time tracking. --- lib/gitlab/import_export/import_export.yml | 2 ++ lib/gitlab/time_tracking_formatter.rb | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 lib/gitlab/time_tracking_formatter.rb (limited to 'lib') diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml index e6ecd118609..08ad3274b38 100644 --- a/lib/gitlab/import_export/import_export.yml +++ b/lib/gitlab/import_export/import_export.yml @@ -6,6 +6,7 @@ project_tree: - :events - issues: - :events + - :timelogs - notes: - :author - :events @@ -27,6 +28,7 @@ project_tree: - :events - :merge_request_diff - :events + - :timelogs - label_links: - label: :priorities diff --git a/lib/gitlab/time_tracking_formatter.rb b/lib/gitlab/time_tracking_formatter.rb new file mode 100644 index 00000000000..d09063c6c8f --- /dev/null +++ b/lib/gitlab/time_tracking_formatter.rb @@ -0,0 +1,30 @@ +module Gitlab + module TimeTrackingFormatter + extend self + + def parse(string) + with_custom_config do + ChronicDuration.parse(string, default_unit: 'hours') rescue nil + end + end + + def output(seconds) + with_custom_config do + ChronicDuration.output(seconds, format: :short, limit_to_hours: false, weeks: true) rescue nil + end + end + + def with_custom_config + # We may want to configure it through project settings in a future version. + ChronicDuration.hours_per_day = 8 + ChronicDuration.days_per_week = 5 + + result = yield + + ChronicDuration.hours_per_day = 24 + ChronicDuration.days_per_week = 7 + + result + end + end +end -- cgit v1.2.1 From 0c1378e7fc504402b35029e8b0f0ed942e104992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 16 Jan 2017 15:53:42 -0500 Subject: Remove rerun since it's not used anymore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/tasks/dev.rake | 5 ----- 1 file changed, 5 deletions(-) (limited to 'lib') diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 6f27972c4e4..5e94fba97bf 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -7,9 +7,4 @@ namespace :dev do Rake::Task["gitlab:setup"].invoke Rake::Task["gitlab:shell:setup"].invoke end - - desc 'GitLab | Start/restart foreman and watch for changes' - task :foreman => :environment do - sh 'rerun --dir app,config,lib -- foreman start' - end end -- cgit v1.2.1 From 959f65a0f029a4b9bcd510d6596a492f389df315 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 16 Jan 2017 20:35:02 -0500 Subject: Correct documentation for `data_attribute` method Also break up a long line, just 'cause. --- lib/banzai/filter/reference_filter.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb index 84bfeac8041..ab7af1cad21 100644 --- a/lib/banzai/filter/reference_filter.rb +++ b/lib/banzai/filter/reference_filter.rb @@ -20,10 +20,10 @@ module Banzai # Examples: # # data_attribute(project: 1, issue: 2) - # # => "data-reference-filter=\"SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\"" + # # => "data-reference-type=\"SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\"" # # data_attribute(project: 3, merge_request: 4) - # # => "data-reference-filter=\"SomeReferenceFilter\" data-project=\"3\" data-merge-request=\"4\"" + # # => "data-reference-type=\"SomeReferenceFilter\" data-project=\"3\" data-merge-request=\"4\"" # # Returns a String def data_attribute(attributes = {}) @@ -31,7 +31,9 @@ module Banzai attributes[:reference_type] ||= self.class.reference_type attributes.delete(:original) if context[:no_original_data] - attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{escape_once(value)}") }.join(" ") + attributes.map do |key, value| + %Q(data-#{key.to_s.dasherize}="#{escape_once(value)}") + end.join(' ') end def escape_once(html) -- cgit v1.2.1 From 2ac0178564e5593b6f666df79ac1b109dcabaf32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 17 Jan 2017 01:22:01 -0500 Subject: Remove some useless require_relative statements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/git/blame.rb | 2 -- lib/gitlab/git/blob.rb | 3 --- lib/gitlab/git/repository.rb | 2 -- 3 files changed, 7 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/git/blame.rb b/lib/gitlab/git/blame.rb index 2913230e979..58193391926 100644 --- a/lib/gitlab/git/blame.rb +++ b/lib/gitlab/git/blame.rb @@ -1,5 +1,3 @@ -require_relative 'encoding_helper' - module Gitlab module Git class Blame diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb index 4a623311c14..b742d9e1e4b 100644 --- a/lib/gitlab/git/blob.rb +++ b/lib/gitlab/git/blob.rb @@ -1,6 +1,3 @@ -require_relative 'encoding_helper' -require_relative 'path_helper' - module Gitlab module Git class Blob diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 79b23d59b3a..7068e68a855 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1,6 +1,4 @@ # Gitlab::Git::Repository is a wrapper around native Rugged::Repository object -require_relative 'encoding_helper' -require_relative 'path_helper' require 'forwardable' require 'tempfile' require 'forwardable' -- cgit v1.2.1 From 3268e3779166c7ddf47ecc0d78397cd75cf2f0e8 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 18 Nov 2016 16:38:02 +0100 Subject: WIP - started refactoring cycle analytics median stuff into stages --- lib/gitlab/cycle_analytics/base_event.rb | 8 ++++---- lib/gitlab/cycle_analytics/base_stage.rb | 24 ++++++++++++++++++++++++ lib/gitlab/cycle_analytics/code_event.rb | 1 - lib/gitlab/cycle_analytics/code_stage.rb | 11 +++++++++++ lib/gitlab/cycle_analytics/events_query.rb | 9 ++------- lib/gitlab/cycle_analytics/issue_event.rb | 1 - lib/gitlab/cycle_analytics/issue_stage.rb | 12 ++++++++++++ lib/gitlab/cycle_analytics/metrics_fetcher.rb | 3 ++- lib/gitlab/cycle_analytics/plan_event.rb | 1 - lib/gitlab/cycle_analytics/plan_stage.rb | 12 ++++++++++++ lib/gitlab/cycle_analytics/production_event.rb | 1 - lib/gitlab/cycle_analytics/production_stage.rb | 11 +++++++++++ lib/gitlab/cycle_analytics/review_event.rb | 1 - lib/gitlab/cycle_analytics/review_stage.rb | 11 +++++++++++ lib/gitlab/cycle_analytics/staging_event.rb | 1 - lib/gitlab/cycle_analytics/staging_stage.rb | 11 +++++++++++ lib/gitlab/cycle_analytics/test_event.rb | 1 - lib/gitlab/cycle_analytics/test_stage.rb | 11 +++++++++++ 18 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 lib/gitlab/cycle_analytics/base_stage.rb create mode 100644 lib/gitlab/cycle_analytics/code_stage.rb create mode 100644 lib/gitlab/cycle_analytics/issue_stage.rb create mode 100644 lib/gitlab/cycle_analytics/plan_stage.rb create mode 100644 lib/gitlab/cycle_analytics/production_stage.rb create mode 100644 lib/gitlab/cycle_analytics/review_stage.rb create mode 100644 lib/gitlab/cycle_analytics/staging_stage.rb create mode 100644 lib/gitlab/cycle_analytics/test_stage.rb (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb index 53a148ad703..c87841c119a 100644 --- a/lib/gitlab/cycle_analytics/base_event.rb +++ b/lib/gitlab/cycle_analytics/base_event.rb @@ -5,10 +5,10 @@ module Gitlab attr_reader :stage, :start_time_attrs, :end_time_attrs, :projections, :query - def initialize(project:, options:) - @query = EventsQuery.new(project: project, options: options) - @project = project - @options = options + def initialize(fetcher:, stage:) + @query = EventsQuery.new(fetcher: fetcher) + @project = fetcher.project + @stage = stage end def fetch diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb new file mode 100644 index 00000000000..70f1e1018c9 --- /dev/null +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -0,0 +1,24 @@ +module Gitlab + module CycleAnalytics + class BaseStage + def initialize(project:, options:, stage: stage) + @project = project + @options = options + @fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, + from: options[:from], + branch: options[:branch]) + @stage = stage + end + + def events + event_class.new(fetcher: @fetcher, stage: @stage).fetch + end + + private + + def event_class + "Gitlab::CycleAnalytics::#{@stage.to_s.capitalize}Event".constantize + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/code_event.rb b/lib/gitlab/cycle_analytics/code_event.rb index 2afdf0b8518..68251630e08 100644 --- a/lib/gitlab/cycle_analytics/code_event.rb +++ b/lib/gitlab/cycle_analytics/code_event.rb @@ -4,7 +4,6 @@ module Gitlab include MergeRequestAllowed 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], diff --git a/lib/gitlab/cycle_analytics/code_stage.rb b/lib/gitlab/cycle_analytics/code_stage.rb new file mode 100644 index 00000000000..9d28393ce53 --- /dev/null +++ b/lib/gitlab/cycle_analytics/code_stage.rb @@ -0,0 +1,11 @@ +module Gitlab + module CycleAnalytics + class CodeStage < BaseStage + def median + @fetcher.calculate_metric(:code, + Issue::Metrics.arel_table[:first_mentioned_in_commit_at], + MergeRequest.arel_table[:created_at]) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/events_query.rb b/lib/gitlab/cycle_analytics/events_query.rb index 2418832ccc2..e2b79384c9b 100644 --- a/lib/gitlab/cycle_analytics/events_query.rb +++ b/lib/gitlab/cycle_analytics/events_query.rb @@ -1,13 +1,8 @@ module Gitlab module CycleAnalytics class EventsQuery - 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) + def initialize(fetcher:) + @fetcher = fetcher end def execute(stage_class) diff --git a/lib/gitlab/cycle_analytics/issue_event.rb b/lib/gitlab/cycle_analytics/issue_event.rb index 705b7e5ce24..76e8decf36e 100644 --- a/lib/gitlab/cycle_analytics/issue_event.rb +++ b/lib/gitlab/cycle_analytics/issue_event.rb @@ -4,7 +4,6 @@ module Gitlab include IssueAllowed 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]] diff --git a/lib/gitlab/cycle_analytics/issue_stage.rb b/lib/gitlab/cycle_analytics/issue_stage.rb new file mode 100644 index 00000000000..6793cc77976 --- /dev/null +++ b/lib/gitlab/cycle_analytics/issue_stage.rb @@ -0,0 +1,12 @@ +module Gitlab + module CycleAnalytics + class IssueStage < BaseStage + def median + @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 + end + end +end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index b71e8735e27..51835bbde24 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -5,10 +5,11 @@ module Gitlab include Gitlab::Database::DateTime include MetricsTables + attr_reader :project + DEPLOYMENT_METRIC_STAGES = %i[production staging] def initialize(project:, from:, branch:) - @project = project @project = project @from = from @branch = branch diff --git a/lib/gitlab/cycle_analytics/plan_event.rb b/lib/gitlab/cycle_analytics/plan_event.rb index 7c3f0e9989f..4b06143495b 100644 --- a/lib/gitlab/cycle_analytics/plan_event.rb +++ b/lib/gitlab/cycle_analytics/plan_event.rb @@ -2,7 +2,6 @@ module Gitlab module CycleAnalytics class PlanEvent < BaseEvent 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]] diff --git a/lib/gitlab/cycle_analytics/plan_stage.rb b/lib/gitlab/cycle_analytics/plan_stage.rb new file mode 100644 index 00000000000..772237087c0 --- /dev/null +++ b/lib/gitlab/cycle_analytics/plan_stage.rb @@ -0,0 +1,12 @@ +module Gitlab + module CycleAnalytics + class PlanStage < BaseStage + def median + @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 + end + end +end diff --git a/lib/gitlab/cycle_analytics/production_event.rb b/lib/gitlab/cycle_analytics/production_event.rb index 4868c3c6237..c03cd4f4909 100644 --- a/lib/gitlab/cycle_analytics/production_event.rb +++ b/lib/gitlab/cycle_analytics/production_event.rb @@ -4,7 +4,6 @@ module Gitlab include IssueAllowed 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], diff --git a/lib/gitlab/cycle_analytics/production_stage.rb b/lib/gitlab/cycle_analytics/production_stage.rb new file mode 100644 index 00000000000..2fb087a8cac --- /dev/null +++ b/lib/gitlab/cycle_analytics/production_stage.rb @@ -0,0 +1,11 @@ +module Gitlab + module CycleAnalytics + class ProductionStage < BaseStage + def median + @fetcher.calculate_metric(:production, + Issue.arel_table[:created_at], + MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/review_event.rb b/lib/gitlab/cycle_analytics/review_event.rb index b394a02cc52..3f9ffa9657b 100644 --- a/lib/gitlab/cycle_analytics/review_event.rb +++ b/lib/gitlab/cycle_analytics/review_event.rb @@ -4,7 +4,6 @@ module Gitlab include MergeRequestAllowed def initialize(*args) - @stage = :review @start_time_attrs = mr_table[:created_at] @end_time_attrs = mr_metrics_table[:merged_at] @projections = [mr_table[:title], diff --git a/lib/gitlab/cycle_analytics/review_stage.rb b/lib/gitlab/cycle_analytics/review_stage.rb new file mode 100644 index 00000000000..ec9f07319e8 --- /dev/null +++ b/lib/gitlab/cycle_analytics/review_stage.rb @@ -0,0 +1,11 @@ +module Gitlab + module CycleAnalytics + class ReviewStage < BaseStage + def median + @fetcher.calculate_metric(:review, + MergeRequest.arel_table[:created_at], + MergeRequest::Metrics.arel_table[:merged_at]) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/staging_event.rb b/lib/gitlab/cycle_analytics/staging_event.rb index a1f30b716f6..eae18b447f0 100644 --- a/lib/gitlab/cycle_analytics/staging_event.rb +++ b/lib/gitlab/cycle_analytics/staging_event.rb @@ -2,7 +2,6 @@ module Gitlab module CycleAnalytics class StagingEvent < BaseEvent 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]] diff --git a/lib/gitlab/cycle_analytics/staging_stage.rb b/lib/gitlab/cycle_analytics/staging_stage.rb new file mode 100644 index 00000000000..9c67a2aa6fe --- /dev/null +++ b/lib/gitlab/cycle_analytics/staging_stage.rb @@ -0,0 +1,11 @@ +module Gitlab + module CycleAnalytics + class StagingStage < BaseStage + def median + @fetcher.calculate_metric(:staging, + MergeRequest::Metrics.arel_table[:merged_at], + MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/test_event.rb b/lib/gitlab/cycle_analytics/test_event.rb index d553d0b5aec..d0736672adf 100644 --- a/lib/gitlab/cycle_analytics/test_event.rb +++ b/lib/gitlab/cycle_analytics/test_event.rb @@ -4,7 +4,6 @@ module Gitlab 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] end diff --git a/lib/gitlab/cycle_analytics/test_stage.rb b/lib/gitlab/cycle_analytics/test_stage.rb new file mode 100644 index 00000000000..6bedfdbba61 --- /dev/null +++ b/lib/gitlab/cycle_analytics/test_stage.rb @@ -0,0 +1,11 @@ +module Gitlab + module CycleAnalytics + class TestStage < BaseStage + def median + @fetcher.calculate_metric(:test, + MergeRequest::Metrics.arel_table[:latest_build_started_at], + MergeRequest::Metrics.arel_table[:latest_build_finished_at]) + end + end + end +end -- cgit v1.2.1 From a998276223510ceee67f686dfc3ef536c0252c5a Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 21 Nov 2016 17:15:25 +0100 Subject: added analytics stage serializer and moved some info to the stage classes from the controller --- lib/gitlab/cycle_analytics/base_stage.rb | 2 ++ lib/gitlab/cycle_analytics/code_stage.rb | 6 ++++++ lib/gitlab/cycle_analytics/issue_stage.rb | 6 ++++++ lib/gitlab/cycle_analytics/plan_stage.rb | 6 ++++++ lib/gitlab/cycle_analytics/production_stage.rb | 6 ++++++ lib/gitlab/cycle_analytics/review_stage.rb | 6 ++++++ lib/gitlab/cycle_analytics/staging_stage.rb | 6 ++++++ lib/gitlab/cycle_analytics/test_stage.rb | 6 ++++++ 8 files changed, 44 insertions(+) (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index 70f1e1018c9..49d1e6304a9 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -1,6 +1,8 @@ module Gitlab module CycleAnalytics class BaseStage + attr_reader :stage, :description + def initialize(project:, options:, stage: stage) @project = project @options = options diff --git a/lib/gitlab/cycle_analytics/code_stage.rb b/lib/gitlab/cycle_analytics/code_stage.rb index 9d28393ce53..f72989c9a72 100644 --- a/lib/gitlab/cycle_analytics/code_stage.rb +++ b/lib/gitlab/cycle_analytics/code_stage.rb @@ -1,6 +1,12 @@ module Gitlab module CycleAnalytics class CodeStage < BaseStage + def initialize(*args) + super(*args) + + @description = "Time until first merge request" + end + def median @fetcher.calculate_metric(:code, Issue::Metrics.arel_table[:first_mentioned_in_commit_at], diff --git a/lib/gitlab/cycle_analytics/issue_stage.rb b/lib/gitlab/cycle_analytics/issue_stage.rb index 6793cc77976..a2ada238cd2 100644 --- a/lib/gitlab/cycle_analytics/issue_stage.rb +++ b/lib/gitlab/cycle_analytics/issue_stage.rb @@ -1,6 +1,12 @@ module Gitlab module CycleAnalytics class IssueStage < BaseStage + def initialize(*args) + super(*args) + + @description = "Time before an issue gets scheduled" + end + def median @fetcher.calculate_metric(:issue, Issue.arel_table[:created_at], diff --git a/lib/gitlab/cycle_analytics/plan_stage.rb b/lib/gitlab/cycle_analytics/plan_stage.rb index 772237087c0..c836068c4ef 100644 --- a/lib/gitlab/cycle_analytics/plan_stage.rb +++ b/lib/gitlab/cycle_analytics/plan_stage.rb @@ -1,6 +1,12 @@ module Gitlab module CycleAnalytics class PlanStage < BaseStage + def initialize(*args) + super(*args) + + @description = "Time before an issue starts implementation" + end + def median @fetcher.calculate_metric(:plan, [Issue::Metrics.arel_table[:first_associated_with_milestone_at], diff --git a/lib/gitlab/cycle_analytics/production_stage.rb b/lib/gitlab/cycle_analytics/production_stage.rb index 2fb087a8cac..d46d37e1acc 100644 --- a/lib/gitlab/cycle_analytics/production_stage.rb +++ b/lib/gitlab/cycle_analytics/production_stage.rb @@ -1,6 +1,12 @@ module Gitlab module CycleAnalytics class ProductionStage < BaseStage + def initialize(*args) + super(*args) + + @description = "From issue creation until deploy to production" + end + def median @fetcher.calculate_metric(:production, Issue.arel_table[:created_at], diff --git a/lib/gitlab/cycle_analytics/review_stage.rb b/lib/gitlab/cycle_analytics/review_stage.rb index ec9f07319e8..4159ba5d70d 100644 --- a/lib/gitlab/cycle_analytics/review_stage.rb +++ b/lib/gitlab/cycle_analytics/review_stage.rb @@ -1,6 +1,12 @@ module Gitlab module CycleAnalytics class ReviewStage < BaseStage + def initialize(*args) + super(*args) + + @description = "Time between merge request creation and merge/close" + end + def median @fetcher.calculate_metric(:review, MergeRequest.arel_table[:created_at], diff --git a/lib/gitlab/cycle_analytics/staging_stage.rb b/lib/gitlab/cycle_analytics/staging_stage.rb index 9c67a2aa6fe..cb4398f15ac 100644 --- a/lib/gitlab/cycle_analytics/staging_stage.rb +++ b/lib/gitlab/cycle_analytics/staging_stage.rb @@ -1,6 +1,12 @@ module Gitlab module CycleAnalytics class StagingStage < BaseStage + def initialize(*args) + super(*args) + + @description = "From merge request merge until deploy to production" + end + def median @fetcher.calculate_metric(:staging, MergeRequest::Metrics.arel_table[:merged_at], diff --git a/lib/gitlab/cycle_analytics/test_stage.rb b/lib/gitlab/cycle_analytics/test_stage.rb index 6bedfdbba61..3ab93bebd87 100644 --- a/lib/gitlab/cycle_analytics/test_stage.rb +++ b/lib/gitlab/cycle_analytics/test_stage.rb @@ -1,6 +1,12 @@ module Gitlab module CycleAnalytics class TestStage < BaseStage + def initialize(*args) + super(*args) + + @description = "Total test time for all commits/merges" + end + def median @fetcher.calculate_metric(:test, MergeRequest::Metrics.arel_table[:latest_build_started_at], -- cgit v1.2.1 From dc6ea14b0d11a5e73e81c95ef723f0c1af69215b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Nov 2016 10:33:19 +0100 Subject: fixed stage entity and added missing stage specs --- lib/gitlab/cycle_analytics/base_stage.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index 49d1e6304a9..27971bfc093 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -3,7 +3,7 @@ module Gitlab class BaseStage attr_reader :stage, :description - def initialize(project:, options:, stage: stage) + def initialize(project:, options:, stage:) @project = project @options = options @fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, @@ -16,6 +16,10 @@ module Gitlab event_class.new(fetcher: @fetcher, stage: @stage).fetch end + def median_data + AnalyticsStageSerializer.new.represent(self).as_json + end + private def event_class -- cgit v1.2.1 From fc6f8f20562ad761c034ffff076d329a3e9e8f4d Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Nov 2016 11:46:02 +0100 Subject: added new summary serializers and refactor all of the summary stuff into separate logical classes --- lib/gitlab/cycle_analytics/summary.rb | 36 ++++++++++++++++++++++++++++ lib/gitlab/cycle_analytics/summary/base.rb | 20 ++++++++++++++++ lib/gitlab/cycle_analytics/summary/commit.rb | 36 ++++++++++++++++++++++++++++ lib/gitlab/cycle_analytics/summary/deploy.rb | 11 +++++++++ lib/gitlab/cycle_analytics/summary/issue.rb | 15 ++++++++++++ 5 files changed, 118 insertions(+) create mode 100644 lib/gitlab/cycle_analytics/summary.rb create mode 100644 lib/gitlab/cycle_analytics/summary/base.rb create mode 100644 lib/gitlab/cycle_analytics/summary/commit.rb create mode 100644 lib/gitlab/cycle_analytics/summary/deploy.rb create mode 100644 lib/gitlab/cycle_analytics/summary/issue.rb (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/summary.rb b/lib/gitlab/cycle_analytics/summary.rb new file mode 100644 index 00000000000..7d172855a94 --- /dev/null +++ b/lib/gitlab/cycle_analytics/summary.rb @@ -0,0 +1,36 @@ +module Gitlab + module CycleAnalytics + module Summary + extend self + + def initialize(project, from:) + @project = project + @from = from + end + + def data + [serialize(issue), + serialize(commit), + serialize(deploy)] + end + + private + + def serialize(summary_object) + AnalyticsSummarySerializer.new.represent(summary_object).as_json + end + + def issue + Summary::Issue.new(project: @project, from: @from) + end + + def deploy + Summary::Deploy.new(project: @project, from: @from) + end + + def commit + Summary::Commit.new(project: @project, from: @from) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/summary/base.rb b/lib/gitlab/cycle_analytics/summary/base.rb new file mode 100644 index 00000000000..1bc4ff00b99 --- /dev/null +++ b/lib/gitlab/cycle_analytics/summary/base.rb @@ -0,0 +1,20 @@ +module Gitlab + module CycleAnalytics + module Summary + class Base + def initialize(project:, from:) + @project = project + @from = from + end + + def title + self.name + end + + def value + raise NotImplementedError.new("Expected #{self.name} to implement value") + end + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/summary/commit.rb b/lib/gitlab/cycle_analytics/summary/commit.rb new file mode 100644 index 00000000000..ec3c067c0be --- /dev/null +++ b/lib/gitlab/cycle_analytics/summary/commit.rb @@ -0,0 +1,36 @@ +module Gitlab + module CycleAnalytics + module Summary + class Commit < Base + def value + @value ||= count_commits + end + + private + + # Don't use the `Gitlab::Git::Repository#log` method, because it enforces + # a limit. Since we need a commit count, we _can't_ enforce a limit, so + # the easiest way forward is to replicate the relevant portions of the + # `log` function here. + def count_commits + return unless ref + + repository = @project.repository.raw_repository + sha = @project.repository.commit(ref).sha + + cmd = %W(git --git-dir=#{repository.path} log) + cmd << '--format=%H' + cmd << "--after=#{@from.iso8601}" + cmd << sha + + raw_output = IO.popen(cmd) { |io| io.read } + raw_output.lines.count + end + + def ref + @ref ||= @project.default_branch.presence + end + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/summary/deploy.rb b/lib/gitlab/cycle_analytics/summary/deploy.rb new file mode 100644 index 00000000000..06032e9200e --- /dev/null +++ b/lib/gitlab/cycle_analytics/summary/deploy.rb @@ -0,0 +1,11 @@ +module Gitlab + module CycleAnalytics + module Summary + class Deploy < Base + def value + @value ||= @project.deployments.where("created_at > ?", @from).count + end + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/summary/issue.rb b/lib/gitlab/cycle_analytics/summary/issue.rb new file mode 100644 index 00000000000..7d62164aae3 --- /dev/null +++ b/lib/gitlab/cycle_analytics/summary/issue.rb @@ -0,0 +1,15 @@ +module Gitlab + module CycleAnalytics + module Summary + class Issue < Base + def title + 'New Issue' + end + + def value + @value ||= @project.issues.created_after(@from).count + end + end + end + end +end -- cgit v1.2.1 From 02e1e4819234662faddd7d8eb5c54d9bfdf9e7e6 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 22 Nov 2016 14:29:25 +0100 Subject: more refactoring and fixing old specs --- lib/gitlab/cycle_analytics/summary.rb | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/summary.rb b/lib/gitlab/cycle_analytics/summary.rb index 7d172855a94..5f0103c9d5a 100644 --- a/lib/gitlab/cycle_analytics/summary.rb +++ b/lib/gitlab/cycle_analytics/summary.rb @@ -9,9 +9,9 @@ module Gitlab end def data - [serialize(issue), - serialize(commit), - serialize(deploy)] + [serialize(Summary::Issue.new(project: @project, from: @from)), + serialize(Summary::Commit.new(project: @project, from: @from)), + serialize(Summary::Deploy.new(project: @project, from: @from))] end private @@ -19,18 +19,6 @@ module Gitlab def serialize(summary_object) AnalyticsSummarySerializer.new.represent(summary_object).as_json end - - def issue - Summary::Issue.new(project: @project, from: @from) - end - - def deploy - Summary::Deploy.new(project: @project, from: @from) - end - - def commit - Summary::Commit.new(project: @project, from: @from) - end end end end -- cgit v1.2.1 From a67311cb4c9f54af43d300fde5240f9a370193d1 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 23 Nov 2016 11:28:28 +0100 Subject: Fix other spec failures --- lib/gitlab/cycle_analytics/base_event.rb | 3 ++- lib/gitlab/cycle_analytics/base_stage.rb | 2 +- lib/gitlab/cycle_analytics/events.rb | 38 ----------------------------- lib/gitlab/cycle_analytics/stage_summary.rb | 22 +++++++++++++++++ lib/gitlab/cycle_analytics/summary.rb | 24 ------------------ lib/gitlab/cycle_analytics/summary/base.rb | 2 +- 6 files changed, 26 insertions(+), 65 deletions(-) delete mode 100644 lib/gitlab/cycle_analytics/events.rb create mode 100644 lib/gitlab/cycle_analytics/stage_summary.rb delete mode 100644 lib/gitlab/cycle_analytics/summary.rb (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb index c87841c119a..d540cb6549c 100644 --- a/lib/gitlab/cycle_analytics/base_event.rb +++ b/lib/gitlab/cycle_analytics/base_event.rb @@ -5,10 +5,11 @@ module Gitlab attr_reader :stage, :start_time_attrs, :end_time_attrs, :projections, :query - def initialize(fetcher:, stage:) + def initialize(fetcher:, stage:, options:) @query = EventsQuery.new(fetcher: fetcher) @project = fetcher.project @stage = stage + @options = options end def fetch diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index 27971bfc093..162ebf18c77 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -13,7 +13,7 @@ module Gitlab end def events - event_class.new(fetcher: @fetcher, stage: @stage).fetch + event_class.new(fetcher: @fetcher, stage: @stage, options: @options).fetch end def median_data diff --git a/lib/gitlab/cycle_analytics/events.rb b/lib/gitlab/cycle_analytics/events.rb deleted file mode 100644 index 2d703d76cbb..00000000000 --- a/lib/gitlab/cycle_analytics/events.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Gitlab - module CycleAnalytics - class Events - def initialize(project:, options:) - @project = project - @options = options - end - - def issue_events - IssueEvent.new(project: @project, options: @options).fetch - end - - def plan_events - PlanEvent.new(project: @project, options: @options).fetch - end - - def code_events - CodeEvent.new(project: @project, options: @options).fetch - end - - def test_events - TestEvent.new(project: @project, options: @options).fetch - end - - def review_events - ReviewEvent.new(project: @project, options: @options).fetch - end - - def staging_events - StagingEvent.new(project: @project, options: @options).fetch - end - - def production_events - ProductionEvent.new(project: @project, options: @options).fetch - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/stage_summary.rb b/lib/gitlab/cycle_analytics/stage_summary.rb new file mode 100644 index 00000000000..dd9e4ac2813 --- /dev/null +++ b/lib/gitlab/cycle_analytics/stage_summary.rb @@ -0,0 +1,22 @@ +module Gitlab + module CycleAnalytics + class StageSummary + def initialize(project, from:) + @project = project + @from = from + end + + def data + [serialize(Summary::Issue.new(project: @project, from: @from)), + serialize(Summary::Commit.new(project: @project, from: @from)), + serialize(Summary::Deploy.new(project: @project, from: @from))] + end + + private + + def serialize(summary_object) + AnalyticsSummarySerializer.new.represent(summary_object).as_json + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/summary.rb b/lib/gitlab/cycle_analytics/summary.rb deleted file mode 100644 index 5f0103c9d5a..00000000000 --- a/lib/gitlab/cycle_analytics/summary.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Gitlab - module CycleAnalytics - module Summary - extend self - - def initialize(project, from:) - @project = project - @from = from - end - - def data - [serialize(Summary::Issue.new(project: @project, from: @from)), - serialize(Summary::Commit.new(project: @project, from: @from)), - serialize(Summary::Deploy.new(project: @project, from: @from))] - end - - private - - def serialize(summary_object) - AnalyticsSummarySerializer.new.represent(summary_object).as_json - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/summary/base.rb b/lib/gitlab/cycle_analytics/summary/base.rb index 1bc4ff00b99..43fa3795e5c 100644 --- a/lib/gitlab/cycle_analytics/summary/base.rb +++ b/lib/gitlab/cycle_analytics/summary/base.rb @@ -8,7 +8,7 @@ module Gitlab end def title - self.name + self.class.name.demodulize end def value -- cgit v1.2.1 From b8056669849729cab5700466a7fae6dc6b2743b2 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 1 Dec 2016 11:21:24 +0100 Subject: refactor cycle analytics - updated based on MR feedback --- lib/gitlab/cycle_analytics/base_event.rb | 10 +++++++--- lib/gitlab/cycle_analytics/base_stage.rb | 19 +++++++++++++------ lib/gitlab/cycle_analytics/class_name_util.rb | 13 +++++++++++++ lib/gitlab/cycle_analytics/code_stage.rb | 12 +++++------- lib/gitlab/cycle_analytics/event.rb | 9 +++++++++ lib/gitlab/cycle_analytics/issue_stage.rb | 14 ++++++-------- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 2 +- lib/gitlab/cycle_analytics/plan_stage.rb | 14 ++++++-------- lib/gitlab/cycle_analytics/production_stage.rb | 12 +++++------- lib/gitlab/cycle_analytics/review_stage.rb | 12 +++++------- lib/gitlab/cycle_analytics/stage.rb | 9 +++++++++ lib/gitlab/cycle_analytics/staging_stage.rb | 12 +++++------- lib/gitlab/cycle_analytics/test_stage.rb | 12 +++++------- 13 files changed, 89 insertions(+), 61 deletions(-) create mode 100644 lib/gitlab/cycle_analytics/class_name_util.rb create mode 100644 lib/gitlab/cycle_analytics/event.rb create mode 100644 lib/gitlab/cycle_analytics/stage.rb (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb index d540cb6549c..eac807af037 100644 --- a/lib/gitlab/cycle_analytics/base_event.rb +++ b/lib/gitlab/cycle_analytics/base_event.rb @@ -2,13 +2,13 @@ module Gitlab module CycleAnalytics class BaseEvent include MetricsTables + include ClassNameUtil - attr_reader :stage, :start_time_attrs, :end_time_attrs, :projections, :query + attr_reader :start_time_attrs, :end_time_attrs, :projections, :query - def initialize(fetcher:, stage:, options:) + def initialize(fetcher:, options:) @query = EventsQuery.new(fetcher: fetcher) @project = fetcher.project - @stage = stage @options = options end @@ -26,6 +26,10 @@ module Gitlab @order || @start_time_attrs end + def stage + class_name_for('Event') + end + private def update_author! diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index 162ebf18c77..551318f536a 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -1,29 +1,36 @@ module Gitlab module CycleAnalytics class BaseStage - attr_reader :stage, :description + include ClassNameUtil - def initialize(project:, options:, stage:) + def initialize(project:, options:) @project = project @options = options @fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, from: options[:from], branch: options[:branch]) - @stage = stage end def events - event_class.new(fetcher: @fetcher, stage: @stage, options: @options).fetch + Gitlab::CycleAnalytics::Event[stage].new(fetcher: @fetcher, options: @options).fetch end def median_data AnalyticsStageSerializer.new.represent(self).as_json end + def title + stage.to_s.capitalize + end + + def median + raise NotImplementedError.new("Expected #{self.name} to implement median") + end + private - def event_class - "Gitlab::CycleAnalytics::#{@stage.to_s.capitalize}Event".constantize + def stage + class_name_for('Stage') end end end diff --git a/lib/gitlab/cycle_analytics/class_name_util.rb b/lib/gitlab/cycle_analytics/class_name_util.rb new file mode 100644 index 00000000000..aac8d888077 --- /dev/null +++ b/lib/gitlab/cycle_analytics/class_name_util.rb @@ -0,0 +1,13 @@ +module Gitlab + module CycleAnalytics + module ClassNameUtil + def class_name_for(type) + class_name.split(type).first.to_sym + end + + def class_name + self.class.name.demodulize + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/code_stage.rb b/lib/gitlab/cycle_analytics/code_stage.rb index f72989c9a72..778ce7b5435 100644 --- a/lib/gitlab/cycle_analytics/code_stage.rb +++ b/lib/gitlab/cycle_analytics/code_stage.rb @@ -1,16 +1,14 @@ module Gitlab module CycleAnalytics class CodeStage < BaseStage - def initialize(*args) - super(*args) - - @description = "Time until first merge request" + def description + "Time until first merge request" end def median - @fetcher.calculate_metric(:code, - Issue::Metrics.arel_table[:first_mentioned_in_commit_at], - MergeRequest.arel_table[:created_at]) + @fetcher.median(:code, + Issue::Metrics.arel_table[:first_mentioned_in_commit_at], + MergeRequest.arel_table[:created_at]) end end end diff --git a/lib/gitlab/cycle_analytics/event.rb b/lib/gitlab/cycle_analytics/event.rb new file mode 100644 index 00000000000..62fac89a0d5 --- /dev/null +++ b/lib/gitlab/cycle_analytics/event.rb @@ -0,0 +1,9 @@ +module Gitlab + module CycleAnalytics + module Event + def self.[](stage_name) + const_get("::Gitlab::CycleAnalytics::#{stage_name.to_s.camelize}Event") + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/issue_stage.rb b/lib/gitlab/cycle_analytics/issue_stage.rb index a2ada238cd2..c317872fb1d 100644 --- a/lib/gitlab/cycle_analytics/issue_stage.rb +++ b/lib/gitlab/cycle_analytics/issue_stage.rb @@ -1,17 +1,15 @@ module Gitlab module CycleAnalytics class IssueStage < BaseStage - def initialize(*args) - super(*args) - - @description = "Time before an issue gets scheduled" + def description + "Time before an issue gets scheduled" end def median - @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]]) + @fetcher.median(: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 end end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index 51835bbde24..bd68a0980ca 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -15,7 +15,7 @@ module Gitlab @branch = branch end - def calculate_metric(name, start_time_attrs, end_time_attrs) + def median(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). diff --git a/lib/gitlab/cycle_analytics/plan_stage.rb b/lib/gitlab/cycle_analytics/plan_stage.rb index c836068c4ef..5e6dd30d9e3 100644 --- a/lib/gitlab/cycle_analytics/plan_stage.rb +++ b/lib/gitlab/cycle_analytics/plan_stage.rb @@ -1,17 +1,15 @@ module Gitlab module CycleAnalytics class PlanStage < BaseStage - def initialize(*args) - super(*args) - - @description = "Time before an issue starts implementation" + def description + "Time before an issue starts implementation" end def median - @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]) + @fetcher.median(: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 end end diff --git a/lib/gitlab/cycle_analytics/production_stage.rb b/lib/gitlab/cycle_analytics/production_stage.rb index d46d37e1acc..acd2f7b2b3b 100644 --- a/lib/gitlab/cycle_analytics/production_stage.rb +++ b/lib/gitlab/cycle_analytics/production_stage.rb @@ -1,16 +1,14 @@ module Gitlab module CycleAnalytics class ProductionStage < BaseStage - def initialize(*args) - super(*args) - - @description = "From issue creation until deploy to production" + def description + "From issue creation until deploy to production" end def median - @fetcher.calculate_metric(:production, - Issue.arel_table[:created_at], - MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) + @fetcher.median(:production, + Issue.arel_table[:created_at], + MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) end end end diff --git a/lib/gitlab/cycle_analytics/review_stage.rb b/lib/gitlab/cycle_analytics/review_stage.rb index 4159ba5d70d..c7b5e34e16a 100644 --- a/lib/gitlab/cycle_analytics/review_stage.rb +++ b/lib/gitlab/cycle_analytics/review_stage.rb @@ -1,16 +1,14 @@ module Gitlab module CycleAnalytics class ReviewStage < BaseStage - def initialize(*args) - super(*args) - - @description = "Time between merge request creation and merge/close" + def description + "Time between merge request creation and merge/close" end def median - @fetcher.calculate_metric(:review, - MergeRequest.arel_table[:created_at], - MergeRequest::Metrics.arel_table[:merged_at]) + @fetcher.median(:review, + MergeRequest.arel_table[:created_at], + MergeRequest::Metrics.arel_table[:merged_at]) end end end diff --git a/lib/gitlab/cycle_analytics/stage.rb b/lib/gitlab/cycle_analytics/stage.rb new file mode 100644 index 00000000000..acf746db6cd --- /dev/null +++ b/lib/gitlab/cycle_analytics/stage.rb @@ -0,0 +1,9 @@ +module Gitlab + module CycleAnalytics + module Stage + def self.[](stage_name) + const_get("::Gitlab::CycleAnalytics::#{stage_name.to_s.camelize}Stage") + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/staging_stage.rb b/lib/gitlab/cycle_analytics/staging_stage.rb index cb4398f15ac..b715a9453c7 100644 --- a/lib/gitlab/cycle_analytics/staging_stage.rb +++ b/lib/gitlab/cycle_analytics/staging_stage.rb @@ -1,16 +1,14 @@ module Gitlab module CycleAnalytics class StagingStage < BaseStage - def initialize(*args) - super(*args) - - @description = "From merge request merge until deploy to production" + def description + "From merge request merge until deploy to production" end def median - @fetcher.calculate_metric(:staging, - MergeRequest::Metrics.arel_table[:merged_at], - MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) + @fetcher.median(:staging, + MergeRequest::Metrics.arel_table[:merged_at], + MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) end end end diff --git a/lib/gitlab/cycle_analytics/test_stage.rb b/lib/gitlab/cycle_analytics/test_stage.rb index 3ab93bebd87..58f72bb405e 100644 --- a/lib/gitlab/cycle_analytics/test_stage.rb +++ b/lib/gitlab/cycle_analytics/test_stage.rb @@ -1,16 +1,14 @@ module Gitlab module CycleAnalytics class TestStage < BaseStage - def initialize(*args) - super(*args) - - @description = "Total test time for all commits/merges" + def description + "Total test time for all commits/merges" end def median - @fetcher.calculate_metric(:test, - MergeRequest::Metrics.arel_table[:latest_build_started_at], - MergeRequest::Metrics.arel_table[:latest_build_finished_at]) + @fetcher.median(:test, + MergeRequest::Metrics.arel_table[:latest_build_started_at], + MergeRequest::Metrics.arel_table[:latest_build_finished_at]) end end end -- cgit v1.2.1 From 69ecd951a9e0acbc31f0a4b9b02e8a1981ceff1e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 1 Dec 2016 12:44:35 +0100 Subject: refactor fetcher and fixed specs --- lib/gitlab/cycle_analytics/base_event.rb | 4 ++-- lib/gitlab/cycle_analytics/events_query.rb | 32 --------------------------- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 15 +++++++++++++ lib/gitlab/database/median.rb | 5 +++++ 4 files changed, 22 insertions(+), 34 deletions(-) delete mode 100644 lib/gitlab/cycle_analytics/events_query.rb (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb index eac807af037..2ce9c34d8e8 100644 --- a/lib/gitlab/cycle_analytics/base_event.rb +++ b/lib/gitlab/cycle_analytics/base_event.rb @@ -7,7 +7,7 @@ module Gitlab attr_reader :start_time_attrs, :end_time_attrs, :projections, :query def initialize(fetcher:, options:) - @query = EventsQuery.new(fetcher: fetcher) + @fetcher = fetcher @project = fetcher.project @options = options end @@ -39,7 +39,7 @@ module Gitlab end def event_result - @event_result ||= @query.execute(self).to_a + @event_result ||= @fetcher.events(self).to_a end def serialize(_event) diff --git a/lib/gitlab/cycle_analytics/events_query.rb b/lib/gitlab/cycle_analytics/events_query.rb deleted file mode 100644 index e2b79384c9b..00000000000 --- a/lib/gitlab/cycle_analytics/events_query.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Gitlab - module CycleAnalytics - class EventsQuery - def initialize(fetcher:) - @fetcher = fetcher - end - - def execute(stage_class) - @stage_class = stage_class - - ActiveRecord::Base.connection.exec_query(query.to_sql) - end - - private - - 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) - - @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 - - 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 bd68a0980ca..0542fbfb38d 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -29,6 +29,21 @@ module Gitlab median_datetime(cte_table, interval_query, name) end + def events(stage_class) + ActiveRecord::Base.connection.exec_query(events_query(stage_class).to_sql) + end + + private + + def events_query(stage_class) + base_query = base_query_for(stage_class.stage) + diff_fn = subtract_datetimes_diff(base_query, stage_class.start_time_attrs, stage_class.end_time_attrs) + + stage_class.custom_query(base_query) + + base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *stage_class.projections).order(stage_class.order.desc) + 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 diff --git a/lib/gitlab/database/median.rb b/lib/gitlab/database/median.rb index 1444d25ebc7..08607c27c09 100644 --- a/lib/gitlab/database/median.rb +++ b/lib/gitlab/database/median.rb @@ -103,6 +103,11 @@ module Gitlab Arel.sql(%Q{EXTRACT(EPOCH FROM "#{arel_attribute.relation.name}"."#{arel_attribute.name}")}) end + def extract_diff_epoch(diff) + return diff unless Gitlab::Database.postgresql? + + Arel.sql(%Q{EXTRACT(EPOCH FROM (#{diff.to_sql}))}) + end # Need to cast '0' to an INTERVAL before we can check if the interval is positive def zero_interval Arel::Nodes::NamedFunction.new("CAST", [Arel.sql("'0' AS INTERVAL")]) -- cgit v1.2.1 From 58dddcdfed6212046447de8b6d304ffd463d0350 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 1 Dec 2016 13:28:24 +0100 Subject: few fixes after merge --- lib/gitlab/cycle_analytics/stage_summary.rb | 5 +++-- lib/gitlab/cycle_analytics/summary/issue.rb | 8 +++++++- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/stage_summary.rb b/lib/gitlab/cycle_analytics/stage_summary.rb index dd9e4ac2813..b34baf5b081 100644 --- a/lib/gitlab/cycle_analytics/stage_summary.rb +++ b/lib/gitlab/cycle_analytics/stage_summary.rb @@ -1,13 +1,14 @@ module Gitlab module CycleAnalytics class StageSummary - def initialize(project, from:) + def initialize(project, from:, current_user:) @project = project @from = from + @current_user = current_user end def data - [serialize(Summary::Issue.new(project: @project, from: @from)), + [serialize(Summary::Issue.new(project: @project, from: @from, current_user: @current_user)), serialize(Summary::Commit.new(project: @project, from: @from)), serialize(Summary::Deploy.new(project: @project, from: @from))] end diff --git a/lib/gitlab/cycle_analytics/summary/issue.rb b/lib/gitlab/cycle_analytics/summary/issue.rb index 7d62164aae3..008468f24b9 100644 --- a/lib/gitlab/cycle_analytics/summary/issue.rb +++ b/lib/gitlab/cycle_analytics/summary/issue.rb @@ -2,12 +2,18 @@ module Gitlab module CycleAnalytics module Summary class Issue < Base + def initialize(project:, from:, current_user:) + @project = project + @from = from + @current_user = current_user + end + def title 'New Issue' end def value - @value ||= @project.issues.created_after(@from).count + @value ||= IssuesFinder.new(@current_user, project_id: @project.id).execute.created_after(@from).count end end end -- cgit v1.2.1 From b214be493d9f179d4a929ee32d94a336da7b38f1 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 2 Dec 2016 17:09:29 +0100 Subject: big refactor based on MR feedback --- lib/gitlab/cycle_analytics/base_event.rb | 62 ---------------------- lib/gitlab/cycle_analytics/base_event_fetcher.rb | 58 ++++++++++++++++++++ lib/gitlab/cycle_analytics/base_stage.rb | 13 +++-- lib/gitlab/cycle_analytics/class_name_util.rb | 13 ----- lib/gitlab/cycle_analytics/code_event.rb | 27 ---------- lib/gitlab/cycle_analytics/code_event_fetcher.rb | 25 +++++++++ lib/gitlab/cycle_analytics/code_stage.rb | 17 +++--- lib/gitlab/cycle_analytics/event.rb | 2 +- lib/gitlab/cycle_analytics/issue_event.rb | 26 --------- lib/gitlab/cycle_analytics/issue_event_fetcher.rb | 23 ++++++++ lib/gitlab/cycle_analytics/issue_stage.rb | 19 ++++--- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 25 +++++---- lib/gitlab/cycle_analytics/plan_event.rb | 45 ---------------- lib/gitlab/cycle_analytics/plan_event_fetcher.rb | 42 +++++++++++++++ lib/gitlab/cycle_analytics/plan_stage.rb | 19 ++++--- lib/gitlab/cycle_analytics/production_event.rb | 25 --------- .../cycle_analytics/production_event_fetcher.rb | 23 ++++++++ lib/gitlab/cycle_analytics/production_stage.rb | 17 +++--- lib/gitlab/cycle_analytics/review_event.rb | 24 --------- lib/gitlab/cycle_analytics/review_event_fetcher.rb | 22 ++++++++ lib/gitlab/cycle_analytics/review_stage.rb | 17 +++--- lib/gitlab/cycle_analytics/stage.rb | 2 +- lib/gitlab/cycle_analytics/staging_event.rb | 30 ----------- .../cycle_analytics/staging_event_fetcher.rb | 28 ++++++++++ lib/gitlab/cycle_analytics/staging_stage.rb | 17 +++--- lib/gitlab/cycle_analytics/test_event.rb | 12 ----- lib/gitlab/cycle_analytics/test_event_fetcher.rb | 6 +++ lib/gitlab/cycle_analytics/test_stage.rb | 17 +++--- 28 files changed, 331 insertions(+), 325 deletions(-) delete mode 100644 lib/gitlab/cycle_analytics/base_event.rb create mode 100644 lib/gitlab/cycle_analytics/base_event_fetcher.rb delete mode 100644 lib/gitlab/cycle_analytics/class_name_util.rb delete mode 100644 lib/gitlab/cycle_analytics/code_event.rb create mode 100644 lib/gitlab/cycle_analytics/code_event_fetcher.rb delete mode 100644 lib/gitlab/cycle_analytics/issue_event.rb create mode 100644 lib/gitlab/cycle_analytics/issue_event_fetcher.rb delete mode 100644 lib/gitlab/cycle_analytics/plan_event.rb create mode 100644 lib/gitlab/cycle_analytics/plan_event_fetcher.rb delete mode 100644 lib/gitlab/cycle_analytics/production_event.rb create mode 100644 lib/gitlab/cycle_analytics/production_event_fetcher.rb delete mode 100644 lib/gitlab/cycle_analytics/review_event.rb create mode 100644 lib/gitlab/cycle_analytics/review_event_fetcher.rb delete mode 100644 lib/gitlab/cycle_analytics/staging_event.rb create mode 100644 lib/gitlab/cycle_analytics/staging_event_fetcher.rb delete mode 100644 lib/gitlab/cycle_analytics/test_event.rb create mode 100644 lib/gitlab/cycle_analytics/test_event_fetcher.rb (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb deleted file mode 100644 index 2ce9c34d8e8..00000000000 --- a/lib/gitlab/cycle_analytics/base_event.rb +++ /dev/null @@ -1,62 +0,0 @@ -module Gitlab - module CycleAnalytics - class BaseEvent - include MetricsTables - include ClassNameUtil - - attr_reader :start_time_attrs, :end_time_attrs, :projections, :query - - def initialize(fetcher:, options:) - @fetcher = fetcher - @project = fetcher.project - @options = options - end - - def fetch - update_author! - - event_result.map do |event| - serialize(event) if has_permission?(event['id']) - end.compact - end - - def custom_query(_base_query); end - - def order - @order || @start_time_attrs - end - - def stage - class_name_for('Event') - end - - private - - def update_author! - return unless event_result.any? && event_result.first['author_id'] - - Updater.update!(event_result, from: 'author_id', to: 'author', klass: User) - end - - def event_result - @event_result ||= @fetcher.events(self).to_a - end - - def serialize(_event) - raise NotImplementedError.new("Expected #{self.name} to implement serialize(event)") - end - - 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 -end diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb new file mode 100644 index 00000000000..0d851f81b1d --- /dev/null +++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb @@ -0,0 +1,58 @@ +module Gitlab + module CycleAnalytics + class BaseEventFetcher + include MetricsTables + + attr_reader :projections, :query, :stage + + def initialize(fetcher:, options:, stage:) + @fetcher = fetcher + @project = fetcher.project + @options = options + @stage = stage + end + + def fetch + update_author! + + event_result.map do |event| + serialize(event) if has_permission?(event['id']) + end.compact + end + + def custom_query(_base_query); end + + def order + @order || @start_time_attrs + end + + private + + def update_author! + return unless event_result.any? && event_result.first['author_id'] + + Updater.update!(event_result, from: 'author_id', to: 'author', klass: User) + end + + def event_result + @event_result ||= @fetcher.events(self).to_a + end + + def serialize(_event) + raise NotImplementedError.new("Expected #{self.name} to implement serialize(event)") + end + + 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 +end diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index 551318f536a..f3b8bb6e1d3 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -1,18 +1,23 @@ module Gitlab module CycleAnalytics class BaseStage - include ClassNameUtil + attr_accessor :start_time_attrs, :end_time_attrs def initialize(project:, options:) @project = project @options = options @fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, from: options[:from], - branch: options[:branch]) + branch: options[:branch], + stage: self) + end + + def event + @event ||= Gitlab::CycleAnalytics::Event[stage].new(fetcher: @fetcher, options: @options) end def events - Gitlab::CycleAnalytics::Event[stage].new(fetcher: @fetcher, options: @options).fetch + event.fetch end def median_data @@ -24,7 +29,7 @@ module Gitlab end def median - raise NotImplementedError.new("Expected #{self.name} to implement median") + @fetcher.median end private diff --git a/lib/gitlab/cycle_analytics/class_name_util.rb b/lib/gitlab/cycle_analytics/class_name_util.rb deleted file mode 100644 index aac8d888077..00000000000 --- a/lib/gitlab/cycle_analytics/class_name_util.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Gitlab - module CycleAnalytics - module ClassNameUtil - def class_name_for(type) - class_name.split(type).first.to_sym - end - - def class_name - self.class.name.demodulize - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/code_event.rb b/lib/gitlab/cycle_analytics/code_event.rb deleted file mode 100644 index 68251630e08..00000000000 --- a/lib/gitlab/cycle_analytics/code_event.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Gitlab - module CycleAnalytics - class CodeEvent < BaseEvent - include MergeRequestAllowed - - def initialize(*args) - @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] - - super(*args) - end - - private - - def serialize(event) - AnalyticsMergeRequestSerializer.new(project: @project).represent(event).as_json - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/code_event_fetcher.rb b/lib/gitlab/cycle_analytics/code_event_fetcher.rb new file mode 100644 index 00000000000..5245b9ca8fc --- /dev/null +++ b/lib/gitlab/cycle_analytics/code_event_fetcher.rb @@ -0,0 +1,25 @@ +module Gitlab + module CycleAnalytics + class CodeEventFetcher < BaseEventFetcher + include MergeRequestAllowed + + def initialize(*args) + @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] + + super(*args) + end + + private + + def serialize(event) + AnalyticsMergeRequestSerializer.new(project: @project).represent(event).as_json + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/code_stage.rb b/lib/gitlab/cycle_analytics/code_stage.rb index 778ce7b5435..977d0d0210c 100644 --- a/lib/gitlab/cycle_analytics/code_stage.rb +++ b/lib/gitlab/cycle_analytics/code_stage.rb @@ -1,14 +1,19 @@ module Gitlab module CycleAnalytics class CodeStage < BaseStage - def description - "Time until first merge request" + def initialize(*args) + @start_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at] + @end_time_attrs = mr_table[:created_at] + + super(*args) end - def median - @fetcher.median(:code, - Issue::Metrics.arel_table[:first_mentioned_in_commit_at], - MergeRequest.arel_table[:created_at]) + def stage + :code + end + + def description + "Time until first merge request" end end end diff --git a/lib/gitlab/cycle_analytics/event.rb b/lib/gitlab/cycle_analytics/event.rb index 62fac89a0d5..bb3a5722a0f 100644 --- a/lib/gitlab/cycle_analytics/event.rb +++ b/lib/gitlab/cycle_analytics/event.rb @@ -2,7 +2,7 @@ module Gitlab module CycleAnalytics module Event def self.[](stage_name) - const_get("::Gitlab::CycleAnalytics::#{stage_name.to_s.camelize}Event") + CycleAnalytics.const_get("#{stage_name.to_s.camelize}Event") end end end diff --git a/lib/gitlab/cycle_analytics/issue_event.rb b/lib/gitlab/cycle_analytics/issue_event.rb deleted file mode 100644 index 76e8decf36e..00000000000 --- a/lib/gitlab/cycle_analytics/issue_event.rb +++ /dev/null @@ -1,26 +0,0 @@ -module Gitlab - module CycleAnalytics - class IssueEvent < BaseEvent - include IssueAllowed - - def initialize(*args) - @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]] - - super(*args) - end - - private - - def serialize(event) - AnalyticsIssueSerializer.new(project: @project).represent(event).as_json - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb new file mode 100644 index 00000000000..0d8da99455e --- /dev/null +++ b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb @@ -0,0 +1,23 @@ +module Gitlab + module CycleAnalytics + class IssueEventFetcher < BaseEventFetcher + include IssueAllowed + + def initialize(*args) + @projections = [issue_table[:title], + issue_table[:iid], + issue_table[:id], + issue_table[:created_at], + issue_table[:author_id]] + + super(*args) + end + + private + + def serialize(event) + AnalyticsIssueSerializer.new(project: @project).represent(event).as_json + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/issue_stage.rb b/lib/gitlab/cycle_analytics/issue_stage.rb index c317872fb1d..14e72c7ea48 100644 --- a/lib/gitlab/cycle_analytics/issue_stage.rb +++ b/lib/gitlab/cycle_analytics/issue_stage.rb @@ -1,15 +1,20 @@ module Gitlab module CycleAnalytics class IssueStage < BaseStage - def description - "Time before an issue gets scheduled" + def initialize(*args) + @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]] + + super(*args) end - def median - @fetcher.median(:issue, - Issue.arel_table[:created_at], - [Issue::Metrics.arel_table[:first_associated_with_milestone_at], - Issue::Metrics.arel_table[:first_added_to_board_at]]) + def stage + :issue + end + + def description + "Time before an issue gets scheduled" end end end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index 0542fbfb38d..865abd0fa6c 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -9,14 +9,15 @@ module Gitlab DEPLOYMENT_METRIC_STAGES = %i[production staging] - def initialize(project:, from:, branch:) + def initialize(project:, from:, branch:, stage:) @project = project @from = from @branch = branch + @stage = stage end - def median(name, start_time_attrs, end_time_attrs) - cte_table = Arel::Table.new("cte_table_for_#{name}") + def median + cte_table = Arel::Table.new("cte_table_for_#{@stage.stage}") # 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). @@ -24,24 +25,26 @@ module Gitlab # cycle analytics stage. interval_query = Arel::Nodes::As.new( cte_table, - subtract_datetimes(base_query_for(name), start_time_attrs, end_time_attrs, name.to_s)) + subtract_datetimes(base_query_for(name), @stage.start_time_attrs, @stage.end_time_attrs, @stage.stage.to_s)) median_datetime(cte_table, interval_query, name) end - def events(stage_class) - ActiveRecord::Base.connection.exec_query(events_query(stage_class).to_sql) + def events + ActiveRecord::Base.connection.exec_query(events_query.to_sql) end private - def events_query(stage_class) - base_query = base_query_for(stage_class.stage) - diff_fn = subtract_datetimes_diff(base_query, stage_class.start_time_attrs, stage_class.end_time_attrs) + def events_query + base_query = base_query_for(@stage.stage) + event = @stage.event - stage_class.custom_query(base_query) + diff_fn = subtract_datetimes_diff(base_query, @stage.start_time_attrs, @stage.end_time_attrs) - base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *stage_class.projections).order(stage_class.order.desc) + event_instance.custom_query(base_query) + + base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *event.projections).order(event.order.desc) end # Join table with a row for every pair (where the merge request diff --git a/lib/gitlab/cycle_analytics/plan_event.rb b/lib/gitlab/cycle_analytics/plan_event.rb deleted file mode 100644 index 4b06143495b..00000000000 --- a/lib/gitlab/cycle_analytics/plan_event.rb +++ /dev/null @@ -1,45 +0,0 @@ -module Gitlab - module CycleAnalytics - class PlanEvent < BaseEvent - def initialize(*args) - @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 - - def custom_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) - 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) - return nil if commits.blank? - - 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: @project, total_time: event['total_time']).represent(commit).as_json - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb new file mode 100644 index 00000000000..3e23c5644d3 --- /dev/null +++ b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb @@ -0,0 +1,42 @@ +module Gitlab + module CycleAnalytics + class PlanEventFetcher < BaseEventFetcher + def initialize(*args) + @projections = [mr_diff_table[:st_commits].as('commits'), + issue_metrics_table[:first_mentioned_in_commit_at]] + + super(*args) + 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 + + def serialize(event) + 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) + return nil if commits.blank? + + 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: @project, total_time: event['total_time']).represent(commit).as_json + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/plan_stage.rb b/lib/gitlab/cycle_analytics/plan_stage.rb index 5e6dd30d9e3..de2d5aaeb23 100644 --- a/lib/gitlab/cycle_analytics/plan_stage.rb +++ b/lib/gitlab/cycle_analytics/plan_stage.rb @@ -1,15 +1,20 @@ module Gitlab module CycleAnalytics class PlanStage < BaseStage - def description - "Time before an issue starts implementation" + def initialize(*args) + @start_time_attrs = [issue_metrics_table[:first_associated_with_milestone_at], + issue_metrics_table[:first_added_to_board_at]] + @end_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at] + + super(*args) end - def median - @fetcher.median(: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]) + def stage + :code + end + + def description + "Time before an issue starts implementation" end end end diff --git a/lib/gitlab/cycle_analytics/production_event.rb b/lib/gitlab/cycle_analytics/production_event.rb deleted file mode 100644 index c03cd4f4909..00000000000 --- a/lib/gitlab/cycle_analytics/production_event.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Gitlab - module CycleAnalytics - class ProductionEvent < BaseEvent - include IssueAllowed - - def initialize(*args) - @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]] - - super(*args) - end - - private - - def serialize(event) - AnalyticsIssueSerializer.new(project: @project).represent(event).as_json - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/production_event_fetcher.rb b/lib/gitlab/cycle_analytics/production_event_fetcher.rb new file mode 100644 index 00000000000..b7eff7d22f4 --- /dev/null +++ b/lib/gitlab/cycle_analytics/production_event_fetcher.rb @@ -0,0 +1,23 @@ +module Gitlab + module CycleAnalytics + class ProductionEventFetcher < BaseEventFetcher + include IssueAllowed + + def initialize(*args) + @projections = [issue_table[:title], + issue_table[:iid], + issue_table[:id], + issue_table[:created_at], + issue_table[:author_id]] + + super(*args) + end + + private + + def serialize(event) + AnalyticsIssueSerializer.new(project: @project).represent(event).as_json + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/production_stage.rb b/lib/gitlab/cycle_analytics/production_stage.rb index acd2f7b2b3b..104c6d3fd30 100644 --- a/lib/gitlab/cycle_analytics/production_stage.rb +++ b/lib/gitlab/cycle_analytics/production_stage.rb @@ -1,14 +1,19 @@ module Gitlab module CycleAnalytics class ProductionStage < BaseStage - def description - "From issue creation until deploy to production" + def initialize(*args) + @start_time_attrs = issue_table[:created_at] + @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] + + super(*args) end - def median - @fetcher.median(:production, - Issue.arel_table[:created_at], - MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) + def stage + :production + end + + def description + "From issue creation until deploy to production" end end end diff --git a/lib/gitlab/cycle_analytics/review_event.rb b/lib/gitlab/cycle_analytics/review_event.rb deleted file mode 100644 index 3f9ffa9657b..00000000000 --- a/lib/gitlab/cycle_analytics/review_event.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Gitlab - module CycleAnalytics - class ReviewEvent < BaseEvent - include MergeRequestAllowed - - def initialize(*args) - @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]] - - super(*args) - end - - def serialize(event) - AnalyticsMergeRequestSerializer.new(project: @project).represent(event).as_json - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/review_event_fetcher.rb b/lib/gitlab/cycle_analytics/review_event_fetcher.rb new file mode 100644 index 00000000000..4df0bd06393 --- /dev/null +++ b/lib/gitlab/cycle_analytics/review_event_fetcher.rb @@ -0,0 +1,22 @@ +module Gitlab + module CycleAnalytics + class ReviewEventFetcher < BaseEventFetcher + include MergeRequestAllowed + + def initialize(*args) + @projections = [mr_table[:title], + mr_table[:iid], + mr_table[:id], + mr_table[:created_at], + mr_table[:state], + mr_table[:author_id]] + + super(*args) + end + + def serialize(event) + AnalyticsMergeRequestSerializer.new(project: @project).represent(event).as_json + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/review_stage.rb b/lib/gitlab/cycle_analytics/review_stage.rb index c7b5e34e16a..c7bbd29693b 100644 --- a/lib/gitlab/cycle_analytics/review_stage.rb +++ b/lib/gitlab/cycle_analytics/review_stage.rb @@ -1,14 +1,19 @@ module Gitlab module CycleAnalytics class ReviewStage < BaseStage - def description - "Time between merge request creation and merge/close" + def initialize(*args) + @start_time_attrs = mr_table[:created_at] + @end_time_attrs = mr_metrics_table[:merged_at] + + super(*args) end - def median - @fetcher.median(:review, - MergeRequest.arel_table[:created_at], - MergeRequest::Metrics.arel_table[:merged_at]) + def stage + :review + end + + def description + "Time between merge request creation and merge/close" end end end diff --git a/lib/gitlab/cycle_analytics/stage.rb b/lib/gitlab/cycle_analytics/stage.rb index acf746db6cd..28e0455df59 100644 --- a/lib/gitlab/cycle_analytics/stage.rb +++ b/lib/gitlab/cycle_analytics/stage.rb @@ -2,7 +2,7 @@ module Gitlab module CycleAnalytics module Stage def self.[](stage_name) - const_get("::Gitlab::CycleAnalytics::#{stage_name.to_s.camelize}Stage") + CycleAnalytics.const_get("#{stage_name.to_s.camelize}Stage") end end end diff --git a/lib/gitlab/cycle_analytics/staging_event.rb b/lib/gitlab/cycle_analytics/staging_event.rb deleted file mode 100644 index eae18b447f0..00000000000 --- a/lib/gitlab/cycle_analytics/staging_event.rb +++ /dev/null @@ -1,30 +0,0 @@ -module Gitlab - module CycleAnalytics - class StagingEvent < BaseEvent - def initialize(*args) - @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] - - super(*args) - end - - def fetch - Updater.update!(event_result, from: 'id', to: 'build', klass: ::Ci::Build) - - super - 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) - AnalyticsBuildSerializer.new.represent(event['build']).as_json - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/staging_event_fetcher.rb b/lib/gitlab/cycle_analytics/staging_event_fetcher.rb new file mode 100644 index 00000000000..ea98e211ad6 --- /dev/null +++ b/lib/gitlab/cycle_analytics/staging_event_fetcher.rb @@ -0,0 +1,28 @@ +module Gitlab + module CycleAnalytics + class StagingEventFetcher < BaseEventFetcher + def initialize(*args) + @projections = [build_table[:id]] + @order = build_table[:created_at] + + super(*args) + end + + def fetch + Updater.update!(event_result, from: 'id', to: 'build', klass: ::Ci::Build) + + super + 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) + AnalyticsBuildSerializer.new.represent(event['build']).as_json + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/staging_stage.rb b/lib/gitlab/cycle_analytics/staging_stage.rb index b715a9453c7..079b26760bb 100644 --- a/lib/gitlab/cycle_analytics/staging_stage.rb +++ b/lib/gitlab/cycle_analytics/staging_stage.rb @@ -1,14 +1,19 @@ module Gitlab module CycleAnalytics class StagingStage < BaseStage - def description - "From merge request merge until deploy to production" + def initialize(*args) + @start_time_attrs = mr_metrics_table[:merged_at] + @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] + + super(*args) end - def median - @fetcher.median(:staging, - MergeRequest::Metrics.arel_table[:merged_at], - MergeRequest::Metrics.arel_table[:first_deployed_to_production_at]) + def stage + :staging + end + + def description + "From merge request merge until deploy to production" end end end diff --git a/lib/gitlab/cycle_analytics/test_event.rb b/lib/gitlab/cycle_analytics/test_event.rb deleted file mode 100644 index d0736672adf..00000000000 --- a/lib/gitlab/cycle_analytics/test_event.rb +++ /dev/null @@ -1,12 +0,0 @@ -module Gitlab - module CycleAnalytics - class TestEvent < StagingEvent - def initialize(*args) - super(*args) - - @start_time_attrs = mr_metrics_table[:latest_build_started_at] - @end_time_attrs = mr_metrics_table[:latest_build_finished_at] - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/test_event_fetcher.rb b/lib/gitlab/cycle_analytics/test_event_fetcher.rb new file mode 100644 index 00000000000..a2589c6601a --- /dev/null +++ b/lib/gitlab/cycle_analytics/test_event_fetcher.rb @@ -0,0 +1,6 @@ +module Gitlab + module CycleAnalytics + class TestEventFetcher < StagingEventFetcher + end + end +end diff --git a/lib/gitlab/cycle_analytics/test_stage.rb b/lib/gitlab/cycle_analytics/test_stage.rb index 58f72bb405e..a105e5f2b1f 100644 --- a/lib/gitlab/cycle_analytics/test_stage.rb +++ b/lib/gitlab/cycle_analytics/test_stage.rb @@ -1,14 +1,19 @@ module Gitlab module CycleAnalytics class TestStage < BaseStage - def description - "Total test time for all commits/merges" + def initialize(*args) + @start_time_attrs = mr_metrics_table[:latest_build_started_at] + @end_time_attrs = mr_metrics_table[:latest_build_finished_at] + + super(*args) end - def median - @fetcher.median(:test, - MergeRequest::Metrics.arel_table[:latest_build_started_at], - MergeRequest::Metrics.arel_table[:latest_build_finished_at]) + def stage + :test + end + + def description + "Total test time for all commits/merges" end end end -- cgit v1.2.1 From 3f681f4cefb5eda594acaab2eaf1be18ebd9066c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 5 Dec 2016 09:47:10 +0100 Subject: fix specs, refactor missing bits from events stuff --- lib/gitlab/cycle_analytics/base_event_fetcher.rb | 8 ++------ lib/gitlab/cycle_analytics/base_stage.rb | 4 +++- lib/gitlab/cycle_analytics/event.rb | 2 +- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 9 ++++++--- 4 files changed, 12 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb index 0d851f81b1d..d4b2d665e59 100644 --- a/lib/gitlab/cycle_analytics/base_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb @@ -3,7 +3,7 @@ module Gitlab class BaseEventFetcher include MetricsTables - attr_reader :projections, :query, :stage + attr_reader :projections, :query, :stage, :order def initialize(fetcher:, options:, stage:) @fetcher = fetcher @@ -22,10 +22,6 @@ module Gitlab def custom_query(_base_query); end - def order - @order || @start_time_attrs - end - private def update_author! @@ -35,7 +31,7 @@ module Gitlab end def event_result - @event_result ||= @fetcher.events(self).to_a + @event_result ||= @fetcher.events.to_a end def serialize(_event) diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index f3b8bb6e1d3..f81a41bccb6 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -1,6 +1,8 @@ module Gitlab module CycleAnalytics class BaseStage + include MetricsTables + attr_accessor :start_time_attrs, :end_time_attrs def initialize(project:, options:) @@ -13,7 +15,7 @@ module Gitlab end def event - @event ||= Gitlab::CycleAnalytics::Event[stage].new(fetcher: @fetcher, options: @options) + @event ||= Gitlab::CycleAnalytics::Event[stage].new(fetcher: @fetcher, options: @options, stage: stage) end def events diff --git a/lib/gitlab/cycle_analytics/event.rb b/lib/gitlab/cycle_analytics/event.rb index bb3a5722a0f..1ba7bc08ee5 100644 --- a/lib/gitlab/cycle_analytics/event.rb +++ b/lib/gitlab/cycle_analytics/event.rb @@ -2,7 +2,7 @@ module Gitlab module CycleAnalytics module Event def self.[](stage_name) - CycleAnalytics.const_get("#{stage_name.to_s.camelize}Event") + CycleAnalytics.const_get("#{stage_name.to_s.camelize}EventFetcher") end end end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index 865abd0fa6c..dd291840ecd 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -38,13 +38,16 @@ module Gitlab def events_query base_query = base_query_for(@stage.stage) - event = @stage.event diff_fn = subtract_datetimes_diff(base_query, @stage.start_time_attrs, @stage.end_time_attrs) - event_instance.custom_query(base_query) + @stage.event.custom_query(base_query) - base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *event.projections).order(event.order.desc) + base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *@stage.event.projections).order(order.desc) + end + + def order + @stage.event.order || @stage.start_time_attrs.is_a?(Array) ? @stage.start_time_attrs.first : @stage.start_time_attrs end # Join table with a row for every pair (where the merge request -- cgit v1.2.1 From 099aa124ebefac0ab490ab8e28294e0ea78279de Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 5 Dec 2016 10:45:07 +0100 Subject: fix plan stage issue and some spec failures --- lib/gitlab/cycle_analytics/base_stage.rb | 4 +++- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 6 +++++- lib/gitlab/cycle_analytics/plan_stage.rb | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index f81a41bccb6..c2605364ff0 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -15,7 +15,9 @@ module Gitlab end def event - @event ||= Gitlab::CycleAnalytics::Event[stage].new(fetcher: @fetcher, options: @options, stage: stage) + @event ||= Gitlab::CycleAnalytics::Event[stage].new(fetcher: @fetcher, + options: @options, + stage: stage) end def events diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index dd291840ecd..559dbc0e8fc 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -47,7 +47,11 @@ module Gitlab end def order - @stage.event.order || @stage.start_time_attrs.is_a?(Array) ? @stage.start_time_attrs.first : @stage.start_time_attrs + @stage.event.order || default_order + end + + def default_order + @stage.start_time_attrs.is_a?(Array) ? @stage.start_time_attrs.first : @stage.start_time_attrs end # Join table with a row for every pair (where the merge request diff --git a/lib/gitlab/cycle_analytics/plan_stage.rb b/lib/gitlab/cycle_analytics/plan_stage.rb index de2d5aaeb23..f8c9b9c4495 100644 --- a/lib/gitlab/cycle_analytics/plan_stage.rb +++ b/lib/gitlab/cycle_analytics/plan_stage.rb @@ -10,7 +10,7 @@ module Gitlab end def stage - :code + :plan end def description -- cgit v1.2.1 From 056b0f199388ffd4d3156c59a344f284131f7cc6 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 5 Dec 2016 12:07:31 +0100 Subject: fix missing refactor in metrics fetcher --- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb index 559dbc0e8fc..4115c092c0d 100644 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ b/lib/gitlab/cycle_analytics/metrics_fetcher.rb @@ -25,9 +25,9 @@ module Gitlab # cycle analytics stage. interval_query = Arel::Nodes::As.new( cte_table, - subtract_datetimes(base_query_for(name), @stage.start_time_attrs, @stage.end_time_attrs, @stage.stage.to_s)) + subtract_datetimes(base_query_for(@stage.stage), @stage.start_time_attrs, @stage.end_time_attrs, @stage.stage.to_s)) - median_datetime(cte_table, interval_query, name) + median_datetime(cte_table, interval_query, @stage.stage) end def events -- cgit v1.2.1 From bbb9f840824aec2467ed2580cae4d9b42b85a7d4 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 5 Dec 2016 13:28:53 +0100 Subject: a few more fixes --- lib/gitlab/cycle_analytics/issue_event_fetcher.rb | 17 ----------------- lib/gitlab/cycle_analytics/production_event_fetcher.rb | 2 +- 2 files changed, 1 insertion(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb index 0d8da99455e..69ba7c3cc9c 100644 --- a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb @@ -1,23 +1,6 @@ module Gitlab module CycleAnalytics class IssueEventFetcher < BaseEventFetcher - include IssueAllowed - - def initialize(*args) - @projections = [issue_table[:title], - issue_table[:iid], - issue_table[:id], - issue_table[:created_at], - issue_table[:author_id]] - - super(*args) - end - - private - - def serialize(event) - AnalyticsIssueSerializer.new(project: @project).represent(event).as_json - end end end end diff --git a/lib/gitlab/cycle_analytics/production_event_fetcher.rb b/lib/gitlab/cycle_analytics/production_event_fetcher.rb index b7eff7d22f4..882e780874f 100644 --- a/lib/gitlab/cycle_analytics/production_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/production_event_fetcher.rb @@ -1,6 +1,6 @@ module Gitlab module CycleAnalytics - class ProductionEventFetcher < BaseEventFetcher + class ProductionEventFetcher < IssueEventFetcher include IssueAllowed def initialize(*args) -- cgit v1.2.1 From 834bcacbaec837d8ec0a269f111bca769843bcb4 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 7 Dec 2016 09:25:11 +0100 Subject: fix refactor of production event fetcher --- lib/gitlab/cycle_analytics/issue_event_fetcher.rb | 17 +++++++++++++++++ lib/gitlab/cycle_analytics/production_event_fetcher.rb | 17 ----------------- 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb index 69ba7c3cc9c..0d8da99455e 100644 --- a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb @@ -1,6 +1,23 @@ module Gitlab module CycleAnalytics class IssueEventFetcher < BaseEventFetcher + include IssueAllowed + + def initialize(*args) + @projections = [issue_table[:title], + issue_table[:iid], + issue_table[:id], + issue_table[:created_at], + issue_table[:author_id]] + + super(*args) + end + + private + + def serialize(event) + AnalyticsIssueSerializer.new(project: @project).represent(event).as_json + end end end end diff --git a/lib/gitlab/cycle_analytics/production_event_fetcher.rb b/lib/gitlab/cycle_analytics/production_event_fetcher.rb index 882e780874f..0fa2e87f673 100644 --- a/lib/gitlab/cycle_analytics/production_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/production_event_fetcher.rb @@ -1,23 +1,6 @@ module Gitlab module CycleAnalytics class ProductionEventFetcher < IssueEventFetcher - include IssueAllowed - - def initialize(*args) - @projections = [issue_table[:title], - issue_table[:iid], - issue_table[:id], - issue_table[:created_at], - issue_table[:author_id]] - - super(*args) - end - - private - - def serialize(event) - AnalyticsIssueSerializer.new(project: @project).represent(event).as_json - end end end end -- cgit v1.2.1 From 982d5a050667c517bbc996a08ca0922f2c5fbfb4 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 9 Dec 2016 12:41:15 +0100 Subject: refactored metrics fetcher - merged into stage and events --- lib/gitlab/cycle_analytics/base_event_fetcher.rb | 27 +++++-- lib/gitlab/cycle_analytics/base_query.rb | 31 ++++++++ lib/gitlab/cycle_analytics/base_stage.rb | 36 +++++---- lib/gitlab/cycle_analytics/code_stage.rb | 2 +- lib/gitlab/cycle_analytics/issue_stage.rb | 2 +- lib/gitlab/cycle_analytics/metrics_fetcher.rb | 86 ---------------------- lib/gitlab/cycle_analytics/plan_event_fetcher.rb | 4 +- lib/gitlab/cycle_analytics/plan_stage.rb | 2 +- lib/gitlab/cycle_analytics/production_helper.rb | 9 +++ lib/gitlab/cycle_analytics/production_stage.rb | 9 ++- lib/gitlab/cycle_analytics/review_stage.rb | 2 +- .../cycle_analytics/staging_event_fetcher.rb | 4 +- lib/gitlab/cycle_analytics/staging_stage.rb | 4 +- lib/gitlab/cycle_analytics/summary/commit.rb | 7 +- lib/gitlab/cycle_analytics/test_stage.rb | 10 ++- 15 files changed, 116 insertions(+), 119 deletions(-) create mode 100644 lib/gitlab/cycle_analytics/base_query.rb delete mode 100644 lib/gitlab/cycle_analytics/metrics_fetcher.rb create mode 100644 lib/gitlab/cycle_analytics/production_helper.rb (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb index d4b2d665e59..8b4ccfd5363 100644 --- a/lib/gitlab/cycle_analytics/base_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb @@ -1,15 +1,14 @@ module Gitlab module CycleAnalytics class BaseEventFetcher - include MetricsTables + include BaseQuery attr_reader :projections, :query, :stage, :order - def initialize(fetcher:, options:, stage:) - @fetcher = fetcher - @project = fetcher.project - @options = options + def initialize(project:, stage:, options:) + @project = project @stage = stage + @options = options end def fetch @@ -20,8 +19,6 @@ module Gitlab end.compact end - def custom_query(_base_query); end - private def update_author! @@ -31,7 +28,21 @@ module Gitlab end def event_result - @event_result ||= @fetcher.events.to_a + @event_result ||= ActiveRecord::Base.connection.exec_query(events_query.to_sql).to_a + end + + def events_query + diff_fn = subtract_datetimes_diff(base_query, @options[:start_time_attrs], @options[:end_time_attrs]) + + base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *projections).order(order.desc) + end + + def order + @order || default_order + end + + def default_order + @options[:start_time_attrs].is_a?(Array) ? @options[:start_time_attrs].first : @options[:start_time_attrs] end def serialize(_event) diff --git a/lib/gitlab/cycle_analytics/base_query.rb b/lib/gitlab/cycle_analytics/base_query.rb new file mode 100644 index 00000000000..d560dca45c8 --- /dev/null +++ b/lib/gitlab/cycle_analytics/base_query.rb @@ -0,0 +1,31 @@ +module Gitlab + module CycleAnalytics + module BaseQuery + include MetricsTables + include Gitlab::Database::Median + include Gitlab::Database::DateTime + + private + + def base_query + @base_query ||= stage_query + end + + def stage_query + 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)). + where(issue_table[:created_at].gteq(@options[:from])) + + # Load merge_requests + 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])) + + query + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index c2605364ff0..afec16d1818 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -1,23 +1,17 @@ module Gitlab module CycleAnalytics class BaseStage - include MetricsTables - - attr_accessor :start_time_attrs, :end_time_attrs + include BaseQuery def initialize(project:, options:) @project = project @options = options - @fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, - from: options[:from], - branch: options[:branch], - stage: self) end def event - @event ||= Gitlab::CycleAnalytics::Event[stage].new(fetcher: @fetcher, - options: @options, - stage: stage) + @event ||= Gitlab::CycleAnalytics::Event[name].new(project: @project, + stage: name, + options: event_options) end def events @@ -29,17 +23,31 @@ module Gitlab end def title - stage.to_s.capitalize + name.to_s.capitalize end def median - @fetcher.median + 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, @start_time_attrs, @end_time_attrs, name.to_s)) + + median_datetime(cte_table, interval_query, name) + end + + def name + raise NotImplementedError.new("Expected #{self.name} to implement name") end private - def stage - class_name_for('Stage') + def event_options + @options.merge(start_time_attrs: @start_time_attrs, end_time_attrs: @end_time_attrs) end end end diff --git a/lib/gitlab/cycle_analytics/code_stage.rb b/lib/gitlab/cycle_analytics/code_stage.rb index 977d0d0210c..111c0e99633 100644 --- a/lib/gitlab/cycle_analytics/code_stage.rb +++ b/lib/gitlab/cycle_analytics/code_stage.rb @@ -8,7 +8,7 @@ module Gitlab super(*args) end - def stage + def name :code end diff --git a/lib/gitlab/cycle_analytics/issue_stage.rb b/lib/gitlab/cycle_analytics/issue_stage.rb index 14e72c7ea48..d320458d7fd 100644 --- a/lib/gitlab/cycle_analytics/issue_stage.rb +++ b/lib/gitlab/cycle_analytics/issue_stage.rb @@ -9,7 +9,7 @@ module Gitlab super(*args) end - def stage + def name :issue end diff --git a/lib/gitlab/cycle_analytics/metrics_fetcher.rb b/lib/gitlab/cycle_analytics/metrics_fetcher.rb deleted file mode 100644 index 4115c092c0d..00000000000 --- a/lib/gitlab/cycle_analytics/metrics_fetcher.rb +++ /dev/null @@ -1,86 +0,0 @@ -module Gitlab - module CycleAnalytics - class MetricsFetcher - include Gitlab::Database::Median - include Gitlab::Database::DateTime - include MetricsTables - - attr_reader :project - - DEPLOYMENT_METRIC_STAGES = %i[production staging] - - def initialize(project:, from:, branch:, stage:) - @project = project - @from = from - @branch = branch - @stage = stage - end - - def median - cte_table = Arel::Table.new("cte_table_for_#{@stage.stage}") - - # 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(@stage.stage), @stage.start_time_attrs, @stage.end_time_attrs, @stage.stage.to_s)) - - median_datetime(cte_table, interval_query, @stage.stage) - end - - def events - ActiveRecord::Base.connection.exec_query(events_query.to_sql) - end - - private - - def events_query - base_query = base_query_for(@stage.stage) - - diff_fn = subtract_datetimes_diff(base_query, @stage.start_time_attrs, @stage.end_time_attrs) - - @stage.event.custom_query(base_query) - - base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *@stage.event.projections).order(order.desc) - end - - def order - @stage.event.order || default_order - end - - def default_order - @stage.start_time_attrs.is_a?(Array) ? @stage.start_time_attrs.first : @stage.start_time_attrs - 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 = 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)). - 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])). - 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(mr_metrics_table[:first_deployed_to_production_at].gteq(@from)) - end - - query - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb index 3e23c5644d3..88a8710dbe6 100644 --- a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb @@ -8,8 +8,10 @@ module Gitlab super(*args) end - def custom_query(base_query) + def events_query base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) + + super end private diff --git a/lib/gitlab/cycle_analytics/plan_stage.rb b/lib/gitlab/cycle_analytics/plan_stage.rb index f8c9b9c4495..a7164e5c5b7 100644 --- a/lib/gitlab/cycle_analytics/plan_stage.rb +++ b/lib/gitlab/cycle_analytics/plan_stage.rb @@ -9,7 +9,7 @@ module Gitlab super(*args) end - def stage + def name :plan end diff --git a/lib/gitlab/cycle_analytics/production_helper.rb b/lib/gitlab/cycle_analytics/production_helper.rb new file mode 100644 index 00000000000..d693443bfa4 --- /dev/null +++ b/lib/gitlab/cycle_analytics/production_helper.rb @@ -0,0 +1,9 @@ +module Gitlab + module CycleAnalytics + module ProductionHelper + def stage_query + super.where(mr_metrics_table[:first_deployed_to_production_at].gteq(@options[:from])) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/production_stage.rb b/lib/gitlab/cycle_analytics/production_stage.rb index 104c6d3fd30..eb221c68324 100644 --- a/lib/gitlab/cycle_analytics/production_stage.rb +++ b/lib/gitlab/cycle_analytics/production_stage.rb @@ -1,6 +1,8 @@ module Gitlab module CycleAnalytics class ProductionStage < BaseStage + include ProductionHelper + def initialize(*args) @start_time_attrs = issue_table[:created_at] @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] @@ -8,13 +10,18 @@ module Gitlab super(*args) end - def stage + def name :production end def description "From issue creation until deploy to production" end + + def query + # Limit to merge requests that have been deployed to production after `@from` + query.where(mr_metrics_table[:first_deployed_to_production_at].gteq(@from)) + end end end end diff --git a/lib/gitlab/cycle_analytics/review_stage.rb b/lib/gitlab/cycle_analytics/review_stage.rb index c7bbd29693b..72ce1ed1e16 100644 --- a/lib/gitlab/cycle_analytics/review_stage.rb +++ b/lib/gitlab/cycle_analytics/review_stage.rb @@ -8,7 +8,7 @@ module Gitlab super(*args) end - def stage + def name :review end diff --git a/lib/gitlab/cycle_analytics/staging_event_fetcher.rb b/lib/gitlab/cycle_analytics/staging_event_fetcher.rb index ea98e211ad6..a34731a5fcd 100644 --- a/lib/gitlab/cycle_analytics/staging_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/staging_event_fetcher.rb @@ -14,8 +14,10 @@ module Gitlab super end - def custom_query(base_query) + def events_query base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) + + super end private diff --git a/lib/gitlab/cycle_analytics/staging_stage.rb b/lib/gitlab/cycle_analytics/staging_stage.rb index 079b26760bb..398c1b5989a 100644 --- a/lib/gitlab/cycle_analytics/staging_stage.rb +++ b/lib/gitlab/cycle_analytics/staging_stage.rb @@ -1,6 +1,8 @@ module Gitlab module CycleAnalytics class StagingStage < BaseStage + include ProductionHelper + def initialize(*args) @start_time_attrs = mr_metrics_table[:merged_at] @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] @@ -8,7 +10,7 @@ module Gitlab super(*args) end - def stage + def name :staging end diff --git a/lib/gitlab/cycle_analytics/summary/commit.rb b/lib/gitlab/cycle_analytics/summary/commit.rb index ec3c067c0be..61a50762164 100644 --- a/lib/gitlab/cycle_analytics/summary/commit.rb +++ b/lib/gitlab/cycle_analytics/summary/commit.rb @@ -23,8 +23,11 @@ module Gitlab cmd << "--after=#{@from.iso8601}" cmd << sha - raw_output = IO.popen(cmd) { |io| io.read } - raw_output.lines.count + output, status = Gitlab::Popen.popen(cmd) { |io| io.read } + + raise IOError, output unless status.zero? + + output.lines.count end def ref diff --git a/lib/gitlab/cycle_analytics/test_stage.rb b/lib/gitlab/cycle_analytics/test_stage.rb index a105e5f2b1f..7e59745ffef 100644 --- a/lib/gitlab/cycle_analytics/test_stage.rb +++ b/lib/gitlab/cycle_analytics/test_stage.rb @@ -8,13 +8,21 @@ module Gitlab super(*args) end - def stage + def name :test end def description "Total test time for all commits/merges" end + + def stage_query + if @options[:branch] + super.where(build_table[:ref].eq(@options[:branch])) + else + super + end + end end end end -- cgit v1.2.1 From 30c6703f0afbf570ca3e3613a55afcbc7094c4eb Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 9 Dec 2016 15:23:09 +0100 Subject: fix specs --- lib/gitlab/cycle_analytics/base_event_fetcher.rb | 8 ++++---- lib/gitlab/cycle_analytics/summary/commit.rb | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb index 8b4ccfd5363..8d10ddf15d7 100644 --- a/lib/gitlab/cycle_analytics/base_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb @@ -19,6 +19,10 @@ module Gitlab end.compact end + def order + @order || default_order + end + private def update_author! @@ -37,10 +41,6 @@ module Gitlab base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *projections).order(order.desc) end - def order - @order || default_order - end - def default_order @options[:start_time_attrs].is_a?(Array) ? @options[:start_time_attrs].first : @options[:start_time_attrs] end diff --git a/lib/gitlab/cycle_analytics/summary/commit.rb b/lib/gitlab/cycle_analytics/summary/commit.rb index 61a50762164..7b8faa4d854 100644 --- a/lib/gitlab/cycle_analytics/summary/commit.rb +++ b/lib/gitlab/cycle_analytics/summary/commit.rb @@ -23,7 +23,7 @@ module Gitlab cmd << "--after=#{@from.iso8601}" cmd << sha - output, status = Gitlab::Popen.popen(cmd) { |io| io.read } + output, status = Gitlab::Popen.popen(cmd) raise IOError, output unless status.zero? -- cgit v1.2.1 From 1b220df56d935f6f9560bdb54f744f17ecfa035b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 9 Dec 2016 15:28:08 +0100 Subject: fix bug retrieving medians --- lib/gitlab/cycle_analytics/base_stage.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index afec16d1818..9143792e044 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -35,7 +35,7 @@ module Gitlab # cycle analytics stage. interval_query = Arel::Nodes::As.new( cte_table, - subtract_datetimes(base_query, @start_time_attrs, @end_time_attrs, name.to_s)) + subtract_datetimes(base_query.dup, @start_time_attrs, @end_time_attrs, name.to_s)) median_datetime(cte_table, interval_query, name) end -- cgit v1.2.1 From 150a448596dda076f0893facbd621429738aba92 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 12 Jan 2017 12:48:01 +0100 Subject: refactored a bunch of stuff based on feedback --- lib/gitlab/cycle_analytics/base_event_fetcher.rb | 2 +- lib/gitlab/cycle_analytics/base_stage.rb | 20 ++++++++++---------- lib/gitlab/cycle_analytics/code_stage.rb | 9 +++++---- lib/gitlab/cycle_analytics/event.rb | 9 --------- lib/gitlab/cycle_analytics/event_fetcher.rb | 9 +++++++++ lib/gitlab/cycle_analytics/issue_stage.rb | 11 ++++++----- lib/gitlab/cycle_analytics/plan_stage.rb | 11 ++++++----- lib/gitlab/cycle_analytics/production_stage.rb | 9 +++++---- lib/gitlab/cycle_analytics/review_stage.rb | 9 +++++---- lib/gitlab/cycle_analytics/staging_stage.rb | 10 +++++----- lib/gitlab/cycle_analytics/test_stage.rb | 9 +++++---- 11 files changed, 57 insertions(+), 51 deletions(-) delete mode 100644 lib/gitlab/cycle_analytics/event.rb create mode 100644 lib/gitlab/cycle_analytics/event_fetcher.rb (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb index 8d10ddf15d7..0d8791d396b 100644 --- a/lib/gitlab/cycle_analytics/base_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb @@ -42,7 +42,7 @@ module Gitlab end def default_order - @options[:start_time_attrs].is_a?(Array) ? @options[:start_time_attrs].first : @options[:start_time_attrs] + [@options[:start_time_attrs]].flatten.first end def serialize(_event) diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index 9143792e044..7ff15051558 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -8,17 +8,11 @@ module Gitlab @options = options end - def event - @event ||= Gitlab::CycleAnalytics::Event[name].new(project: @project, - stage: name, - options: event_options) - end - def events - event.fetch + event_fetcher.fetch end - def median_data + def as_json AnalyticsStageSerializer.new.represent(self).as_json end @@ -35,7 +29,7 @@ module Gitlab # cycle analytics stage. interval_query = Arel::Nodes::As.new( cte_table, - subtract_datetimes(base_query.dup, @start_time_attrs, @end_time_attrs, name.to_s)) + subtract_datetimes(base_query.dup, start_time_attrs, end_time_attrs, name.to_s)) median_datetime(cte_table, interval_query, name) end @@ -46,8 +40,14 @@ module Gitlab private + def event_fetcher + @event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(project: @project, + stage: name, + options: event_options) + end + def event_options - @options.merge(start_time_attrs: @start_time_attrs, end_time_attrs: @end_time_attrs) + @options.merge(start_time_attrs: start_time_attrs, end_time_attrs: end_time_attrs) end end end diff --git a/lib/gitlab/cycle_analytics/code_stage.rb b/lib/gitlab/cycle_analytics/code_stage.rb index 111c0e99633..d1bc2055ba8 100644 --- a/lib/gitlab/cycle_analytics/code_stage.rb +++ b/lib/gitlab/cycle_analytics/code_stage.rb @@ -1,11 +1,12 @@ module Gitlab module CycleAnalytics class CodeStage < BaseStage - def initialize(*args) - @start_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at] - @end_time_attrs = mr_table[:created_at] + def start_time_attrs + @start_time_attrs ||= issue_metrics_table[:first_mentioned_in_commit_at] + end - super(*args) + def end_time_attrs + @end_time_attrs ||= mr_table[:created_at] end def name diff --git a/lib/gitlab/cycle_analytics/event.rb b/lib/gitlab/cycle_analytics/event.rb deleted file mode 100644 index 1ba7bc08ee5..00000000000 --- a/lib/gitlab/cycle_analytics/event.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Gitlab - module CycleAnalytics - module Event - def self.[](stage_name) - CycleAnalytics.const_get("#{stage_name.to_s.camelize}EventFetcher") - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/event_fetcher.rb b/lib/gitlab/cycle_analytics/event_fetcher.rb new file mode 100644 index 00000000000..50e126cf00b --- /dev/null +++ b/lib/gitlab/cycle_analytics/event_fetcher.rb @@ -0,0 +1,9 @@ +module Gitlab + module CycleAnalytics + module EventFetcher + def self.[](stage_name) + CycleAnalytics.const_get("#{stage_name.to_s.camelize}EventFetcher") + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/issue_stage.rb b/lib/gitlab/cycle_analytics/issue_stage.rb index d320458d7fd..d2068fbc38f 100644 --- a/lib/gitlab/cycle_analytics/issue_stage.rb +++ b/lib/gitlab/cycle_analytics/issue_stage.rb @@ -1,12 +1,13 @@ module Gitlab module CycleAnalytics class IssueStage < BaseStage - def initialize(*args) - @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]] + def start_time_attrs + @start_time_attrs ||= issue_table[:created_at] + end - super(*args) + def end_time_attrs + @end_time_attrs ||= [issue_metrics_table[:first_associated_with_milestone_at], + issue_metrics_table[:first_added_to_board_at]] end def name diff --git a/lib/gitlab/cycle_analytics/plan_stage.rb b/lib/gitlab/cycle_analytics/plan_stage.rb index a7164e5c5b7..3b4dfc6a30e 100644 --- a/lib/gitlab/cycle_analytics/plan_stage.rb +++ b/lib/gitlab/cycle_analytics/plan_stage.rb @@ -1,12 +1,13 @@ module Gitlab module CycleAnalytics class PlanStage < BaseStage - def initialize(*args) - @start_time_attrs = [issue_metrics_table[:first_associated_with_milestone_at], - issue_metrics_table[:first_added_to_board_at]] - @end_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at] + def start_time_attrs + @start_time_attrs ||= [issue_metrics_table[:first_associated_with_milestone_at], + issue_metrics_table[:first_added_to_board_at]] + end - super(*args) + def end_time_attrs + @end_time_attrs ||= issue_metrics_table[:first_mentioned_in_commit_at] end def name diff --git a/lib/gitlab/cycle_analytics/production_stage.rb b/lib/gitlab/cycle_analytics/production_stage.rb index eb221c68324..2a6bcc80116 100644 --- a/lib/gitlab/cycle_analytics/production_stage.rb +++ b/lib/gitlab/cycle_analytics/production_stage.rb @@ -3,11 +3,12 @@ module Gitlab class ProductionStage < BaseStage include ProductionHelper - def initialize(*args) - @start_time_attrs = issue_table[:created_at] - @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] + def start_time_attrs + @start_time_attrs ||= issue_table[:created_at] + end - super(*args) + def end_time_attrs + @end_time_attrs ||= mr_metrics_table[:first_deployed_to_production_at] end def name diff --git a/lib/gitlab/cycle_analytics/review_stage.rb b/lib/gitlab/cycle_analytics/review_stage.rb index 72ce1ed1e16..fbaa3010d81 100644 --- a/lib/gitlab/cycle_analytics/review_stage.rb +++ b/lib/gitlab/cycle_analytics/review_stage.rb @@ -1,11 +1,12 @@ module Gitlab module CycleAnalytics class ReviewStage < BaseStage - def initialize(*args) - @start_time_attrs = mr_table[:created_at] - @end_time_attrs = mr_metrics_table[:merged_at] + def start_time_attrs + @start_time_attrs ||= mr_table[:created_at] + end - super(*args) + def end_time_attrs + @end_time_attrs ||= mr_metrics_table[:merged_at] end def name diff --git a/lib/gitlab/cycle_analytics/staging_stage.rb b/lib/gitlab/cycle_analytics/staging_stage.rb index 398c1b5989a..945909a4d62 100644 --- a/lib/gitlab/cycle_analytics/staging_stage.rb +++ b/lib/gitlab/cycle_analytics/staging_stage.rb @@ -2,12 +2,12 @@ module Gitlab module CycleAnalytics class StagingStage < BaseStage include ProductionHelper + def start_time_attrs + @start_time_attrs ||= mr_metrics_table[:merged_at] + end - def initialize(*args) - @start_time_attrs = mr_metrics_table[:merged_at] - @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] - - super(*args) + def end_time_attrs + @end_time_attrs ||= mr_metrics_table[:first_deployed_to_production_at] end def name diff --git a/lib/gitlab/cycle_analytics/test_stage.rb b/lib/gitlab/cycle_analytics/test_stage.rb index 7e59745ffef..0079d56e0e4 100644 --- a/lib/gitlab/cycle_analytics/test_stage.rb +++ b/lib/gitlab/cycle_analytics/test_stage.rb @@ -1,11 +1,12 @@ module Gitlab module CycleAnalytics class TestStage < BaseStage - def initialize(*args) - @start_time_attrs = mr_metrics_table[:latest_build_started_at] - @end_time_attrs = mr_metrics_table[:latest_build_finished_at] + def start_time_attrs + @start_time_attrs ||= mr_metrics_table[:latest_build_started_at] + end - super(*args) + def end_time_attrs + @end_time_attrs ||= mr_metrics_table[:latest_build_finished_at] end def name -- cgit v1.2.1 From 1d775d9712d0c493ae171e37fe507f5160cd7d0e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 12 Jan 2017 14:32:30 +0100 Subject: fix spec --- lib/gitlab/cycle_analytics/base_stage.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index 7ff15051558..74bbcdcb3dd 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -42,8 +42,8 @@ module Gitlab def event_fetcher @event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(project: @project, - stage: name, - options: event_options) + stage: name, + options: event_options) end def event_options -- cgit v1.2.1 From 8faf0c7abe9ddd8e9b3932f8f22c8319ada06a35 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 17 Jan 2017 14:16:36 +0100 Subject: Add detailed statuses for external commit statuses --- lib/gitlab/ci/status/external/common.rb | 22 ++++++++++++++++++++++ lib/gitlab/ci/status/external/factory.rb | 13 +++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 lib/gitlab/ci/status/external/common.rb create mode 100644 lib/gitlab/ci/status/external/factory.rb (limited to 'lib') diff --git a/lib/gitlab/ci/status/external/common.rb b/lib/gitlab/ci/status/external/common.rb new file mode 100644 index 00000000000..f067381d6a8 --- /dev/null +++ b/lib/gitlab/ci/status/external/common.rb @@ -0,0 +1,22 @@ +module Gitlab + module Ci + module Status + module External + module Common + def has_details? + can?(user, :read_commit_status, subject) && + subject.target_url.present? + end + + def details_path + subject.target_url + end + + def has_action? + false + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/external/factory.rb b/lib/gitlab/ci/status/external/factory.rb new file mode 100644 index 00000000000..07b15bd8d97 --- /dev/null +++ b/lib/gitlab/ci/status/external/factory.rb @@ -0,0 +1,13 @@ +module Gitlab + module Ci + module Status + module External + class Factory < Status::Factory + def self.common_helpers + Status::External::Common + end + end + end + end + end +end -- cgit v1.2.1 From 9f1beed7a7d5cb70db843c765754b8a4c7de141a Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 17 Jan 2017 14:30:33 +0100 Subject: Link external commit status badge to a target URL Using new detailed statuses factory for external commit statuses. --- lib/gitlab/ci/status/external/common.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/ci/status/external/common.rb b/lib/gitlab/ci/status/external/common.rb index f067381d6a8..4969a350862 100644 --- a/lib/gitlab/ci/status/external/common.rb +++ b/lib/gitlab/ci/status/external/common.rb @@ -4,8 +4,8 @@ module Gitlab module External module Common def has_details? - can?(user, :read_commit_status, subject) && - subject.target_url.present? + subject.target_url.present? && + can?(user, :read_commit_status, subject) end def details_path -- cgit v1.2.1 From 9ce8aa31f2f55563cbf4212f7dd2b51576967a55 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 18 Jan 2017 12:24:53 +0100 Subject: Respond with validation errors in commit status API If validation errors are present, include validation errors in the commit status API payload, instead of depending on state machine errors caused by invalid record. --- lib/api/commit_statuses.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 4bbdf06a49c..b6e6820c3f4 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -78,6 +78,8 @@ module API description: params[:description] ) + render_validation_error!(status) if status.invalid? + begin case params[:state] when 'pending' -- cgit v1.2.1 From b4f67cc2294f262d35fe63cc1e60eccebc4667ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 6 Jan 2017 12:30:19 +0100 Subject: Document presenters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/view/presenter.rb | 32 +++++++++++++++++++++++++++++ lib/gitlab/view/presenter_factory.rb | 39 ++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 lib/gitlab/view/presenter.rb create mode 100644 lib/gitlab/view/presenter_factory.rb (limited to 'lib') diff --git a/lib/gitlab/view/presenter.rb b/lib/gitlab/view/presenter.rb new file mode 100644 index 00000000000..8b63b271b99 --- /dev/null +++ b/lib/gitlab/view/presenter.rb @@ -0,0 +1,32 @@ +module Gitlab + module View + module Presenter + extend ActiveSupport::Concern + + included do + include Gitlab::Routing + include Gitlab::Allowable + end + + def with_subject(subject) + tap { @subject = subject } + end + + def with_user(user) + tap { @user = user } + end + + private + + attr_reader :subject, :user + + class_methods do + def presents(name) + define_method(name) do + subject + end + end + end + end + end +end diff --git a/lib/gitlab/view/presenter_factory.rb b/lib/gitlab/view/presenter_factory.rb new file mode 100644 index 00000000000..c8cab1249da --- /dev/null +++ b/lib/gitlab/view/presenter_factory.rb @@ -0,0 +1,39 @@ +module Gitlab + module View + class PresenterFactory + def initialize(subject, user: nil) + @subject = subject + @user = user + end + + def fabricate! + presenter = + if presenter_class.ancestors.include?(SimpleDelegator) + delegator_presenter + else + simple_presenter + end + + presenter + .with_subject(subject) + .with_user(user) + end + + private + + attr_reader :subject, :user + + def presenter_class + "#{subject.class.name.demodulize}Presenter".constantize + end + + def delegator_presenter + presenter_class.new(subject) + end + + def simple_presenter + presenter_class.new + end + end + end +end -- cgit v1.2.1 From bf789ff567c71ff68c216bfa8f3d43e09b6f49fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 9 Jan 2017 21:45:49 +0100 Subject: Improve presenter architecture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/view/presenter.rb | 32 -------------------------------- lib/gitlab/view/presenter/base.rb | 26 ++++++++++++++++++++++++++ lib/gitlab/view/presenter/delegated.rb | 19 +++++++++++++++++++ lib/gitlab/view/presenter/simple.rb | 17 +++++++++++++++++ 4 files changed, 62 insertions(+), 32 deletions(-) delete mode 100644 lib/gitlab/view/presenter.rb create mode 100644 lib/gitlab/view/presenter/base.rb create mode 100644 lib/gitlab/view/presenter/delegated.rb create mode 100644 lib/gitlab/view/presenter/simple.rb (limited to 'lib') diff --git a/lib/gitlab/view/presenter.rb b/lib/gitlab/view/presenter.rb deleted file mode 100644 index 8b63b271b99..00000000000 --- a/lib/gitlab/view/presenter.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Gitlab - module View - module Presenter - extend ActiveSupport::Concern - - included do - include Gitlab::Routing - include Gitlab::Allowable - end - - def with_subject(subject) - tap { @subject = subject } - end - - def with_user(user) - tap { @user = user } - end - - private - - attr_reader :subject, :user - - class_methods do - def presents(name) - define_method(name) do - subject - end - end - end - end - end -end diff --git a/lib/gitlab/view/presenter/base.rb b/lib/gitlab/view/presenter/base.rb new file mode 100644 index 00000000000..51e7ab064fe --- /dev/null +++ b/lib/gitlab/view/presenter/base.rb @@ -0,0 +1,26 @@ +module Gitlab + module View + module Presenter + module Base + extend ActiveSupport::Concern + + include Gitlab::Routing + include Gitlab::Allowable + + attr_reader :subject + + def can?(user, action) + super(user, action, subject) + end + + private + + class_methods do + def presents(name) + define_method(name) { subject } + end + end + end + end + end +end diff --git a/lib/gitlab/view/presenter/delegated.rb b/lib/gitlab/view/presenter/delegated.rb new file mode 100644 index 00000000000..f4d330c590e --- /dev/null +++ b/lib/gitlab/view/presenter/delegated.rb @@ -0,0 +1,19 @@ +module Gitlab + module View + module Presenter + class Delegated < SimpleDelegator + include Gitlab::View::Presenter::Base + + def initialize(subject, **attributes) + @subject = subject + + attributes.each do |key, value| + define_singleton_method(key) { value } + end + + super(subject) + end + end + end + end +end diff --git a/lib/gitlab/view/presenter/simple.rb b/lib/gitlab/view/presenter/simple.rb new file mode 100644 index 00000000000..b7653a0f3cc --- /dev/null +++ b/lib/gitlab/view/presenter/simple.rb @@ -0,0 +1,17 @@ +module Gitlab + module View + module Presenter + class Simple + include Gitlab::View::Presenter::Base + + def initialize(subject, **attributes) + @subject = subject + + attributes.each do |key, value| + define_singleton_method(key) { value } + end + end + end + end + end +end -- cgit v1.2.1 From e5a29b451473c6f188d5096f21055d27a51fdf90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 9 Jan 2017 21:46:38 +0100 Subject: Improve presenter factory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/view/presenter/factory.rb | 22 ++++++++++++++++++++ lib/gitlab/view/presenter_factory.rb | 39 ------------------------------------ 2 files changed, 22 insertions(+), 39 deletions(-) create mode 100644 lib/gitlab/view/presenter/factory.rb delete mode 100644 lib/gitlab/view/presenter_factory.rb (limited to 'lib') diff --git a/lib/gitlab/view/presenter/factory.rb b/lib/gitlab/view/presenter/factory.rb new file mode 100644 index 00000000000..92979c61a25 --- /dev/null +++ b/lib/gitlab/view/presenter/factory.rb @@ -0,0 +1,22 @@ +module Gitlab + module View + module Presenter + class Factory + def initialize(subject, **attributes) + @subject = subject + @attributes = attributes + end + + def fabricate! + presenter_class.new(@subject, @attributes) + end + + private + + def presenter_class + @subject.class.const_get('Presenter') + end + end + end + end +end diff --git a/lib/gitlab/view/presenter_factory.rb b/lib/gitlab/view/presenter_factory.rb deleted file mode 100644 index c8cab1249da..00000000000 --- a/lib/gitlab/view/presenter_factory.rb +++ /dev/null @@ -1,39 +0,0 @@ -module Gitlab - module View - class PresenterFactory - def initialize(subject, user: nil) - @subject = subject - @user = user - end - - def fabricate! - presenter = - if presenter_class.ancestors.include?(SimpleDelegator) - delegator_presenter - else - simple_presenter - end - - presenter - .with_subject(subject) - .with_user(user) - end - - private - - attr_reader :subject, :user - - def presenter_class - "#{subject.class.name.demodulize}Presenter".constantize - end - - def delegator_presenter - presenter_class.new(subject) - end - - def simple_presenter - presenter_class.new - end - end - end -end -- cgit v1.2.1 From 061bb6eb6ed0ca6be3c571b3fcfd14a6f9729205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 10 Jan 2017 17:41:04 -0500 Subject: More improvements to presenters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/view/presenter/base.rb | 10 ++++++---- lib/gitlab/view/presenter/factory.rb | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/view/presenter/base.rb b/lib/gitlab/view/presenter/base.rb index 51e7ab064fe..83c8ba5c1cf 100644 --- a/lib/gitlab/view/presenter/base.rb +++ b/lib/gitlab/view/presenter/base.rb @@ -9,13 +9,15 @@ module Gitlab attr_reader :subject - def can?(user, action) - super(user, action, subject) + def can?(user, action, overriden_subject = nil) + super(user, action, overriden_subject || subject) end - private - class_methods do + def presenter? + true + end + def presents(name) define_method(name) { subject } end diff --git a/lib/gitlab/view/presenter/factory.rb b/lib/gitlab/view/presenter/factory.rb index 92979c61a25..d172d61e2c9 100644 --- a/lib/gitlab/view/presenter/factory.rb +++ b/lib/gitlab/view/presenter/factory.rb @@ -8,13 +8,15 @@ module Gitlab end def fabricate! - presenter_class.new(@subject, @attributes) + presenter_class.new(subject, attributes) end private + attr_reader :subject, :attributes + def presenter_class - @subject.class.const_get('Presenter') + "#{subject.class.name}Presenter".constantize end end end -- cgit v1.2.1 From 0f3c9355c1b57a56b4027df4deb78a2520596b15 Mon Sep 17 00:00:00 2001 From: Ruben Davila Date: Wed, 18 Jan 2017 10:48:16 -0600 Subject: Add some API endpoints for time tracking. New endpoints are: POST :project_id/(issues|merge_requests)/(:issue_id|:merge_request_id)/time_estimate" POST :project_id/(issues|merge_requests)/(:issue_id|:merge_request_id)/reset_time_estimate" POST :project_id/(issues|merge_requests)/(:issue_id|:merge_request_id)/add_spent_time" POST :project_id/(issues|merge_requests)/(:issue_id|:merge_request_id)/reset_spent_time" GET :project_id/(issues|merge_requests)/(:issue_id|:merge_request_id)/time_stats" --- lib/api/entities.rb | 7 +++ lib/api/helpers.rb | 4 ++ lib/api/issues.rb | 2 + lib/api/merge_requests.rb | 22 ++++--- lib/api/time_tracking_endpoints.rb | 114 ++++++++++++++++++++++++++++++++++ lib/gitlab/time_tracking_formatter.rb | 6 +- 6 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 lib/api/time_tracking_endpoints.rb (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 885ce7d44bc..9f59939e9ae 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -268,6 +268,13 @@ module API end end + class IssuableTimeStats < Grape::Entity + expose :time_estimate + expose :total_time_spent + expose :human_time_estimate + expose :human_total_time_spent + end + class ExternalIssue < Grape::Entity expose :title expose :id diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 20b5bc1502a..0fcb352000a 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -86,6 +86,10 @@ module API IssuesFinder.new(current_user, project_id: user_project.id).find(id) end + def find_project_merge_request(id) + MergeRequestsFinder.new(current_user, project_id: user_project.id).find(id) + end + def authenticate! unauthorized! unless current_user end diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 161269cbd41..fe016c1ec0a 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -89,6 +89,8 @@ module API requires :id, type: String, desc: 'The ID of a project' end resource :projects do + include TimeTrackingEndpoints + desc 'Get a list of project issues' do success Entities::Issue end diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 5d1fe22f2df..e77af4b7a0d 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -10,6 +10,8 @@ module API requires :id, type: String, desc: 'The ID of a project' end resource :projects do + include TimeTrackingEndpoints + helpers do def handle_merge_request_errors!(errors) if errors[:project_access].any? @@ -96,7 +98,7 @@ module API requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' end delete ":id/merge_requests/:merge_request_id" do - merge_request = user_project.merge_requests.find_by(id: params[:merge_request_id]) + merge_request = find_project_merge_request(params[:merge_request_id]) authorize!(:destroy_merge_request, merge_request) merge_request.destroy @@ -116,7 +118,7 @@ module API success Entities::MergeRequest end get path do - merge_request = user_project.merge_requests.find(params[:merge_request_id]) + merge_request = find_project_merge_request(params[:merge_request_id]) authorize! :read_merge_request, merge_request present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project end @@ -125,7 +127,7 @@ module API success Entities::RepoCommit end get "#{path}/commits" do - merge_request = user_project.merge_requests.find(params[:merge_request_id]) + merge_request = find_project_merge_request(params[:merge_request_id]) authorize! :read_merge_request, merge_request present merge_request.commits, with: Entities::RepoCommit end @@ -134,7 +136,7 @@ module API success Entities::MergeRequestChanges end get "#{path}/changes" do - merge_request = user_project.merge_requests.find(params[:merge_request_id]) + merge_request = find_project_merge_request(params[:merge_request_id]) authorize! :read_merge_request, merge_request present merge_request, with: Entities::MergeRequestChanges, current_user: current_user end @@ -153,7 +155,7 @@ module API :remove_source_branch end put path do - merge_request = user_project.merge_requests.find(params.delete(:merge_request_id)) + merge_request = find_project_merge_request(params.delete(:merge_request_id)) authorize! :update_merge_request, merge_request mr_params = declared_params(include_missing: false) @@ -180,7 +182,7 @@ module API optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch' end put "#{path}/merge" do - merge_request = user_project.merge_requests.find(params[:merge_request_id]) + merge_request = find_project_merge_request(params[:merge_request_id]) # Merge request can not be merged # because user dont have permissions to push into target branch @@ -216,7 +218,7 @@ module API success Entities::MergeRequest end post "#{path}/cancel_merge_when_build_succeeds" do - merge_request = user_project.merge_requests.find(params[:merge_request_id]) + merge_request = find_project_merge_request(params[:merge_request_id]) unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user) @@ -233,7 +235,7 @@ module API use :pagination end get "#{path}/comments" do - merge_request = user_project.merge_requests.find(params[:merge_request_id]) + merge_request = find_project_merge_request(params[:merge_request_id]) authorize! :read_merge_request, merge_request @@ -248,7 +250,7 @@ module API requires :note, type: String, desc: 'The text of the comment' end post "#{path}/comments" do - merge_request = user_project.merge_requests.find(params[:merge_request_id]) + merge_request = find_project_merge_request(params[:merge_request_id]) authorize! :create_note, merge_request opts = { @@ -273,7 +275,7 @@ module API use :pagination end get "#{path}/closes_issues" do - merge_request = user_project.merge_requests.find(params[:merge_request_id]) + merge_request = find_project_merge_request(params[:merge_request_id]) issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user)) present paginate(issues), with: issue_entity(user_project), current_user: current_user end diff --git a/lib/api/time_tracking_endpoints.rb b/lib/api/time_tracking_endpoints.rb new file mode 100644 index 00000000000..85b5f7d98b8 --- /dev/null +++ b/lib/api/time_tracking_endpoints.rb @@ -0,0 +1,114 @@ +module API + module TimeTrackingEndpoints + extend ActiveSupport::Concern + + included do + helpers do + def issuable_name + declared_params.has_key?(:issue_id) ? 'issue' : 'merge_request' + end + + def issuable_key + "#{issuable_name}_id".to_sym + end + + def update_issuable_key + "update_#{issuable_name}".to_sym + end + + def read_issuable_key + "read_#{issuable_name}".to_sym + end + + def load_issuable + @issuable ||= begin + case issuable_name + when 'issue' + find_project_issue(params.delete(issuable_key)) + when 'merge_request' + find_project_merge_request(params.delete(issuable_key)) + end + end + end + + def update_issuable(attrs) + custom_params = declared_params(include_missing: false) + custom_params.merge!(attrs) + + issuable = update_service.new(user_project, current_user, custom_params).execute(load_issuable) + if issuable.valid? + present issuable, with: Entities::IssuableTimeStats + else + render_validation_error!(issuable) + end + end + + def update_service + issuable_name == 'issue' ? ::Issues::UpdateService : ::MergeRequests::UpdateService + end + end + + issuable_name = name.end_with?('Issues') ? 'issue' : 'merge_request' + issuable_collection_name = issuable_name.pluralize + issuable_key = "#{issuable_name}_id".to_sym + + desc "Set a time estimate for a project #{issuable_name}" + params do + requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}" + requires :duration, type: String, desc: 'The duration to be parsed' + end + post ":id/#{issuable_collection_name}/:#{issuable_key}/time_estimate" do + authorize! update_issuable_key, load_issuable + + status :ok + update_issuable(time_estimate: Gitlab::TimeTrackingFormatter.parse(params.delete(:duration))) + end + + desc "Reset the time estimate for a project #{issuable_name}" + params do + requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}" + end + post ":id/#{issuable_collection_name}/:#{issuable_key}/reset_time_estimate" do + authorize! update_issuable_key, load_issuable + + status :ok + update_issuable(time_estimate: 0) + end + + desc "Add spent time for a project #{issuable_name}" + params do + requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}" + requires :duration, type: String, desc: 'The duration to be parsed' + end + post ":id/#{issuable_collection_name}/:#{issuable_key}/add_spent_time" do + authorize! update_issuable_key, load_issuable + + update_issuable(spend_time: { + duration: Gitlab::TimeTrackingFormatter.parse(params.delete(:duration)), + user: current_user + }) + end + + desc "Reset spent time for a project #{issuable_name}" + params do + requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}" + end + post ":id/#{issuable_collection_name}/:#{issuable_key}/reset_spent_time" do + authorize! update_issuable_key, load_issuable + + status :ok + update_issuable(spend_time: { duration: :reset, user: current_user }) + end + + desc "Show time stats for a project #{issuable_name}" + params do + requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}" + end + get ":id/#{issuable_collection_name}/:#{issuable_key}/time_stats" do + authorize! read_issuable_key, load_issuable + + present load_issuable, with: Entities::IssuableTimeStats + end + end + end +end diff --git a/lib/gitlab/time_tracking_formatter.rb b/lib/gitlab/time_tracking_formatter.rb index d09063c6c8f..d615c24149a 100644 --- a/lib/gitlab/time_tracking_formatter.rb +++ b/lib/gitlab/time_tracking_formatter.rb @@ -4,7 +4,11 @@ module Gitlab def parse(string) with_custom_config do - ChronicDuration.parse(string, default_unit: 'hours') rescue nil + string.sub!(/\A-/, '') + + seconds = ChronicDuration.parse(string, default_unit: 'hours') rescue nil + seconds *= -1 if seconds && Regexp.last_match + seconds end end -- cgit v1.2.1