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
This commit is contained in:
Z.J. van de Weg 2017-05-23 17:10:07 +02:00
parent f06daa26ef
commit 47a0276e53
11 changed files with 169 additions and 20 deletions

View File

@ -45,6 +45,17 @@ class Projects::JobsController < Projects::ApplicationController
@builds = @project.pipelines.find_by_sha(@build.sha).builds.order('id DESC')
@builds = @builds.where("id not in (?)", @build.id)
@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
def trace

View File

@ -204,14 +204,17 @@ module Ci
end
def merge_request
merge_requests = MergeRequest.includes(:merge_request_diff)
.where(source_branch: ref,
source_project: pipeline.project)
.reorder(iid: :asc)
@merge_request ||=
begin
merge_requests = MergeRequest.includes(:merge_request_diff)
.where(source_branch: ref,
source_project: pipeline.project)
.reorder(iid: :asc)
merge_requests.find do |merge_request|
merge_request.commits_sha.include?(pipeline.sha)
end
merge_requests.find do |merge_request|
merge_request.commits_sha.include?(pipeline.sha)
end
end
end
def repo_url

View File

@ -0,0 +1,34 @@
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

View File

@ -1,8 +1,10 @@
class BuildSerializer < BaseSerializer
entity BuildEntity
def represent_status(resource)
def represent_status(resource, opts = {}, entity_class = nil)
data = represent(resource, { only: [:status] })
data.fetch(:status, {})
represent(resource, opts, entity_class)
end
end

View File

@ -0,0 +1,3 @@
class RunnerEntity < Grape::Entity
expose :id, :name, :description
end

View File

@ -0,0 +1,4 @@
---
title: Job details page update real time
merge_request: 11651
author:

View File

@ -9,7 +9,9 @@ module Gitlab
# - Ending in `noteable/issue/<id>/notes` for the `issue_notes` route
# - Ending in `issues/id`/realtime_changes` for the `issue_title` route
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_REGEX = Regexp.union(*RESERVED_WORDS.map(&Regexp.method(:escape)))
ROUTES = [
@ -40,6 +42,10 @@ module Gitlab
Gitlab::EtagCaching::Router::Route.new(
%r(^(?!.*(#{RESERVED_WORDS_REGEX})).*/pipelines/\d+\.json\z),
'project_pipeline'
),
Gitlab::EtagCaching::Router::Route.new(
%r(^(?!.*(#{RESERVED_WORDS_REGEX})).*/builds/\d+\.json\z),
'project_build'
)
].freeze

View File

@ -101,26 +101,48 @@ describe Projects::JobsController do
end
describe 'GET show' do
context 'when build exists' do
let!(:build) { create(:ci_build, pipeline: pipeline) }
let!(:build) { create(:ci_build, :failed, pipeline: pipeline) }
before do
get_show(id: build.id)
context 'when requesting HTML' do
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
it 'has a build' do
expect(response).to have_http_status(:ok)
expect(assigns(:build).id).to eq(build.id)
context 'when build does not exist' do
before do
get_show(id: 1234)
end
it 'renders not_found' do
expect(response).to have_http_status(:not_found)
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
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
it 'renders not_found' do
expect(response).to have_http_status(:not_found)
it 'exposes needed information' do
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

View File

@ -67,6 +67,17 @@ describe Gitlab::EtagCaching::Router do
expect(result.name).to eq 'merge_request_pipelines'
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
env = build_env(
'/my-group/my-project/blob/master/pipelines.json'

View File

@ -0,0 +1,39 @@
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

View File

@ -0,0 +1,14 @@
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