Commit 1a8053a4 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch '11367-expose-design-blobs-through-controller' into 'master'

Expose design images to front-end

Closes #11367

See merge request gitlab-org/gitlab-ee!13037
parents 4a6d48e1 7d018487
......@@ -15,13 +15,9 @@ const defaultClient = createDefaultClient({
variables: { fullPath: projectPath, iid: issueIid },
});
return {
...result.project.issue.designs.designs.edges.find(
({ node }) => parseInt(node.id, 10) === id,
).node,
// TODO: Remove this once backend exposes raw images
image: 'http://via.placeholder.com/1000',
};
return result.project.issue.designs.designs.edges.find(
({ node }) => parseInt(node.id, 10) === id,
).node;
},
},
});
......
......@@ -19,12 +19,7 @@ export default {
iid: this.issueIid,
};
},
update: data =>
data.project.issue.designs.designs.edges.map(({ node }) => ({
...node,
// TODO: Remove this once backend exposes raw images
image: 'http://via.placeholder.com/1000',
})),
update: data => data.project.issue.designs.designs.edges.map(({ node }) => node),
error() {
this.error = true;
},
......
# 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
......
---
title: Expose Design blobs through GraphQL
merge_request: 13037
author:
type: added
......@@ -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)
......
......@@ -35,7 +35,7 @@ describe Resolvers::DesignManagement::VersionResolver do
end
end
context "when the is anonymous" do
context "when the user is anonymous" do
let(:current_user) { nil }
it "returns nothing" do
......@@ -43,7 +43,7 @@ describe Resolvers::DesignManagement::VersionResolver do
end
end
context "when the is cannot see designs" do
context "when the user cannot see designs" do
it "returns nothing" do
expect(resolve_versions(first_design, {}, current_user: create(:user))).to be_empty
end
......
......@@ -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