Commit 47a0276e authored by Z.J. van de Weg's avatar Z.J. van de Weg

Initial implementation for real time job view

Added the needed keys and paths to a new entity, BuildDetailsEntity.
Not renaming BuildEntity to BuildBasicEntity on explicit request. Most
code now has test coverage, but not all. This will be added on later
commits on this branch.

Resolves gitlab-org/gitlab-ce#31397
parent f06daa26
...@@ -45,6 +45,17 @@ class Projects::JobsController < Projects::ApplicationController ...@@ -45,6 +45,17 @@ class Projects::JobsController < Projects::ApplicationController
@builds = @project.pipelines.find_by_sha(@build.sha).builds.order('id DESC') @builds = @project.pipelines.find_by_sha(@build.sha).builds.order('id DESC')
@builds = @builds.where("id not in (?)", @build.id) @builds = @builds.where("id not in (?)", @build.id)
@pipeline = @build.pipeline @pipeline = @build.pipeline
respond_to do |format|
format.html
format.json do
Gitlab::PollingInterval.set_header(response, interval: 10_000)
render json: BuildSerializer
.new(project: @project, current_user: @current_user)
.represent_status(@build, {}, BuildDetailsEntity)
end
end
end end
def trace def trace
......
...@@ -204,14 +204,17 @@ module Ci ...@@ -204,14 +204,17 @@ module Ci
end end
def merge_request def merge_request
merge_requests = MergeRequest.includes(:merge_request_diff) @merge_request ||=
.where(source_branch: ref, begin
source_project: pipeline.project) merge_requests = MergeRequest.includes(:merge_request_diff)
.reorder(iid: :asc) .where(source_branch: ref,
source_project: pipeline.project)
merge_requests.find do |merge_request| .reorder(iid: :asc)
merge_request.commits_sha.include?(pipeline.sha)
end merge_requests.find do |merge_request|
merge_request.commits_sha.include?(pipeline.sha)
end
end
end end
def repo_url def repo_url
......
class BuildDetailsEntity < BuildEntity
expose :coverage, :erased_at, :duration
expose :tag_list, as: :tags
expose :artifacts, using: BuildArtifactEntity
expose :runner, using: RunnerEntity
expose :pipeline, using: PipelineEntity
expose :merge_request_path do |build|
merge_request = build.merge_request
project = build.project
if merge_request.nil? || !can?(request.current_user, :read_merge_request, project)
nil
else
namespace_project_merge_request_path(project.namespace, project, merge_request)
end
end
expose :new_issue_path do |build|
project = build.project
unless build.failed? && can?(request.current_user, :create_issue, project)
nil
else
new_namespace_project_issue_path(project.namespace, project)
end
end
expose :raw_path do |build|
project = build.project
raw_namespace_project_build_path(project.namespace, project, build)
end
end
class BuildSerializer < BaseSerializer class BuildSerializer < BaseSerializer
entity BuildEntity entity BuildEntity
def represent_status(resource) def represent_status(resource, opts = {}, entity_class = nil)
data = represent(resource, { only: [:status] }) data = represent(resource, { only: [:status] })
data.fetch(:status, {}) data.fetch(:status, {})
represent(resource, opts, entity_class)
end end
end end
class RunnerEntity < Grape::Entity
expose :id, :name, :description
end
---
title: Job details page update real time
merge_request: 11651
author:
...@@ -9,7 +9,9 @@ module Gitlab ...@@ -9,7 +9,9 @@ module Gitlab
# - Ending in `noteable/issue/<id>/notes` for the `issue_notes` route # - Ending in `noteable/issue/<id>/notes` for the `issue_notes` route
# - Ending in `issues/id`/realtime_changes` for the `issue_title` route # - Ending in `issues/id`/realtime_changes` for the `issue_title` route
USED_IN_ROUTES = %w[noteable issue notes issues realtime_changes USED_IN_ROUTES = %w[noteable issue notes issues realtime_changes
commit pipelines merge_requests new].freeze commit pipelines merge_requests builds
new].freeze
RESERVED_WORDS = Gitlab::PathRegex::ILLEGAL_PROJECT_PATH_WORDS - USED_IN_ROUTES RESERVED_WORDS = Gitlab::PathRegex::ILLEGAL_PROJECT_PATH_WORDS - USED_IN_ROUTES
RESERVED_WORDS_REGEX = Regexp.union(*RESERVED_WORDS.map(&Regexp.method(:escape))) RESERVED_WORDS_REGEX = Regexp.union(*RESERVED_WORDS.map(&Regexp.method(:escape)))
ROUTES = [ ROUTES = [
...@@ -40,6 +42,10 @@ module Gitlab ...@@ -40,6 +42,10 @@ module Gitlab
Gitlab::EtagCaching::Router::Route.new( Gitlab::EtagCaching::Router::Route.new(
%r(^(?!.*(#{RESERVED_WORDS_REGEX})).*/pipelines/\d+\.json\z), %r(^(?!.*(#{RESERVED_WORDS_REGEX})).*/pipelines/\d+\.json\z),
'project_pipeline' 'project_pipeline'
),
Gitlab::EtagCaching::Router::Route.new(
%r(^(?!.*(#{RESERVED_WORDS_REGEX})).*/builds/\d+\.json\z),
'project_build'
) )
].freeze ].freeze
......
...@@ -101,26 +101,48 @@ describe Projects::JobsController do ...@@ -101,26 +101,48 @@ describe Projects::JobsController do
end end
describe 'GET show' do describe 'GET show' do
context 'when build exists' do let!(:build) { create(:ci_build, :failed, pipeline: pipeline) }
let!(:build) { create(:ci_build, pipeline: pipeline) }
before do context 'when requesting HTML' do
get_show(id: build.id) context 'when build exists' do
before do
get_show(id: build.id)
end
it 'has a build' do
expect(response).to have_http_status(:ok)
expect(assigns(:build).id).to eq(build.id)
end
end end
it 'has a build' do context 'when build does not exist' do
expect(response).to have_http_status(:ok) before do
expect(assigns(:build).id).to eq(build.id) get_show(id: 1234)
end
it 'renders not_found' do
expect(response).to have_http_status(:not_found)
end
end end
end end
context 'when build does not exist' do context 'when requesting JSON' do
let(:merge_request) { create(:merge_request, source_project: project) }
before do before do
get_show(id: 1234) project.add_developer(user)
sign_in(user)
allow_any_instance_of(Ci::Build).to receive(:merge_request).and_return(merge_request)
get_show(id: build.id, format: :json)
end end
it 'renders not_found' do it 'exposes needed information' do
expect(response).to have_http_status(:not_found) expect(response).to have_http_status(:ok)
expect(json_response['new_issue_path']).to end_with('/issues/new')
expect(json_response['raw_path']).to match(/builds\/\d+\/raw\z/)
expect(json_response['merge_request_path']).to match(/merge_requests\/\d+\z/)
end end
end end
......
...@@ -67,6 +67,17 @@ describe Gitlab::EtagCaching::Router do ...@@ -67,6 +67,17 @@ describe Gitlab::EtagCaching::Router do
expect(result.name).to eq 'merge_request_pipelines' expect(result.name).to eq 'merge_request_pipelines'
end end
it 'matches build endpoint' do
env = build_env(
'/my-group/my-project/builds/234.json'
)
result = described_class.match(env)
expect(result).to be_present
expect(result.name).to eq 'project_build'
end
it 'does not match blob with confusing name' do it 'does not match blob with confusing name' do
env = build_env( env = build_env(
'/my-group/my-project/blob/master/pipelines.json' '/my-group/my-project/blob/master/pipelines.json'
......
require 'spec_helper'
describe BuildDetailsEntity do
it 'inherits from BuildEntity' do
expect(described_class).to be < BuildEntity
end
describe '#as_json' do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let!(:build) { create(:ci_build, :failed, project: project) }
let(:request) { double('request') }
let(:entity) { described_class.new(build, request: request, current_user: user, project: project) }
subject { entity.as_json }
before do
allow(request).to receive(:current_user).and_return(user)
project.add_master(user)
end
context 'when the user has access to issues and merge requests' do
let!(:merge_request) { create(:merge_request, source_project: project) }
it 'contains the needed key value pairs' do
expect(subject).to include(:coverage, :erased_at, :duration)
expect(subject).to include(:artifacts, :runner, :pipeline)
expect(subject).to include(:raw_path, :merge_request_path, :new_issue_path)
end
end
context 'when the user can only read the build' do
it "won't display the paths to issues and merge requests" do
expect(subject['new_issue_path']).to be_nil
expect(subject['merge_request_path']).to be_nil
end
end
end
end
require 'spec_helper'
describe RunnerEntity do
let(:runner) { build(:ci_runner) }
let(:entity) { described_class.represent(runner) }
describe '#as_json' do
subject { entity.as_json }
it 'contains required fields' do
expect(subject).to include(:id, :name, :description)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment