Commit 546ec46d authored by Luke Duncalfe's avatar Luke Duncalfe

Expose design images

A new controller exposes the design blob to the client. The path for
this resource is now also provided in the GraphQL DesignType.

https://gitlab.com/gitlab-org/gitlab-ee/issues/11367
parent d8d0bc3f
# frozen_string_literal: true
class Projects::DesignsController < Projects::ApplicationController
include SendsBlob
before_action :authorize_read_design!
def show
blob = design_repository.blob_at(ref, design.full_path)
send_blob(design_repository, blob, inline: (params[:inline] != 'false'))
end
private
def ref
@ref ||= params[:ref] || design_repository.root_ref
end
def design
@design ||= project.designs.find(params[:id])
end
def design_repository
@design_repository ||= @project.design_repository
end
def authorize_read_design!
unless can?(current_user, :read_design, design)
access_denied!
end
end
end
......@@ -11,6 +11,9 @@ module Types
field :project, Types::ProjectType, null: false
field :issue, Types::IssueType, null: false
field :filename, GraphQL::STRING_TYPE, null: false
field :image, GraphQL::STRING_TYPE, null: false, resolve: -> (design, _args, _ctx) do
Gitlab::Routing.url_helpers.project_design_url(design.project, design)
end
field :versions,
Types::DesignManagement::VersionType.connection_type,
resolver: Resolvers::DesignManagement::VersionResolver,
......
......@@ -4,7 +4,7 @@ module DesignManagement
class Design < ApplicationRecord
include Gitlab::FileTypeDetection
belongs_to :project
belongs_to :project, inverse_of: :designs
belongs_to :issue
has_many :design_versions
......
......@@ -20,7 +20,7 @@ module DesignManagement
end
def repository
@repository ||= ::DesignManagement::Repository.new(project)
project.design_repository
end
end
end
......@@ -51,6 +51,7 @@ module EE
has_many :approver_groups, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :approval_rules, class_name: 'ApprovalProjectRule'
has_many :audit_events, as: :entity
has_many :designs, inverse_of: :project, class_name: 'DesignManagement::Design'
has_many :path_locks
has_many :vulnerability_feedback, class_name: 'Vulnerabilities::Feedback'
has_many :vulnerabilities, class_name: 'Vulnerabilities::Occurrence'
......@@ -582,6 +583,10 @@ module EE
::Gitlab::Graphql.enabled?
end
def design_repository
@design_repository ||= DesignManagement::Repository.new(self)
end
private
def set_override_pull_mirror_available
......
......@@ -71,6 +71,14 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
end
scope '-' do
resources :designs, only: [], constraints: { id: /\d+/ } do
member do
get '(*ref)', action: 'show', as: '', constraints: { ref: Gitlab::PathRegex.git_reference_regex }
end
end
end
end
end
end
......
# frozen_string_literal: true
require 'spec_helper'
describe Projects::DesignsController do
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project) }
let(:design) { create(:design, :with_file, issue: issue) }
before do
stub_licensed_features(design_management: true)
end
describe 'GET #show' do
it 'serves the file using workhorse' do
get(:show,
params: {
namespace_id: project.namespace,
project_id: project,
id: design.id,
ref: 'HEAD'
})
expect(response).to have_gitlab_http_status(200)
expect(response.header['Content-Disposition']).to eq('inline')
expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
end
end
end
......@@ -5,5 +5,37 @@ FactoryBot.define do
issue
project { issue.project }
sequence(:filename) { |n| "homescreen-#{n}.jpg" }
trait :with_file do
transient do
versions_count 1
file File.join(Rails.root, 'spec/fixtures/dk.png')
end
after :create do |design, evaluator|
unless evaluator.versions_count.zero?
project = design.project
repository = project.design_repository
repository.create_if_not_exists
evaluator.versions_count.times do |i|
actions = [{
action: i.zero? ? :create : :update, # First version is :create, successive versions are :update
file_path: design.full_path,
content: evaluator.file
}]
sha = repository.multi_action(
project.creator,
branch_name: 'master',
message: "Automatically created file #{design.filename}",
actions: actions
)
FactoryBot.create(:design_version, designs: [design], sha: sha)
end
end
end
end
end
end
......@@ -5,8 +5,8 @@ describe 'User paginates issue designs', :js do
let(:issue) { create(:issue, project: project) }
before do
create(:design, issue: issue, filename: 'world.png')
create(:design, issue: issue, filename: 'dk.png')
create(:design, :with_file, issue: issue, filename: 'world.png')
create(:design, :with_file, issue: issue, filename: 'dk.png')
stub_licensed_features(design_management: true)
......
# frozen_string_literal: true
require 'spec_helper'
describe 'Users views raw design image files' do
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project) }
let(:design) { create(:design, :with_file, issue: issue, versions_count: 2) }
let(:newest_version) { design.versions.ordered.first }
let(:oldest_version) { design.versions.ordered.last }
before do
stub_licensed_features(design_management: true)
end
it 'serves the latest design version when no ref is given' do
visit project_design_path(design.project, design)
expect(response_headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to eq(
workhorse_data_header_for_version(oldest_version.sha)
)
end
it 'serves the correct design version when a ref is given' do
visit project_design_path(design.project, design, oldest_version.sha)
expect(response_headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to eq(
workhorse_data_header_for_version(oldest_version.sha)
)
end
private
def workhorse_data_header_for_version(ref)
blob = project.design_repository.blob_at(ref, design.full_path)
Gitlab::Workhorse.send_git_blob(project.design_repository, blob).last
end
end
......@@ -5,7 +5,7 @@ describe 'User views issue designs', :js do
let(:issue) { create(:issue, project: project) }
before do
create(:design, issue: issue, filename: 'world.png')
create(:design, :with_file, issue: issue, filename: 'world.png')
stub_licensed_features(design_management: true)
......
......@@ -5,7 +5,7 @@ describe 'User views issue designs', :js do
let(:issue) { create(:issue, project: project) }
before do
create(:design, issue: issue, filename: 'world.png')
create(:design, :with_file, issue: issue, filename: 'world.png')
stub_licensed_features(design_management: true)
......
......@@ -5,5 +5,5 @@ require 'spec_helper'
describe GitlabSchema.types['Design'] do
it { expect(described_class).to require_graphql_authorizations(:read_design) }
it { expect(described_class).to have_graphql_fields(:id, :project, :issue, :filename, :versions) }
it { expect(described_class).to have_graphql_fields(:id, :project, :issue, :filename, :image, :versions) }
end
......@@ -88,6 +88,7 @@ project:
- reviews
- incident_management_setting
- merge_trains
- designs
prometheus_metrics:
- project
- prometheus_alerts
......
......@@ -28,4 +28,12 @@ describe 'EE-specific project routing' do
expect(get('/gitlab/gitlabhq/pipelines/12/security')).to route_to('projects/pipelines#security', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '12')
end
end
describe Projects::DesignsController, 'routing' do
it 'to #show' do
expect(get('/gitlab/gitlabhq/-/designs/1/master')).to route_to('projects/designs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', ref: 'master')
expect(get('/gitlab/gitlabhq/-/designs/1/my/branch')).to route_to('projects/designs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', ref: 'my/branch')
expect(get('/gitlab/gitlabhq/-/designs/1/f166f5c7afaed9e1236e4e5965585f235795db4c3f45e8a9f6ea9dde098c')).to route_to('projects/designs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', ref: 'f166f5c7afaed9e1236e4e5965585f235795db4c3f45e8a9f6ea9dde098c')
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