Commit a524777a authored by Nathan Friend's avatar Nathan Friend Committed by Peter Leitzen

Add related links to Release GraphQL endpoint

This commit adds related links to the Release portion of our GraphQL
endpoint.
parent 678c9c1b
# 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
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