Commit f2a223c8 authored by Jan Provaznik's avatar Jan Provaznik Committed by Robert Speicher

Added description column to requirements

* adds both description and description_html columns
parent db258ca1
# frozen_string_literal: true
class AddDescriptionToRequirements < ActiveRecord::Migration[6.0]
DOWNTIME = false
# rubocop:disable Migration/AddLimitToTextColumns
# limit for description is added in 20200923071644_add_text_limit_to_requirements_description
# for description_html limit is not set because it's for caching purposes and
# its value is generated from `description`
def change
add_column :requirements, :description, :text
add_column :requirements, :description_html, :text
end
# rubocop:enable Migration/AddLimitToTextColumns
end
# frozen_string_literal: true
class AddTextLimitToRequirementsDescription < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_text_limit :requirements, :description, 10_000
end
def down
remove_text_limit :requirements, :description
end
end
1751fa6522a88582cb6a580acc95665f4e3f3a879f2365d5fd0a824ad1b4806d
\ No newline at end of file
0df2b1e65ef0dc563c55e575968e4fd768cec2e713e3b1c999cf584ef62b629d
\ No newline at end of file
......@@ -15365,7 +15365,10 @@ CREATE TABLE requirements (
cached_markdown_version integer,
state smallint DEFAULT 1 NOT NULL,
title character varying(255) NOT NULL,
title_html text
title_html text,
description text,
description_html text,
CONSTRAINT check_785ae25b9d CHECK ((char_length(description) <= 10000))
);
CREATE SEQUENCE requirements_id_seq
......
......@@ -3406,14 +3406,19 @@ input CreateRequirementInput {
clientMutationId: String
"""
The project full path the requirement is associated with
Description of the requirement
"""
description: String
"""
Full project path the requirement is associated with
"""
projectPath: ID!
"""
Title of the requirement
"""
title: String!
title: String
}
"""
......@@ -3431,7 +3436,7 @@ type CreateRequirementPayload {
errors: [String!]!
"""
The requirement after mutation
Requirement after mutation
"""
requirement: Requirement
}
......@@ -15681,6 +15686,16 @@ type Requirement {
"""
createdAt: Time!
"""
Description of the requirement
"""
description: String
"""
The GitLab Flavored Markdown rendering of `description`
"""
descriptionHtml: String
"""
ID of the requirement
"""
......@@ -15741,6 +15756,11 @@ type Requirement {
"""
title: String
"""
The GitLab Flavored Markdown rendering of `title`
"""
titleHtml: String
"""
Timestamp of when the requirement was last updated
"""
......@@ -19004,6 +19024,11 @@ input UpdateRequirementInput {
"""
clientMutationId: String
"""
Description of the requirement
"""
description: String
"""
The iid of the requirement to update
"""
......@@ -19015,7 +19040,7 @@ input UpdateRequirementInput {
lastTestReportState: TestReportState
"""
The project full path the requirement is associated with
Full project path the requirement is associated with
"""
projectPath: ID!
......@@ -19045,7 +19070,7 @@ type UpdateRequirementPayload {
errors: [String!]!
"""
The requirement after mutation
Requirement after mutation
"""
requirement: Requirement
}
......
......@@ -9153,19 +9153,25 @@
"name": "title",
"description": "Title of the requirement",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "description",
"description": "Description of the requirement",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "projectPath",
"description": "The project full path the requirement is associated with",
"description": "Full project path the requirement is associated with",
"type": {
"kind": "NON_NULL",
"name": null,
......@@ -9239,7 +9245,7 @@
},
{
"name": "requirement",
"description": "The requirement after mutation",
"description": "Requirement after mutation",
"args": [
],
......@@ -45414,6 +45420,34 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "description",
"description": "Description of the requirement",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "descriptionHtml",
"description": "The GitLab Flavored Markdown rendering of `description`",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": "ID of the requirement",
......@@ -45577,6 +45611,20 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "titleHtml",
"description": "The GitLab Flavored Markdown rendering of `title`",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updatedAt",
"description": "Timestamp of when the requirement was last updated",
......@@ -55352,38 +55400,48 @@
"defaultValue": null
},
{
"name": "state",
"description": "State of the requirement",
"name": "description",
"description": "Description of the requirement",
"type": {
"kind": "ENUM",
"name": "RequirementState",
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "iid",
"description": "The iid of the requirement to update",
"name": "projectPath",
"description": "Full project path the requirement is associated with",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "projectPath",
"description": "The project full path the requirement is associated with",
"name": "state",
"description": "State of the requirement",
"type": {
"kind": "ENUM",
"name": "RequirementState",
"ofType": null
},
"defaultValue": null
},
{
"name": "iid",
"description": "The iid of the requirement to update",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"name": "String",
"ofType": null
}
},
......@@ -55461,7 +55519,7 @@
},
{
"name": "requirement",
"description": "The requirement after mutation",
"description": "Requirement after mutation",
"args": [
],
......@@ -550,7 +550,7 @@ Autogenerated return type of CreateRequirement.
| ----- | ---- | ----------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `requirement` | Requirement | The requirement after mutation |
| `requirement` | Requirement | Requirement after mutation |
### CreateSnippetPayload
......@@ -2126,12 +2126,15 @@ Represents a requirement.
| ----- | ---- | ----------- |
| `author` | User! | Author of the requirement |
| `createdAt` | Time! | Timestamp of when the requirement was created |
| `description` | String | Description of the requirement |
| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` |
| `id` | ID! | ID of the requirement |
| `iid` | ID! | Internal ID of the requirement |
| `lastTestReportState` | TestReportState | Latest requirement test report state |
| `project` | Project! | Project to which the requirement belongs |
| `state` | RequirementState! | State of the requirement |
| `title` | String | Title of the requirement |
| `titleHtml` | String | The GitLab Flavored Markdown rendering of `title` |
| `updatedAt` | Time! | Timestamp of when the requirement was last updated |
| `userPermissions` | RequirementPermissions! | Permissions for the current user on the resource |
......@@ -2712,7 +2715,7 @@ Autogenerated return type of UpdateRequirement.
| ----- | ---- | ----------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `requirement` | Requirement | The requirement after mutation |
| `requirement` | Requirement | Requirement after mutation |
### UpdateSnippetPayload
......
# frozen_string_literal: true
module Mutations
module RequirementsManagement
class BaseRequirement < BaseMutation
include ResolvesProject
field :requirement, Types::RequirementsManagement::RequirementType,
null: true,
description: 'Requirement after mutation'
argument :title, GraphQL::STRING_TYPE,
required: false,
description: 'Title of the requirement'
argument :description, GraphQL::STRING_TYPE,
required: false,
description: 'Description of the requirement'
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: 'Full project path the requirement is associated with'
end
end
end
......@@ -2,25 +2,11 @@
module Mutations
module RequirementsManagement
class CreateRequirement < BaseMutation
include ResolvesProject
class CreateRequirement < BaseRequirement
graphql_name 'CreateRequirement'
authorize :create_requirement
field :requirement, Types::RequirementsManagement::RequirementType,
null: true,
description: 'The requirement after mutation'
argument :title, GraphQL::STRING_TYPE,
required: true,
description: 'Title of the requirement'
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: 'The project full path the requirement is associated with'
def resolve(args)
project_path = args.delete(:project_path)
project = authorized_find!(full_path: project_path)
......
......@@ -2,21 +2,11 @@
module Mutations
module RequirementsManagement
class UpdateRequirement < BaseMutation
include ResolvesProject
class UpdateRequirement < BaseRequirement
graphql_name 'UpdateRequirement'
authorize :update_requirement
field :requirement, Types::RequirementsManagement::RequirementType,
null: true,
description: 'The requirement after mutation'
argument :title, GraphQL::STRING_TYPE,
required: false,
description: 'Title of the requirement'
argument :state, Types::RequirementsManagement::RequirementStateEnum,
required: false,
description: 'State of the requirement'
......@@ -25,18 +15,15 @@ module Mutations
required: true,
description: 'The iid of the requirement to update'
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: 'The project full path the requirement is associated with'
argument :last_test_report_state, Types::RequirementsManagement::TestReportStateEnum,
required: false,
description: 'Creates a test report for the requirement with the given state'
def ready?(**args)
if args.values_at(:title, :state, :last_test_report_state).compact.blank?
update_args = [:title, :state, :last_test_report_state, :description]
if args.values_at(*update_args).compact.blank?
raise Gitlab::Graphql::Errors::ArgumentError,
'title, state or last_test_report_state argument is required'
"At least one of #{update_args.join(', ')} is required"
end
super
......
......@@ -12,17 +12,28 @@ module Types
field :id, GraphQL::ID_TYPE, null: false,
description: 'ID of the requirement'
field :iid, GraphQL::ID_TYPE, null: false,
description: 'Internal ID of the requirement'
field :title, GraphQL::STRING_TYPE, null: true,
description: 'Title of the requirement'
markdown_field :title_html, null: true
field :description, GraphQL::STRING_TYPE, null: true,
description: 'Description of the requirement'
markdown_field :description_html, null: true
field :state, RequirementsManagement::RequirementStateEnum, null: false,
description: 'State of the requirement'
field :last_test_report_state, RequirementsManagement::TestReportStateEnum, null: true, complexity: 5,
description: 'Latest requirement test report state'
field :project, ProjectType, null: false,
description: 'Project to which the requirement belongs',
resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, obj.project_id).find }
field :author, UserType, null: false,
description: 'Author of the requirement',
resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, obj.author_id).find }
......@@ -33,6 +44,7 @@ module Types
field :created_at, Types::TimeType, null: false,
description: 'Timestamp of when the requirement was created'
field :updated_at, Types::TimeType, null: false,
description: 'Timestamp of when the requirement was last updated'
end
......
......@@ -14,6 +14,7 @@ module RequirementsManagement
self.table_name = 'requirements'
cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description, issuable_state_filter_enabled: true
strip_attributes :title
......
......@@ -14,7 +14,7 @@ module RequirementsManagement
private
def whitelisted_requirement_params
params.slice(:title)
params.slice(:title, :description)
end
end
end
......@@ -26,7 +26,7 @@ module RequirementsManagement
end
def whitelisted_requirement_params
params.slice(:title, :state)
params.slice(:title, :description, :state)
end
end
end
---
title: Add description field to requirements model and expose it in GraphQL API
merge_request: 43099
author:
type: added
......@@ -18,7 +18,8 @@ RSpec.describe Mutations::RequirementsManagement::CreateRequirement do
subject do
mutation.resolve(
project_path: project.full_path,
title: 'foo'
title: 'foo',
description: 'some desc'
)
end
......@@ -36,6 +37,7 @@ RSpec.describe Mutations::RequirementsManagement::CreateRequirement do
it 'creates new requirement' do
expect(subject[:requirement][:title]).to eq('foo')
expect(subject[:requirement][:description]).to eq('some desc')
expect(subject[:errors]).to be_empty
end
end
......
......@@ -21,6 +21,7 @@ RSpec.describe Mutations::RequirementsManagement::UpdateRequirement do
project_path: project.full_path,
iid: requirement.iid.to_s,
title: 'foo',
description: 'some desc',
state: 'archived',
last_test_report_state: 'passed'
)
......@@ -41,6 +42,7 @@ RSpec.describe Mutations::RequirementsManagement::UpdateRequirement do
it 'updates new requirement', :aggregate_failures do
expect(subject[:requirement]).to have_attributes(
title: 'foo',
description: 'some desc',
state: 'archived',
last_test_report_state: 'passed'
)
......
......@@ -3,7 +3,9 @@
require 'spec_helper'
RSpec.describe GitlabSchema.types['Requirement'] do
fields = %i[id iid title state last_test_report_state project author created_at updated_at user_permissions test_reports]
fields = %i[id iid title titleHtml description descriptionHtml state
last_test_report_state project author created_at updated_at
user_permissions test_reports]
it { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Requirement) }
......
......@@ -81,7 +81,7 @@ RSpec.describe 'Updating a Requirement' do
let(:attributes) { {} }
it_behaves_like 'a mutation that returns top-level errors',
errors: ['title, state or last_test_report_state argument is required']
errors: ['At least one of title, state, last_test_report_state, description is required']
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