Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/projects/jobs_controller.rb39
-rw-r--r--app/finders/todos_finder.rb10
-rw-r--r--app/graphql/mutations/concerns/mutations/resolves_group.rb15
-rw-r--r--app/graphql/resolvers/todo_resolver.rb90
-rw-r--r--app/graphql/types/query_type.rb5
-rw-r--r--app/graphql/types/todo_action_enum.rb13
-rw-r--r--app/graphql/types/todo_state_enum.rb8
-rw-r--r--app/graphql/types/todo_target_enum.rb9
-rw-r--r--app/graphql/types/todo_type.rb53
-rw-r--r--app/graphql/types/user_type.rb3
-rw-r--r--app/models/ci/build_trace.rb41
-rw-r--r--app/models/evidence.rb27
-rw-r--r--app/models/group.rb6
-rw-r--r--app/models/hooks/web_hook.rb2
-rw-r--r--app/models/release.rb7
-rw-r--r--app/models/todo.rb8
-rw-r--r--app/policies/todo_policy.rb10
-rw-r--r--app/presenters/todo_presenter.rb7
-rw-r--r--app/serializers/build_trace_entity.rb17
-rw-r--r--app/serializers/build_trace_serializer.rb5
-rw-r--r--app/serializers/evidences/author_entity.rb9
-rw-r--r--app/serializers/evidences/evidence_entity.rb7
-rw-r--r--app/serializers/evidences/evidence_serializer.rb7
-rw-r--r--app/serializers/evidences/issue_entity.rb1
-rw-r--r--app/serializers/evidences/milestone_entity.rb2
-rw-r--r--app/serializers/evidences/release_entity.rb4
-rw-r--r--app/workers/all_queues.yml1
-rw-r--r--app/workers/create_evidence_worker.rb12
28 files changed, 370 insertions, 48 deletions
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index 0fdd4d4f33d..050e2d1079b 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -11,7 +11,7 @@ class Projects::JobsController < Projects::ApplicationController
before_action :authorize_erase_build!, only: [:erase]
before_action :authorize_use_build_terminal!, only: [:terminal, :terminal_websocket_authorize]
before_action :verify_api_request!, only: :terminal_websocket_authorize
- before_action only: [:trace] do
+ before_action only: [:show] do
push_frontend_feature_flag(:job_log_json)
end
@@ -67,38 +67,27 @@ class Projects::JobsController < Projects::ApplicationController
# rubocop: enable CodeReuse/ActiveRecord
def trace
- if Feature.enabled?(:job_log_json, @project)
- json_trace
- else
- html_trace
- end
- end
-
- def html_trace
build.trace.read do |stream|
respond_to do |format|
format.json do
- result = {
- id: @build.id, status: @build.status, complete: @build.complete?
- }
-
- if stream.valid?
- stream.limit
- state = params[:state].presence
- trace = stream.html_with_state(state)
- result.merge!(trace.to_h)
- end
-
- render json: result
+ # TODO: when the feature flag is removed we should not pass
+ # content_format to serialize method.
+ content_format = Feature.enabled?(:job_log_json, @project) ? :json : :html
+
+ build_trace = Ci::BuildTrace.new(
+ build: @build,
+ stream: stream,
+ state: params[:state],
+ content_format: content_format)
+
+ render json: BuildTraceSerializer
+ .new(project: @project, current_user: @current_user)
+ .represent(build_trace)
end
end
end
end
- def json_trace
- # will be implemented with https://gitlab.com/gitlab-org/gitlab-foss/issues/66454
- end
-
def retry
return respond_422 unless @build.retryable?
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index 2932e558a37..2b46e51290f 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -33,6 +33,8 @@ class TodosFinder
end
def execute
+ return Todo.none if current_user.nil?
+
items = current_user.todos
items = by_action_id(items)
items = by_action(items)
@@ -180,11 +182,9 @@ class TodosFinder
end
def by_group(items)
- if group?
- items.for_group_and_descendants(group)
- else
- items
- end
+ return items unless group?
+
+ items.for_group_ids_and_descendants(params[:group_id])
end
def by_state(items)
diff --git a/app/graphql/mutations/concerns/mutations/resolves_group.rb b/app/graphql/mutations/concerns/mutations/resolves_group.rb
new file mode 100644
index 00000000000..4306ce512f1
--- /dev/null
+++ b/app/graphql/mutations/concerns/mutations/resolves_group.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Mutations
+ module ResolvesGroup
+ extend ActiveSupport::Concern
+
+ def resolve_group(full_path:)
+ resolver.resolve(full_path: full_path)
+ end
+
+ def resolver
+ Resolvers::GroupResolver.new(object: nil, context: context)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/todo_resolver.rb b/app/graphql/resolvers/todo_resolver.rb
new file mode 100644
index 00000000000..38a4539f34a
--- /dev/null
+++ b/app/graphql/resolvers/todo_resolver.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class TodoResolver < BaseResolver
+ type Types::TodoType, null: true
+
+ alias_method :user, :object
+
+ argument :action, [Types::TodoActionEnum],
+ required: false,
+ description: 'The action to be filtered'
+
+ argument :author_id, [GraphQL::ID_TYPE],
+ required: false,
+ description: 'The ID of an author'
+
+ argument :project_id, [GraphQL::ID_TYPE],
+ required: false,
+ description: 'The ID of a project'
+
+ argument :group_id, [GraphQL::ID_TYPE],
+ required: false,
+ description: 'The ID of a group'
+
+ argument :state, [Types::TodoStateEnum],
+ required: false,
+ description: 'The state of the todo'
+
+ argument :type, [Types::TodoTargetEnum],
+ required: false,
+ description: 'The type of the todo'
+
+ def resolve(**args)
+ return Todo.none if user != context[:current_user]
+
+ TodosFinder.new(user, todo_finder_params(args)).execute
+ end
+
+ private
+
+ # TODO: Support multiple queries for e.g. state and type on TodosFinder:
+ #
+ # https://gitlab.com/gitlab-org/gitlab/merge_requests/18487
+ # https://gitlab.com/gitlab-org/gitlab/merge_requests/18518
+ #
+ # As soon as these MR's are merged, we can refactor this to query by
+ # multiple contents.
+ #
+ def todo_finder_params(args)
+ {
+ state: first_state(args),
+ type: first_type(args),
+ group_id: first_group_id(args),
+ author_id: first_author_id(args),
+ action_id: first_action(args),
+ project_id: first_project(args)
+ }
+ end
+
+ def first_project(args)
+ first_query_field(args, :project_id)
+ end
+
+ def first_action(args)
+ first_query_field(args, :action)
+ end
+
+ def first_author_id(args)
+ first_query_field(args, :author_id)
+ end
+
+ def first_group_id(args)
+ first_query_field(args, :group_id)
+ end
+
+ def first_state(args)
+ first_query_field(args, :state)
+ end
+
+ def first_type(args)
+ first_query_field(args, :type)
+ end
+
+ def first_query_field(query, field)
+ return unless query.key?(field)
+
+ query[field].first if query[field].respond_to?(:first)
+ end
+ end
+end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index bbf94fb92df..996bf225976 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -14,6 +14,11 @@ module Types
resolver: Resolvers::GroupResolver,
description: "Find a group"
+ field :current_user, Types::UserType,
+ null: true,
+ resolve: -> (_obj, _args, context) { context[:current_user] },
+ description: "Get information about current user"
+
field :namespace, Types::NamespaceType,
null: true,
resolver: Resolvers::NamespaceResolver,
diff --git a/app/graphql/types/todo_action_enum.rb b/app/graphql/types/todo_action_enum.rb
new file mode 100644
index 00000000000..0e538838474
--- /dev/null
+++ b/app/graphql/types/todo_action_enum.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ class TodoActionEnum < BaseEnum
+ value 'assigned', value: 1
+ value 'mentioned', value: 2
+ value 'build_failed', value: 3
+ value 'marked', value: 4
+ value 'approval_required', value: 5
+ value 'unmergeable', value: 6
+ value 'directly_addressed', value: 7
+ end
+end
diff --git a/app/graphql/types/todo_state_enum.rb b/app/graphql/types/todo_state_enum.rb
new file mode 100644
index 00000000000..29a28b5208d
--- /dev/null
+++ b/app/graphql/types/todo_state_enum.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+module Types
+ class TodoStateEnum < BaseEnum
+ value 'pending'
+ value 'done'
+ end
+end
diff --git a/app/graphql/types/todo_target_enum.rb b/app/graphql/types/todo_target_enum.rb
new file mode 100644
index 00000000000..9a7391dcd99
--- /dev/null
+++ b/app/graphql/types/todo_target_enum.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Types
+ class TodoTargetEnum < BaseEnum
+ value 'Issue'
+ value 'MergeRequest'
+ value 'Epic'
+ end
+end
diff --git a/app/graphql/types/todo_type.rb b/app/graphql/types/todo_type.rb
new file mode 100644
index 00000000000..d36daaf7dec
--- /dev/null
+++ b/app/graphql/types/todo_type.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module Types
+ class TodoType < BaseObject
+ graphql_name 'Todo'
+ description 'Representing a todo entry'
+
+ present_using TodoPresenter
+
+ authorize :read_todo
+
+ field :id, GraphQL::ID_TYPE,
+ description: 'Id of the todo',
+ null: false
+
+ field :project, Types::ProjectType,
+ description: 'The project this todo is associated with',
+ null: true,
+ authorize: :read_project,
+ resolve: -> (todo, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, todo.project_id).find }
+
+ field :group, Types::GroupType,
+ description: 'Group this todo is associated with',
+ null: true,
+ authorize: :read_group,
+ resolve: -> (todo, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, todo.group_id).find }
+
+ field :author, Types::UserType,
+ description: 'The owner of this todo',
+ null: false,
+ resolve: -> (todo, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, todo.author_id).find }
+
+ field :action, Types::TodoActionEnum,
+ description: 'Action of the todo',
+ null: false
+
+ field :target_type, Types::TodoTargetEnum,
+ description: 'Target type of the todo',
+ null: false
+
+ field :body, GraphQL::STRING_TYPE,
+ description: 'Body of the todo',
+ null: false
+
+ field :state, Types::TodoStateEnum,
+ description: 'State of the todo',
+ null: false
+
+ field :created_at, Types::TimeType,
+ description: 'Timestamp this todo was created',
+ null: false
+ end
+end
diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb
index 9f7d2a171d6..1ba37927b40 100644
--- a/app/graphql/types/user_type.rb
+++ b/app/graphql/types/user_type.rb
@@ -12,5 +12,8 @@ module Types
field :username, GraphQL::STRING_TYPE, null: false # rubocop:disable Graphql/Descriptions
field :avatar_url, GraphQL::STRING_TYPE, null: false # rubocop:disable Graphql/Descriptions
field :web_url, GraphQL::STRING_TYPE, null: false # rubocop:disable Graphql/Descriptions
+ field :todos, Types::TodoType.connection_type, null: false,
+ resolver: Resolvers::TodoResolver,
+ description: 'Todos of this user'
end
end
diff --git a/app/models/ci/build_trace.rb b/app/models/ci/build_trace.rb
new file mode 100644
index 00000000000..b9db1559836
--- /dev/null
+++ b/app/models/ci/build_trace.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Ci
+ class BuildTrace
+ CONVERTERS = {
+ html: Gitlab::Ci::Ansi2html,
+ json: Gitlab::Ci::Ansi2json
+ }.freeze
+
+ attr_reader :trace, :build
+
+ delegate :state, :append, :truncated, :offset, :size, :total, to: :trace, allow_nil: true
+ delegate :id, :status, :complete?, to: :build, prefix: true
+
+ def initialize(build:, stream:, state:, content_format:)
+ @build = build
+ @content_format = content_format
+
+ if stream.valid?
+ stream.limit
+ @trace = CONVERTERS.fetch(content_format).convert(stream.stream, state)
+ end
+ end
+
+ def json?
+ @content_format == :json
+ end
+
+ def html?
+ @content_format == :html
+ end
+
+ def json_lines
+ @trace&.lines if json?
+ end
+
+ def html_lines
+ @trace&.html if html?
+ end
+ end
+end
diff --git a/app/models/evidence.rb b/app/models/evidence.rb
new file mode 100644
index 00000000000..69a00f1cb3f
--- /dev/null
+++ b/app/models/evidence.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class Evidence < ApplicationRecord
+ include ShaAttribute
+
+ belongs_to :release
+
+ before_validation :generate_summary_and_sha
+
+ default_scope { order(created_at: :asc) }
+
+ sha_attribute :summary_sha
+
+ def milestones
+ @milestones ||= release.milestones.includes(:issues)
+ end
+
+ private
+
+ def generate_summary_and_sha
+ summary = Evidences::EvidenceSerializer.new.represent(self) # rubocop: disable CodeReuse/Serializer
+ return unless summary
+
+ self.summary = summary
+ self.summary_sha = Gitlab::CryptoHelper.sha256(summary)
+ end
+end
diff --git a/app/models/group.rb b/app/models/group.rb
index 8b21206fccf..042201ffa14 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -473,6 +473,12 @@ class Group < Namespace
errors.add(:visibility_level, "#{visibility} is not allowed since there are sub-groups with higher visibility.")
end
+
+ def self.groups_including_descendants_by(group_ids)
+ Gitlab::ObjectHierarchy
+ .new(Group.where(id: group_ids))
+ .base_and_descendants
+ end
end
Group.prepend_if_ee('EE::Group')
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 16fc7fdbd48..e51b1c41059 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -13,7 +13,7 @@ class WebHook < ApplicationRecord
algorithm: 'aes-256-gcm',
key: Settings.attr_encrypted_db_key_base_32
- has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ has_many :web_hook_logs
validates :url, presence: true
validates :url, public_url: true, unless: ->(hook) { hook.is_a?(SystemHook) }
diff --git a/app/models/release.rb b/app/models/release.rb
index 8759e38060c..add57367f61 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -14,6 +14,7 @@ class Release < ApplicationRecord
has_many :milestone_releases
has_many :milestones, through: :milestone_releases
+ has_one :evidence
default_value_for :released_at, allows_nil: false do
Time.zone.now
@@ -28,6 +29,8 @@ class Release < ApplicationRecord
delegate :repository, to: :project
+ after_commit :create_evidence!, on: :create
+
def commit
strong_memoize(:commit) do
repository.commit(actual_sha)
@@ -66,6 +69,10 @@ class Release < ApplicationRecord
repository.find_tag(tag)
end
end
+
+ def create_evidence!
+ CreateEvidenceWorker.perform_async(self.id)
+ end
end
Release.prepend_if_ee('EE::Release')
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 456115872d1..1927b54510e 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -75,13 +75,13 @@ class Todo < ApplicationRecord
after_save :keep_around_commit, if: :commit_id
class << self
- # Returns all todos for the given group and its descendants.
+ # Returns all todos for the given group ids and their descendants.
#
- # group - A `Group` to retrieve todos for.
+ # group_ids - Group Ids to retrieve todos for.
#
# Returns an `ActiveRecord::Relation`.
- def for_group_and_descendants(group)
- groups = group.self_and_descendants
+ def for_group_ids_and_descendants(group_ids)
+ groups = Group.groups_including_descendants_by(group_ids)
from_union([
for_project(Project.for_group(groups)),
diff --git a/app/policies/todo_policy.rb b/app/policies/todo_policy.rb
new file mode 100644
index 00000000000..f8644217f04
--- /dev/null
+++ b/app/policies/todo_policy.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class TodoPolicy < BasePolicy
+ desc 'User can only read own todos'
+ condition(:own_todo) do
+ @user && @subject.user_id == @user.id
+ end
+
+ rule { own_todo }.enable :read_todo
+end
diff --git a/app/presenters/todo_presenter.rb b/app/presenters/todo_presenter.rb
new file mode 100644
index 00000000000..b57fc712c5a
--- /dev/null
+++ b/app/presenters/todo_presenter.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class TodoPresenter < Gitlab::View::Presenter::Delegated
+ include GlobalID::Identification
+
+ presents :todo
+end
diff --git a/app/serializers/build_trace_entity.rb b/app/serializers/build_trace_entity.rb
new file mode 100644
index 00000000000..b5bac8a5d64
--- /dev/null
+++ b/app/serializers/build_trace_entity.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class BuildTraceEntity < Grape::Entity
+ expose :build_id, as: :id
+ expose :build_status, as: :status
+ expose :build_complete?, as: :complete
+
+ expose :state
+ expose :append
+ expose :truncated
+ expose :offset
+ expose :size
+ expose :total
+
+ expose :json_lines, as: :lines, if: ->(*) { object.json? }
+ expose :html_lines, as: :html, if: ->(*) { object.html? }
+end
diff --git a/app/serializers/build_trace_serializer.rb b/app/serializers/build_trace_serializer.rb
new file mode 100644
index 00000000000..c95158f10a4
--- /dev/null
+++ b/app/serializers/build_trace_serializer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class BuildTraceSerializer < BaseSerializer
+ entity BuildTraceEntity
+end
diff --git a/app/serializers/evidences/author_entity.rb b/app/serializers/evidences/author_entity.rb
deleted file mode 100644
index 9023c64dad2..00000000000
--- a/app/serializers/evidences/author_entity.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module Evidences
- class AuthorEntity < Grape::Entity
- expose :id
- expose :name
- expose :email
- end
-end
diff --git a/app/serializers/evidences/evidence_entity.rb b/app/serializers/evidences/evidence_entity.rb
new file mode 100644
index 00000000000..9689ae10895
--- /dev/null
+++ b/app/serializers/evidences/evidence_entity.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module Evidences
+ class EvidenceEntity < Grape::Entity
+ expose :release, using: Evidences::ReleaseEntity
+ end
+end
diff --git a/app/serializers/evidences/evidence_serializer.rb b/app/serializers/evidences/evidence_serializer.rb
new file mode 100644
index 00000000000..d03032bc65c
--- /dev/null
+++ b/app/serializers/evidences/evidence_serializer.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module Evidences
+ class EvidenceSerializer < BaseSerializer
+ entity EvidenceEntity
+ end
+end
diff --git a/app/serializers/evidences/issue_entity.rb b/app/serializers/evidences/issue_entity.rb
index 883256bf38a..2f1f5dc3d18 100644
--- a/app/serializers/evidences/issue_entity.rb
+++ b/app/serializers/evidences/issue_entity.rb
@@ -5,7 +5,6 @@ module Evidences
expose :id
expose :title
expose :description
- expose :author, using: AuthorEntity
expose :state
expose :iid
expose :confidential
diff --git a/app/serializers/evidences/milestone_entity.rb b/app/serializers/evidences/milestone_entity.rb
index 8118cab4403..eeb3d58d4c7 100644
--- a/app/serializers/evidences/milestone_entity.rb
+++ b/app/serializers/evidences/milestone_entity.rb
@@ -9,6 +9,6 @@ module Evidences
expose :iid
expose :created_at
expose :due_date
- expose :issues, using: IssueEntity
+ expose :issues, using: Evidences::IssueEntity
end
end
diff --git a/app/serializers/evidences/release_entity.rb b/app/serializers/evidences/release_entity.rb
index 8916ce67b4c..59e379a3c08 100644
--- a/app/serializers/evidences/release_entity.rb
+++ b/app/serializers/evidences/release_entity.rb
@@ -7,7 +7,7 @@ module Evidences
expose :name
expose :description
expose :created_at
- expose :project, using: ProjectEntity
- expose :milestones, using: MilestoneEntity
+ expose :project, using: Evidences::ProjectEntity
+ expose :milestones, using: Evidences::MilestoneEntity
end
end
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index a33afd436b0..cd8d1d05d8b 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -173,3 +173,4 @@
- delete_stored_files
- import_issues_csv
- project_daily_statistics
+- create_evidence
diff --git a/app/workers/create_evidence_worker.rb b/app/workers/create_evidence_worker.rb
new file mode 100644
index 00000000000..5fc901ae514
--- /dev/null
+++ b/app/workers/create_evidence_worker.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class CreateEvidenceWorker
+ include ApplicationWorker
+
+ def perform(release_id)
+ release = Release.find_by_id(release_id)
+ return unless release
+
+ Evidence.create!(release: release)
+ end
+end