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
diff options
context:
space:
mode:
authorRobert Speicher <rspeicher@gmail.com>2021-01-20 22:34:23 +0300
committerRobert Speicher <rspeicher@gmail.com>2021-01-20 22:34:23 +0300
commit6438df3a1e0fb944485cebf07976160184697d72 (patch)
tree00b09bfd170e77ae9391b1a2f5a93ef6839f2597 /lib/atlassian
parent42bcd54d971da7ef2854b896a7b34f4ef8601067 (diff)
Add latest changes from gitlab-org/gitlab@13-8-stable-eev13.8.0-rc42
Diffstat (limited to 'lib/atlassian')
-rw-r--r--lib/atlassian/jira_connect/client.rb73
-rw-r--r--lib/atlassian/jira_connect/serializers/build_entity.rb6
-rw-r--r--lib/atlassian/jira_connect/serializers/deployment_entity.rb90
-rw-r--r--lib/atlassian/jira_connect/serializers/environment_entity.rb39
-rw-r--r--lib/atlassian/jira_connect/serializers/feature_flag_entity.rb83
-rw-r--r--lib/atlassian/jira_connect/serializers/pipeline_entity.rb31
6 files changed, 318 insertions, 4 deletions
diff --git a/lib/atlassian/jira_connect/client.rb b/lib/atlassian/jira_connect/client.rb
index da24d0e20ee..c67fe24d456 100644
--- a/lib/atlassian/jira_connect/client.rb
+++ b/lib/atlassian/jira_connect/client.rb
@@ -16,11 +16,15 @@ module Atlassian
common = { project: project, update_sequence_id: update_sequence_id }
dev_info = args.slice(:commits, :branches, :merge_requests)
build_info = args.slice(:pipelines)
+ deploy_info = args.slice(:deployments)
+ ff_info = args.slice(:feature_flags)
responses = []
responses << store_dev_info(**common, **dev_info) if dev_info.present?
responses << store_build_info(**common, **build_info) if build_info.present?
+ responses << store_deploy_info(**common, **deploy_info) if deploy_info.present?
+ responses << store_ff_info(**common, **ff_info) if ff_info.present?
raise ArgumentError, 'Invalid arguments' if responses.empty?
responses.compact
@@ -28,11 +32,47 @@ module Atlassian
private
+ def store_ff_info(project:, feature_flags:, **opts)
+ return unless Feature.enabled?(:jira_sync_feature_flags, project)
+
+ items = feature_flags.map { |flag| ::Atlassian::JiraConnect::Serializers::FeatureFlagEntity.represent(flag, opts) }
+ items.reject! { |item| item.issue_keys.empty? }
+
+ return if items.empty?
+
+ r = post('/rest/featureflags/0.1/bulk', {
+ flags: items,
+ properties: { projectId: "project-#{project.id}" }
+ })
+
+ handle_response(r, 'feature flags') do |data|
+ failed = data['failedFeatureFlags']
+ if failed.present?
+ errors = failed.flat_map do |k, errs|
+ errs.map { |e| "#{k}: #{e['message']}" }
+ end
+ { 'errorMessages' => errors }
+ end
+ end
+ end
+
+ def store_deploy_info(project:, deployments:, **opts)
+ return unless Feature.enabled?(:jira_sync_deployments, project)
+
+ items = deployments.map { |d| ::Atlassian::JiraConnect::Serializers::DeploymentEntity.represent(d, opts) }
+ items.reject! { |d| d.issue_keys.empty? }
+
+ return if items.empty?
+
+ r = post('/rest/deployments/0.1/bulk', { deployments: items })
+ handle_response(r, 'deployments') { |data| errors(data, 'rejectedDeployments') }
+ end
+
def store_build_info(project:, pipelines:, update_sequence_id: nil)
return unless Feature.enabled?(:jira_sync_builds, project)
builds = pipelines.map do |pipeline|
- build = Serializers::BuildEntity.represent(
+ build = ::Atlassian::JiraConnect::Serializers::BuildEntity.represent(
pipeline,
update_sequence_id: update_sequence_id
)
@@ -42,7 +82,8 @@ module Atlassian
end.compact
return if builds.empty?
- post('/rest/builds/0.1/bulk', { builds: builds })
+ r = post('/rest/builds/0.1/bulk', { builds: builds })
+ handle_response(r, 'builds') { |data| errors(data, 'rejectedBuilds') }
end
def store_dev_info(project:, commits: nil, branches: nil, merge_requests: nil, update_sequence_id: nil)
@@ -75,6 +116,34 @@ module Atlassian
{ providerMetadata: { product: "GitLab #{Gitlab::VERSION}" } }
end
+ def handle_response(response, name, &block)
+ data = response.parsed_response
+
+ case response.code
+ when 200 then yield data
+ when 400 then { 'errorMessages' => data.map { |e| e['message'] } }
+ when 401 then { 'errorMessages' => ['Invalid JWT'] }
+ when 403 then { 'errorMessages' => ["App does not support #{name}"] }
+ when 413 then { 'errorMessages' => ['Data too large'] + data.map { |e| e['message'] } }
+ when 429 then { 'errorMessages' => ['Rate limit exceeded'] }
+ when 503 then { 'errorMessages' => ['Service unavailable'] }
+ else
+ { 'errorMessages' => ['Unknown error'], 'response' => data }
+ end
+ end
+
+ def errors(data, key)
+ messages = if data[key].present?
+ data[key].flat_map do |rejection|
+ rejection['errors'].map { |e| e['message'] }
+ end
+ else
+ []
+ end
+
+ { 'errorMessages' => messages }
+ end
+
def user_notes_count(merge_requests)
return unless merge_requests
diff --git a/lib/atlassian/jira_connect/serializers/build_entity.rb b/lib/atlassian/jira_connect/serializers/build_entity.rb
index 3eb8b1f1978..8372d2a62da 100644
--- a/lib/atlassian/jira_connect/serializers/build_entity.rb
+++ b/lib/atlassian/jira_connect/serializers/build_entity.rb
@@ -25,8 +25,10 @@ module Atlassian
# extract Jira issue keys from either the source branch/ref or the
# merge request title.
@issue_keys ||= begin
- src = "#{pipeline.source_ref} #{pipeline.merge_request&.title}"
- JiraIssueKeyExtractor.new(src).issue_keys
+ pipeline.all_merge_requests.flat_map do |mr|
+ src = "#{mr.source_branch} #{mr.title}"
+ JiraIssueKeyExtractor.new(src).issue_keys
+ end.uniq
end
end
diff --git a/lib/atlassian/jira_connect/serializers/deployment_entity.rb b/lib/atlassian/jira_connect/serializers/deployment_entity.rb
new file mode 100644
index 00000000000..9ef1666b61c
--- /dev/null
+++ b/lib/atlassian/jira_connect/serializers/deployment_entity.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+module Atlassian
+ module JiraConnect
+ module Serializers
+ class DeploymentEntity < Grape::Entity
+ include Gitlab::Routing
+
+ format_with(:iso8601, &:iso8601)
+
+ expose :schema_version, as: :schemaVersion
+ expose :iid, as: :deploymentSequenceNumber
+ expose :update_sequence_id, as: :updateSequenceNumber
+ expose :display_name, as: :displayName
+ expose :description
+ expose :associations
+ expose :url
+ expose :label
+ expose :state
+ expose :updated_at, as: :lastUpdated, format_with: :iso8601
+ expose :pipeline_entity, as: :pipeline
+ expose :environment_entity, as: :environment
+
+ def issue_keys
+ return [] unless build&.pipeline.present?
+
+ @issue_keys ||= BuildEntity.new(build.pipeline).issue_keys
+ end
+
+ private
+
+ delegate :project, :deployable, :environment, :iid, :ref, :short_sha, to: :object
+ alias_method :deployment, :object
+ alias_method :build, :deployable
+
+ def associations
+ keys = issue_keys
+
+ [{ associationType: :issueKeys, values: keys }] if keys.present?
+ end
+
+ def display_name
+ "Deployment #{iid} (#{ref}@#{short_sha}) to #{environment.name}"
+ end
+
+ def label
+ "#{project.full_path}-#{environment.name}-#{iid}-#{short_sha}"
+ end
+
+ def description
+ "Deployment #{deployment.iid} of #{project.name} at #{short_sha} (#{build&.name}) to #{environment.name}"
+ end
+
+ def url
+ # There is no controller action to show a single deployment, so we
+ # link to the build instead
+ project_job_url(project, build) if build
+ end
+
+ def state
+ case deployment.status
+ when 'created' then 'pending'
+ when 'running' then 'in_progress'
+ when 'success' then 'successful'
+ when 'failed' then 'failed'
+ when 'canceled', 'skipped' then 'cancelled'
+ else
+ 'unknown'
+ end
+ end
+
+ def schema_version
+ '1.0'
+ end
+
+ def pipeline_entity
+ PipelineEntity.new(build.pipeline) if build&.pipeline.present?
+ end
+
+ def environment_entity
+ EnvironmentEntity.new(environment)
+ end
+
+ def update_sequence_id
+ options[:update_sequence_id] || Client.generate_update_sequence_id
+ end
+ end
+ end
+ end
+end
diff --git a/lib/atlassian/jira_connect/serializers/environment_entity.rb b/lib/atlassian/jira_connect/serializers/environment_entity.rb
new file mode 100644
index 00000000000..f3699e4d0ee
--- /dev/null
+++ b/lib/atlassian/jira_connect/serializers/environment_entity.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Atlassian
+ module JiraConnect
+ module Serializers
+ class EnvironmentEntity < Grape::Entity
+ format_with(:string, &:to_s)
+
+ expose :id, format_with: :string
+ expose :display_name, as: :displayName
+ expose :type
+
+ private
+
+ alias_method :environment, :object
+ delegate :project, to: :object
+
+ def display_name
+ "#{project.name}/#{environment.name}"
+ end
+
+ def type
+ case environment.name
+ when /prod/i
+ 'production'
+ when /test/i
+ 'testing'
+ when /staging/i
+ 'staging'
+ when /(dev|review)/i
+ 'development'
+ else
+ 'unmapped'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/atlassian/jira_connect/serializers/feature_flag_entity.rb b/lib/atlassian/jira_connect/serializers/feature_flag_entity.rb
new file mode 100644
index 00000000000..e17c150aacb
--- /dev/null
+++ b/lib/atlassian/jira_connect/serializers/feature_flag_entity.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+module Atlassian
+ module JiraConnect
+ module Serializers
+ class FeatureFlagEntity < Grape::Entity
+ include Gitlab::Routing
+
+ alias_method :flag, :object
+
+ format_with(:string, &:to_s)
+
+ expose :schema_version, as: :schemaVersion
+ expose :id, format_with: :string
+ expose :name, as: :key
+ expose :update_sequence_id, as: :updateSequenceId
+ expose :name, as: :displayName
+ expose :summary
+ expose :details
+ expose :issue_keys, as: :issueKeys
+
+ def issue_keys
+ @issue_keys ||= JiraIssueKeyExtractor.new(flag.description).issue_keys
+ end
+
+ def schema_version
+ '1.0'
+ end
+
+ def update_sequence_id
+ options[:update_sequence_id] || Client.generate_update_sequence_id
+ end
+
+ STRATEGY_NAMES = {
+ ::Operations::FeatureFlags::Strategy::STRATEGY_DEFAULT => 'All users',
+ ::Operations::FeatureFlags::Strategy::STRATEGY_GITLABUSERLIST => 'User List',
+ ::Operations::FeatureFlags::Strategy::STRATEGY_GRADUALROLLOUTUSERID => 'Percent of users',
+ ::Operations::FeatureFlags::Strategy::STRATEGY_FLEXIBLEROLLOUT => 'Percent rollout',
+ ::Operations::FeatureFlags::Strategy::STRATEGY_USERWITHID => 'User IDs'
+ }.freeze
+
+ private
+
+ # The summary does not map very well to our FeatureFlag model.
+ #
+ # We allow feature flags to have multiple strategies, depending
+ # on the environment. Jira expects a single rollout strategy.
+ #
+ # Also, we don't actually support showing a single flag, so we use the
+ # edit path as an interim solution.
+ def summary(strategies = flag.strategies)
+ {
+ url: project_url(flag.project) + "/-/feature_flags/#{flag.id}/edit",
+ lastUpdated: flag.updated_at.iso8601,
+ status: {
+ enabled: flag.active,
+ defaultValue: '',
+ rollout: {
+ percentage: strategies.map do |s|
+ s.parameters['rollout'] || s.parameters['percentage']
+ end.compact.first&.to_f,
+ text: strategies.map { |s| STRATEGY_NAMES[s.name] }.compact.join(', ')
+ }.compact
+ }
+ }
+ end
+
+ def details
+ envs = flag.strategies.flat_map do |s|
+ s.scopes.map do |es|
+ env_type = es.environment_scope.scan(/development|testing|staging|production/).first
+ [es.environment_scope, env_type, s]
+ end
+ end
+
+ envs.map do |env_name, env_type, strat|
+ summary([strat]).merge(environment: { name: env_name, type: env_type }.compact)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/atlassian/jira_connect/serializers/pipeline_entity.rb b/lib/atlassian/jira_connect/serializers/pipeline_entity.rb
new file mode 100644
index 00000000000..e67cf1a7229
--- /dev/null
+++ b/lib/atlassian/jira_connect/serializers/pipeline_entity.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Atlassian
+ module JiraConnect
+ module Serializers
+ # Both this an BuildEntity represent a Ci::Pipeline
+ class PipelineEntity < Grape::Entity
+ include Gitlab::Routing
+
+ format_with(:string, &:to_s)
+
+ expose :id, format_with: :string
+ expose :display_name, as: :displayName
+ expose :url
+
+ private
+
+ alias_method :pipeline, :object
+ delegate :project, to: :object
+
+ def display_name
+ "#{project.name} pipeline #{pipeline.iid}"
+ end
+
+ def url
+ project_pipeline_url(project, pipeline)
+ end
+ end
+ end
+ end
+end