Commit 734c5e6a authored by Jan Provaznik's avatar Jan Provaznik

Added Requirement model

This is a basic model to represent Requirement resource.
parent 9341017a
---
title: Add migration for Requirement model
merge_request: 26097
author:
type: added
# frozen_string_literal: true
class CreateRequirements < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table :requirements do |t|
t.integer :state, limit: 2, default: 1, null: false
t.integer :iid, null: false
t.references :project, index: true, foreign_key: { on_delete: :cascade }
t.references :group, index: true, foreign_key: { to_table: :namespaces, on_delete: :cascade }
t.references :author, index: true, foreign_key: { to_table: :users, on_delete: :nullify }
t.timestamps_with_timezone null: false
t.string :title, limit: 255, null: false
t.text :title_html
t.index :title
t.index :state
t.index :created_at
t.index :updated_at
t.index %w(project_id iid), name: 'index_requirements_on_project_id_and_iid', where: 'project_id IS NOT NULL', unique: true, using: :btree
t.index %w(group_id iid), name: 'index_requirements_on_group_id_and_iid', where: 'group_id IS NOT NULL', unique: true, using: :btree
end
end
end
...@@ -3722,6 +3722,27 @@ ActiveRecord::Schema.define(version: 2020_02_27_165129) do ...@@ -3722,6 +3722,27 @@ ActiveRecord::Schema.define(version: 2020_02_27_165129) do
t.index ["project_id", "programming_language_id"], name: "index_repository_languages_on_project_and_languages_id", unique: true t.index ["project_id", "programming_language_id"], name: "index_repository_languages_on_project_and_languages_id", unique: true
end end
create_table "requirements", force: :cascade do |t|
t.integer "state", limit: 2, default: 1, null: false
t.integer "iid", null: false
t.bigint "project_id"
t.bigint "group_id"
t.bigint "author_id"
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.string "title", limit: 255, null: false
t.text "title_html"
t.index ["author_id"], name: "index_requirements_on_author_id"
t.index ["created_at"], name: "index_requirements_on_created_at"
t.index ["group_id", "iid"], name: "index_requirements_on_group_id_and_iid", unique: true, where: "(group_id IS NOT NULL)"
t.index ["group_id"], name: "index_requirements_on_group_id"
t.index ["project_id", "iid"], name: "index_requirements_on_project_id_and_iid", unique: true, where: "(project_id IS NOT NULL)"
t.index ["project_id"], name: "index_requirements_on_project_id"
t.index ["state"], name: "index_requirements_on_state"
t.index ["title"], name: "index_requirements_on_title"
t.index ["updated_at"], name: "index_requirements_on_updated_at"
end
create_table "resource_label_events", force: :cascade do |t| create_table "resource_label_events", force: :cascade do |t|
t.integer "action", null: false t.integer "action", null: false
t.integer "issue_id" t.integer "issue_id"
...@@ -4996,6 +5017,9 @@ ActiveRecord::Schema.define(version: 2020_02_27_165129) do ...@@ -4996,6 +5017,9 @@ ActiveRecord::Schema.define(version: 2020_02_27_165129) do
add_foreign_key "releases", "users", column: "author_id", name: "fk_8e4456f90f", on_delete: :nullify add_foreign_key "releases", "users", column: "author_id", name: "fk_8e4456f90f", on_delete: :nullify
add_foreign_key "remote_mirrors", "projects", name: "fk_43a9aa4ca8", on_delete: :cascade add_foreign_key "remote_mirrors", "projects", name: "fk_43a9aa4ca8", on_delete: :cascade
add_foreign_key "repository_languages", "projects", on_delete: :cascade add_foreign_key "repository_languages", "projects", on_delete: :cascade
add_foreign_key "requirements", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "requirements", "projects", on_delete: :cascade
add_foreign_key "requirements", "users", column: "author_id", on_delete: :nullify
add_foreign_key "resource_label_events", "epics", on_delete: :cascade add_foreign_key "resource_label_events", "epics", on_delete: :cascade
add_foreign_key "resource_label_events", "issues", on_delete: :cascade add_foreign_key "resource_label_events", "issues", on_delete: :cascade
add_foreign_key "resource_label_events", "labels", on_delete: :nullify add_foreign_key "resource_label_events", "labels", on_delete: :nullify
......
...@@ -17,6 +17,7 @@ module EE ...@@ -17,6 +17,7 @@ module EE
add_authentication_token_field :saml_discovery_token, unique: false, token_generator: -> { Devise.friendly_token(8) } add_authentication_token_field :saml_discovery_token, unique: false, token_generator: -> { Devise.friendly_token(8) }
has_many :epics has_many :epics
has_many :requirements
has_one :saml_provider has_one :saml_provider
has_many :ip_restrictions, autosave: true has_many :ip_restrictions, autosave: true
......
...@@ -59,6 +59,7 @@ module EE ...@@ -59,6 +59,7 @@ module EE
has_many :audit_events, as: :entity has_many :audit_events, as: :entity
has_many :designs, inverse_of: :project, class_name: 'DesignManagement::Design' has_many :designs, inverse_of: :project, class_name: 'DesignManagement::Design'
has_many :path_locks has_many :path_locks
has_many :requirements
# the rationale behind vulnerabilities and vulnerability_findings can be found here: # the rationale behind vulnerabilities and vulnerability_findings can be found here:
# https://gitlab.com/gitlab-org/gitlab/issues/10252#terminology # https://gitlab.com/gitlab-org/gitlab/issues/10252#terminology
......
...@@ -32,6 +32,7 @@ module EE ...@@ -32,6 +32,7 @@ module EE
has_many :reviews, foreign_key: :author_id, inverse_of: :author has_many :reviews, foreign_key: :author_id, inverse_of: :author
has_many :epics, foreign_key: :author_id has_many :epics, foreign_key: :author_id
has_many :requirements, foreign_key: :author_id
has_many :assigned_epics, foreign_key: :assignee_id, class_name: "Epic" has_many :assigned_epics, foreign_key: :assignee_id, class_name: "Epic"
has_many :path_locks, dependent: :destroy # rubocop: disable Cop/ActiveRecordDependent has_many :path_locks, dependent: :destroy # rubocop: disable Cop/ActiveRecordDependent
has_many :vulnerability_feedback, foreign_key: :author_id, class_name: 'Vulnerabilities::Feedback' has_many :vulnerability_feedback, foreign_key: :author_id, class_name: 'Vulnerabilities::Feedback'
......
# frozen_string_literal: true
class Requirement < ApplicationRecord
include CacheMarkdownField
cache_markdown_field :title, pipeline: :single_line
belongs_to :author, class_name: 'User'
belongs_to :project
belongs_to :group
validates :author, :project, :title, presence: true
# For MVC, we allow only project-scoped requirements,
# but we already know that in the next iteration we will
# need also group-scoped requirements
validates :group, absence: true
validates :title, length: { maximum: Issuable::TITLE_LENGTH_MAX }
validates :title_html, length: { maximum: Issuable::TITLE_HTML_LENGTH_MAX }, allow_blank: true
enum state: { opened: 1, archived: 2 }
# In the next iteration we will support also group-level requirements
# so it use resource_parent instead of project directly
def resource_parent
project
end
end
...@@ -7,6 +7,7 @@ module EE ...@@ -7,6 +7,7 @@ module EE
def migrate_records def migrate_records
migrate_epics migrate_epics
migrate_requirements
migrate_vulnerabilities_feedback migrate_vulnerabilities_feedback
migrate_reviews migrate_reviews
super super
...@@ -19,6 +20,10 @@ module EE ...@@ -19,6 +20,10 @@ module EE
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def migrate_requirements
user.requirements.update_all(author_id: ghost_user.id)
end
def migrate_vulnerabilities_feedback def migrate_vulnerabilities_feedback
user.vulnerability_feedback.update_all(author_id: ghost_user.id) user.vulnerability_feedback.update_all(author_id: ghost_user.id)
user.commented_vulnerability_feedback.update_all(comment_author_id: ghost_user.id) user.commented_vulnerability_feedback.update_all(comment_author_id: ghost_user.id)
......
# frozen_string_literal: true
FactoryBot.define do
factory :requirement do
project
author
title { generate(:title) }
title_html { "<h2>#{title}</h2>" }
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Requirement do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
describe 'associations' do
subject { build(:requirement) }
it { is_expected.to belong_to(:author).class_name('User') }
it { is_expected.to belong_to(:group) }
it { is_expected.to belong_to(:project) }
end
describe 'validations' do
subject { build(:requirement) }
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:author) }
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_absence_of(:group) }
it { is_expected.to validate_length_of(:title).is_at_most(::Issuable::TITLE_LENGTH_MAX) }
it { is_expected.to validate_length_of(:title_html).is_at_most(::Issuable::TITLE_HTML_LENGTH_MAX) }
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