Commit 905e52f6 authored by Peter Leitzen's avatar Peter Leitzen

Merge branch 'nfriend-add-links-to-release-graphql' into 'master'

Add links to Release data from GraphQL endpoint

See merge request gitlab-org/gitlab!33612
parents d095b936 a524777a
# frozen_string_literal: true
module Types
class ReleaseLinksType < BaseObject
graphql_name 'ReleaseLinks'
authorize :download_code
alias_method :release, :object
present_using ReleasePresenter
field :self_url, GraphQL::STRING_TYPE, null: true,
description: 'HTTP URL of the release'
field :merge_requests_url, GraphQL::STRING_TYPE, null: true,
description: 'HTTP URL of the merge request page filtered by this release'
field :issues_url, GraphQL::STRING_TYPE, null: true,
description: 'HTTP URL of the issues page filtered by this release'
field :edit_url, GraphQL::STRING_TYPE, null: true,
description: "HTTP URL of the release's edit page",
authorize: :update_release
end
end
......@@ -28,6 +28,8 @@ module Types
description: 'Timestamp of when the release was released'
field :assets, Types::ReleaseAssetsType, null: true, method: :itself,
description: 'Assets of the release'
field :links, Types::ReleaseLinksType, null: true, method: :itself,
description: 'Links of the release'
field :milestones, Types::MilestoneType.connection_type, null: true,
description: 'Milestones associated to the release'
field :evidences, Types::EvidenceType.connection_type, null: true,
......
......@@ -10161,6 +10161,11 @@ type Release {
last: Int
): ReleaseEvidenceConnection
"""
Links of the release
"""
links: ReleaseLinks
"""
Milestones associated to the release
"""
......@@ -10452,6 +10457,28 @@ type ReleaseEvidenceEdge {
node: ReleaseEvidence
}
type ReleaseLinks {
"""
HTTP URL of the release's edit page
"""
editUrl: String
"""
HTTP URL of the issues page filtered by this release
"""
issuesUrl: String
"""
HTTP URL of the merge request page filtered by this release
"""
mergeRequestsUrl: String
"""
HTTP URL of the release
"""
selfUrl: String
}
"""
Represents the source code attached to a release in a particular format
"""
......
......@@ -29727,6 +29727,20 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "links",
"description": "Links of the release",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "ReleaseLinks",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "milestones",
"description": "Milestones associated to the release",
......@@ -30508,6 +30522,75 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ReleaseLinks",
"description": null,
"fields": [
{
"name": "editUrl",
"description": "HTTP URL of the release's edit page",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "issuesUrl",
"description": "HTTP URL of the issues page filtered by this release",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "mergeRequestsUrl",
"description": "HTTP URL of the merge request page filtered by this release",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "selfUrl",
"description": "HTTP URL of the release",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ReleaseSource",
......@@ -1410,6 +1410,7 @@ Represents a release
| `createdAt` | Time | Timestamp of when the release was created |
| `description` | String | Description (also known as "release notes") of the release |
| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` |
| `links` | ReleaseLinks | Links of the release |
| `name` | String | Name of the release |
| `releasedAt` | Time | Timestamp of when the release was released |
| `tagName` | String | Name of the tag associated with the release |
......@@ -1446,6 +1447,15 @@ Evidence for a release
| `id` | ID! | ID of the evidence |
| `sha` | String | SHA1 ID of the evidence hash |
## ReleaseLinks
| Name | Type | Description |
| --- | ---- | ---------- |
| `editUrl` | String | HTTP URL of the release's edit page |
| `issuesUrl` | String | HTTP URL of the issues page filtered by this release |
| `mergeRequestsUrl` | String | HTTP URL of the merge request page filtered by this release |
| `selfUrl` | String | HTTP URL of the release |
## ReleaseSource
Represents the source code attached to a release in a particular format
......
# frozen_string_literal: true
require 'spec_helper'
describe GitlabSchema.types['ReleaseLinks'] do
it { expect(described_class).to require_graphql_authorizations(:download_code) }
it 'has the expected fields' do
expected_fields = %w[
selfUrl
mergeRequestsUrl
issuesUrl
editUrl
]
expect(described_class).to include_graphql_fields(*expected_fields)
end
end
......@@ -9,7 +9,8 @@ describe GitlabSchema.types['Release'] do
expected_fields = %w[
tag_name tag_path
description description_html
name assets milestones evidences author commit
name milestones evidences author commit
assets links
created_at released_at
]
......@@ -22,6 +23,12 @@ describe GitlabSchema.types['Release'] do
it { is_expected.to have_graphql_type(Types::ReleaseAssetsType) }
end
describe 'links field' do
subject { described_class.fields['links'] }
it { is_expected.to have_graphql_type(Types::ReleaseLinksType) }
end
describe 'milestones field' do
subject { described_class.fields['milestones'] }
......
# frozen_string_literal: true
require 'spec_helper'
require 'pp'
describe 'Query.project(fullPath).release(tagName)' do
include GraphqlHelpers
......@@ -12,15 +11,19 @@ describe 'Query.project(fullPath).release(tagName)' do
let_it_be(:reporter) { create(:user) }
let_it_be(:stranger) { create(:user) }
let(:params_for_issues_and_mrs) { { scope: 'all', state: 'opened', release_tag: release.tag } }
let(:post_query) { post_graphql(query, current_user: current_user) }
let(:path_prefix) { %w[project release] }
let(:data) { graphql_data.dig(*path) }
def query(rq = release_fields)
graphql_query_for(:project, { fullPath: project.full_path },
query_graphql_field(:release, { tagName: release.tag }, rq))
end
let(:post_query) { post_graphql(query, current_user: current_user) }
let(:path_prefix) { %w[project release] }
let(:data) { graphql_data.dig(*path) }
before do
stub_default_url_options(host: 'www.example.com')
end
shared_examples 'full access to the release field' do
describe 'scalar fields' do
......@@ -83,10 +86,10 @@ describe 'Query.project(fullPath).release(tagName)' do
it 'finds the author of the release' do
post_query
expect(data).to eq({
expect(data).to eq(
'id' => global_id_of(release.author),
'username' => release.author.username
})
)
end
end
......@@ -100,7 +103,7 @@ describe 'Query.project(fullPath).release(tagName)' do
it 'finds the commit associated with the release' do
post_query
expect(data).to eq({ 'sha' => release.commit.sha })
expect(data).to eq('sha' => release.commit.sha)
end
end
......@@ -115,7 +118,7 @@ describe 'Query.project(fullPath).release(tagName)' do
it 'returns the number of assets associated to the release' do
post_query
expect(data).to eq({ 'count' => release.sources.size + release.links.size })
expect(data).to eq('count' => release.sources.size + release.links.size)
end
end
......@@ -166,6 +169,28 @@ describe 'Query.project(fullPath).release(tagName)' do
end
end
describe 'links' do
let(:path) { path_prefix + %w[links] }
let(:release_fields) do
query_graphql_field(:links, nil, %{
selfUrl
mergeRequestsUrl
issuesUrl
})
end
it 'finds all release links' do
post_query
expect(data).to eq(
'selfUrl' => project_release_url(project, release),
'mergeRequestsUrl' => project_merge_requests_url(project, params_for_issues_and_mrs),
'issuesUrl' => project_issues_url(project, params_for_issues_and_mrs)
)
end
end
describe 'evidences' do
let(:path) { path_prefix + %w[evidences] }
......@@ -177,14 +202,13 @@ describe 'Query.project(fullPath).release(tagName)' do
post_query
evidence = release.evidences.first.present
expected = {
expect(data["nodes"].first).to eq(
'id' => global_id_of(evidence),
'sha' => evidence.sha,
'filepath' => evidence.filepath,
'collectedAt' => evidence.collected_at.utc.iso8601
}
expect(data["nodes"].first).to eq(expected)
)
end
end
end
......@@ -207,6 +231,38 @@ describe 'Query.project(fullPath).release(tagName)' do
end
end
shared_examples 'access to editUrl' do
let(:path) { path_prefix + %w[links] }
let(:release_fields) do
query_graphql_field(:links, nil, 'editUrl')
end
before do
post_query
end
it 'returns editUrl' do
expect(data).to eq('editUrl' => edit_project_release_url(project, release))
end
end
shared_examples 'no access to editUrl' do
let(:path) { path_prefix + %w[links] }
let(:release_fields) do
query_graphql_field(:links, nil, 'editUrl')
end
before do
post_query
end
it 'does not return editUrl' do
expect(data).to eq('editUrl' => nil)
end
end
describe "ensures that the correct data is returned based on the project's visibility and the user's access level" do
context 'when the project is private' do
let_it_be(:project) { create(:project, :repository, :private) }
......@@ -238,12 +294,14 @@ describe 'Query.project(fullPath).release(tagName)' do
let(:current_user) { reporter }
it_behaves_like 'full access to the release field'
it_behaves_like 'no access to editUrl'
end
context 'when the user has Developer permissions' do
let(:current_user) { developer }
it_behaves_like 'full access to the release field'
it_behaves_like 'access to editUrl'
end
end
......@@ -265,12 +323,21 @@ describe 'Query.project(fullPath).release(tagName)' do
let(:current_user) { stranger }
it_behaves_like 'full access to the release field'
it_behaves_like 'no access to editUrl'
end
context 'when the user has Guest permissions' do
let(:current_user) { guest }
it_behaves_like 'full access to the release field'
it_behaves_like 'no access to editUrl'
end
context 'when the user has Reporter permissions' do
let(:current_user) { reporter }
it_behaves_like 'full access to the release field'
it_behaves_like 'no access to editUrl'
end
context 'when the user has Reporter permissions' do
......@@ -283,6 +350,7 @@ describe 'Query.project(fullPath).release(tagName)' do
let(:current_user) { developer }
it_behaves_like 'full access to the release field'
it_behaves_like 'access to editUrl'
end
end
end
......
......@@ -5,9 +5,10 @@ require 'spec_helper'
describe 'Query.project(fullPath).releases()' do
include GraphqlHelpers
let_it_be(:stranger) { create(:user) }
let_it_be(:guest) { create(:user) }
let_it_be(:reporter) { create(:user) }
let_it_be(:stranger) { create(:user) }
let_it_be(:developer) { create(:user) }
let(:query) do
graphql_query_for(:project, { fullPath: project.full_path },
......@@ -33,15 +34,25 @@ describe 'Query.project(fullPath).releases()' do
sha
}
}
links {
selfUrl
mergeRequestsUrl
issuesUrl
}
}
}
})
end
let(:params_for_issues_and_mrs) { { scope: 'all', state: 'opened', release_tag: release.tag } }
let(:post_query) { post_graphql(query, current_user: current_user) }
let(:data) { graphql_data.dig('project', 'releases', 'nodes', 0) }
before do
stub_default_url_options(host: 'www.example.com')
end
shared_examples 'full access to all repository-related fields' do
describe 'repository-related fields' do
before do
......@@ -57,7 +68,7 @@ describe 'Query.project(fullPath).releases()' do
{ 'sha' => e.sha }
end
expect(data).to eq({
expect(data).to eq(
'tagName' => release.tag,
'tagPath' => project_tag_path(project, release.tag),
'name' => release.name,
......@@ -72,8 +83,13 @@ describe 'Query.project(fullPath).releases()' do
},
'evidences' => {
'nodes' => expected_evidences
},
'links' => {
'selfUrl' => project_release_url(project, release),
'mergeRequestsUrl' => project_merge_requests_url(project, params_for_issues_and_mrs),
'issuesUrl' => project_issues_url(project, params_for_issues_and_mrs)
}
})
)
end
end
end
......@@ -85,7 +101,7 @@ describe 'Query.project(fullPath).releases()' do
end
it 'does not return data for fields that expose repository information' do
expect(data).to eq({
expect(data).to eq(
'tagName' => nil,
'tagPath' => nil,
'name' => "Release-#{release.id}",
......@@ -98,9 +114,76 @@ describe 'Query.project(fullPath).releases()' do
},
'evidences' => {
'nodes' => []
},
'links' => nil
)
end
end
end
# editUrl is tested separately becuase its permissions
# are slightly different than other release fields
shared_examples 'access to editUrl' do
let(:query) do
graphql_query_for(:project, { fullPath: project.full_path },
%{
releases {
nodes {
links {
editUrl
}
}
}
})
end
before do
post_query
end
it 'returns editUrl' do
expect(data).to eq(
'links' => {
'editUrl' => edit_project_release_url(project, release)
}
)
end
end
shared_examples 'no access to editUrl' do
let(:query) do
graphql_query_for(:project, { fullPath: project.full_path },
%{
releases {
nodes {
links {
editUrl
}
}
}
})
end
end
before do
post_query
end
it 'does not return editUrl' do
expect(data).to eq(
'links' => {
'editUrl' => nil
}
)
end
end
shared_examples 'no access to any release data' do
before do
post_query
end
it 'returns nil' do
expect(data).to eq(nil)
end
end
......@@ -112,6 +195,13 @@ describe 'Query.project(fullPath).releases()' do
before_all do
project.add_guest(guest)
project.add_reporter(reporter)
project.add_developer(developer)
end
context 'when the user is not logged in' do
let(:current_user) { stranger }
it_behaves_like 'no access to any release data'
end
context 'when the user has Guest permissions' do
......@@ -124,6 +214,14 @@ describe 'Query.project(fullPath).releases()' do
let(:current_user) { reporter }
it_behaves_like 'full access to all repository-related fields'
it_behaves_like 'no access to editUrl'
end
context 'when the user has Developer permissions' do
let(:current_user) { developer }
it_behaves_like 'full access to all repository-related fields'
it_behaves_like 'access to editUrl'
end
end
......@@ -134,18 +232,35 @@ describe 'Query.project(fullPath).releases()' do
before_all do
project.add_guest(guest)
project.add_reporter(reporter)
project.add_developer(developer)
end
context 'when the user is not logged in' do
let(:current_user) { stranger }
it_behaves_like 'full access to all repository-related fields'
it_behaves_like 'no access to editUrl'
end
context 'when the user has Guest permissions' do
let(:current_user) { guest }
it_behaves_like 'full access to all repository-related fields'
it_behaves_like 'no access to editUrl'
end
context 'when the user has Reporter permissions' do
let(:current_user) { reporter }
it_behaves_like 'full access to all repository-related fields'
it_behaves_like 'no access to editUrl'
end
context 'when the user has Developer permissions' do
let(:current_user) { developer }
it_behaves_like 'full access to all repository-related fields'
it_behaves_like 'access to editUrl'
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