Commit f94e4488 authored by Alex Kalderimis's avatar Alex Kalderimis

Merge branch '333508-ajk-use-type-new' into 'master'

Integrations model: use `type_new` column, mark `type` for removal

See merge request gitlab-org/gitlab!80065
parents 9d161a73 3e6da3aa
......@@ -10,9 +10,20 @@ module Types
# https://gitlab.com/gitlab-org/gitlab/-/issues/213088
field :type, GraphQL::Types::String, null: true,
description: 'Class name of the service.'
field :service_type, ::Types::Projects::ServiceTypeEnum, null: true,
description: 'Type of the service.'
field :active, GraphQL::Types::Boolean, null: true,
description: 'Indicates if the service is active.'
def type
enum = ::Types::Projects::ServiceTypeEnum.coerce_result(service_type, context)
enum.downcase.camelize
end
def service_type
object.type
end
definition_methods do
def resolve_type(object, context)
if object.is_a?(::Integrations::Jira)
......
......@@ -8,7 +8,7 @@ module Types
class << self
private
def type_description(type)
def type_description(name, type)
"#{type} type"
end
end
......@@ -16,8 +16,10 @@ module Types
# This prepend must stay here because the dynamic block below depends on it.
prepend_mod # rubocop: disable Cop/InjectEnterpriseEditionModule
::Integration.available_integration_types(include_dev: false).each do |type|
value type.underscore.upcase, value: type, description: type_description(type)
::Integration.available_integration_names(include_dev: false).each do |name|
type = "#{name.camelize}Service"
domain_value = Integration.integration_name_to_type(name)
value type.underscore.upcase, value: domain_value, description: type_description(name, type)
end
end
end
......
......@@ -12,6 +12,11 @@ class Integration < ApplicationRecord
include IgnorableColumns
ignore_column :template, remove_with: '15.0', remove_after: '2022-04-22'
ignore_column :type, remove_with: '15.0', remove_after: '2022-04-22'
UnknownType = Class.new(StandardError)
self.inheritance_column = :type_new
INTEGRATION_NAMES = %w[
asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker datadog discord
......@@ -44,7 +49,7 @@ class Integration < ApplicationRecord
serialize :properties, JSON # rubocop:disable Cop/ActiveRecordSerialize
attribute :type, Gitlab::Integrations::StiType.new
alias_attribute :type, :type_new
default_value_for :active, false
default_value_for :alert_events, true
......@@ -79,9 +84,10 @@ class Integration < ApplicationRecord
validate :validate_belongs_to_project_or_group
scope :external_issue_trackers, -> { where(category: 'issue_tracker').active }
scope :external_wikis, -> { where(type: 'ExternalWikiService').active }
scope :by_name, ->(name) { by_type(integration_name_to_type(name)) }
scope :external_wikis, -> { by_name(:external_wiki).active }
scope :active, -> { where(active: true) }
scope :by_type, -> (type) { where(type: type) }
scope :by_type, ->(type) { where(type: type) } # INTERNAL USE ONLY: use by_name instead
scope :by_active_flag, -> (flag) { where(active: flag) }
scope :inherit_from_id, -> (id) { where(inherit_from_id: id) }
scope :with_default_settings, -> { where.not(inherit_from_id: nil) }
......@@ -231,7 +237,7 @@ class Integration < ApplicationRecord
end
# Returns a list of available integration types.
# Example: ["AsanaService", ...]
# Example: ["Integrations::Asana", ...]
def self.available_integration_types(include_project_specific: true, include_dev: true)
available_integration_names(include_project_specific: include_project_specific, include_dev: include_dev).map do
integration_name_to_type(_1)
......@@ -239,22 +245,27 @@ class Integration < ApplicationRecord
end
# Returns the model for the given integration name.
# Example: "asana" => Integrations::Asana
# Example: :asana => Integrations::Asana
def self.integration_name_to_model(name)
type = integration_name_to_type(name)
integration_type_to_model(type)
end
# Returns the STI type for the given integration name.
# Example: "asana" => "AsanaService"
# Example: "asana" => "Integrations::Asana"
def self.integration_name_to_type(name)
"#{name}_service".camelize
name = name.to_s
if available_integration_names.exclude?(name)
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(UnknownType.new(name.inspect))
else
"Integrations::#{name.camelize}"
end
end
# Returns the model for the given STI type.
# Example: "AsanaService" => Integrations::Asana
# Example: "Integrations::Asana" => Integrations::Asana
def self.integration_type_to_model(type)
Gitlab::Integrations::StiType.new.cast(type).constantize
type.constantize
end
private_class_method :integration_type_to_model
......@@ -303,7 +314,7 @@ class Integration < ApplicationRecord
from_union([
active.where(instance: true),
active.where(group_id: group_ids, inherit_from_id: nil)
]).order(Arel.sql("type ASC, array_position(#{array}::bigint[], #{table_name}.group_id), instance DESC")).group_by(&:type).each do |type, records|
]).order(Arel.sql("type_new ASC, array_position(#{array}::bigint[], #{table_name}.group_id), instance DESC")).group_by(&:type).each do |type, records|
build_from_integration(records.first, association => scope.id).save
end
end
......@@ -380,8 +391,10 @@ class Integration < ApplicationRecord
%w[active]
end
# return a hash of columns => values suitable for passing to insert_all
def to_integration_hash
as_json(methods: :type, except: %w[id instance project_id group_id])
column = self.class.attribute_aliases.fetch('type', 'type')
as_json(except: %w[id instance project_id group_id]).merge(column => type)
end
def to_data_fields_hash
......
......@@ -8726,6 +8726,7 @@ An emoji awarded by a user.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="baseserviceactive"></a>`active` | [`Boolean`](#boolean) | Indicates if the service is active. |
| <a id="baseserviceservicetype"></a>`serviceType` | [`ServiceType`](#servicetype) | Type of the service. |
| <a id="baseservicetype"></a>`type` | [`String`](#string) | Class name of the service. |
### `Blob`
......@@ -12028,6 +12029,7 @@ Represents an iteration cadence.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="jiraserviceactive"></a>`active` | [`Boolean`](#boolean) | Indicates if the service is active. |
| <a id="jiraserviceservicetype"></a>`serviceType` | [`ServiceType`](#servicetype) | Type of the service. |
| <a id="jiraservicetype"></a>`type` | [`String`](#string) | Class name of the service. |
#### Fields with arguments
......@@ -19284,6 +19286,7 @@ Implementations:
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="serviceactive"></a>`active` | [`Boolean`](#boolean) | Indicates if the service is active. |
| <a id="serviceservicetype"></a>`serviceType` | [`ServiceType`](#servicetype) | Type of the service. |
| <a id="servicetype"></a>`type` | [`String`](#string) | Class name of the service. |
#### `TimeboxReportInterface`
......@@ -12,14 +12,13 @@ module EE
private
override :type_description
def type_description(type)
def type_description(name, type)
description = super
description = [description, ' (Gitlab.com only)'].join if saas_only?(type)
description = [description, ' (Gitlab.com only)'].join if saas_only?(name)
description
end
def saas_only?(type)
name = ::Integration.integration_type_to_name(type)
def saas_only?(name)
::Integration.saas_only_integration_names.include?(name)
end
end
......
......@@ -286,12 +286,17 @@ module EE
end
def with_slack_application_disabled
joins(<<~SQL)
LEFT JOIN #{::Integration.table_name} ON #{::Integration.table_name}.project_id = projects.id
AND #{::Integration.table_name}.type = 'GitlabSlackApplicationService'
AND #{::Integration.table_name}.active IS true
SQL
.where(integrations: { id: nil })
# Using Arel to avoid exposing what the column backing the type: attribute is
# rubocop: disable GitlabSecurity/PublicSend
with_active_slack = ::Integration.active.by_name(:gitlab_slack_application)
join_contraint = arel_table[:id].eq(::Integration.arel_table[:project_id])
constraint = with_active_slack.where_clause.send(:predicates).reduce(join_contraint) { |a, b| a.and(b) }
join = arel_table.join(::Integration.arel_table, Arel::Nodes::OuterJoin).on(constraint).join_sources
# rubocop: enable GitlabSecurity/PublicSend
joins(join).where(integrations: { id: nil })
rescue ::Integration::UnknownType
all
end
override :with_web_entity_associations
......
......@@ -4,12 +4,12 @@ FactoryBot.define do
factory :gitlab_slack_application_integration, class: 'Integrations::GitlabSlackApplication' do
project
active { true }
type { 'GitlabSlackApplicationService' }
type { 'Integrations::GitlabSlackApplication' }
end
factory :github_integration, class: 'Integrations::Github' do
project
type { 'GithubService' }
type { 'Integrations::Github' }
active { true }
token { 'github-token' }
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['ServiceType'] do
it 'exposes all the EE project services' do
expect(described_class.values.keys).to include(*ee_service_enums)
end
def ee_service_enums
%w[
GITHUB_SERVICE
]
end
it 'coerces values correctly' do
integration = build(:github_integration)
expect(described_class.coerce_isolated_result(integration.type)).to eq 'GITHUB_SERVICE'
end
end
......@@ -1879,18 +1879,37 @@ RSpec.describe Project do
end
describe '#with_slack_application_disabled' do
it 'returns projects where Slack application is disabled' do
project1 = create(:project)
project2 = create(:project)
let(:project1) { create(:project) }
let(:project2) { create(:project) }
let(:project3) { create(:project) }
before do
create(:gitlab_slack_application_integration, project: project2)
create(:gitlab_slack_application_integration, project: project3, active: false)
end
context 'when slack applications are available' do
it 'returns projects where Slack application is disabled or absent' do
projects = described_class.with_slack_application_disabled
expect(projects).to include(project1)
expect(projects).to include(project1, project3)
expect(projects).not_to include(project2)
end
end
context 'when slack applications are not available' do
before do
allow(::Gitlab).to receive(:dev_or_test_env?).and_return(false)
end
it 'returns projects where Slack application is disabled or absent' do
projects = described_class.with_slack_application_disabled
expect(projects).to include(project1, project2, project3)
end
end
end
describe '#licensed_features', :saas do
let(:plan_license) { :free }
let(:global_license) { create(:license) }
......
......@@ -12,6 +12,16 @@ FactoryBot.define do
issue_tracker
end
factory :jenkins_integration, class: 'Integrations::Jenkins' do
project
active { true }
type { 'Integrations::Jenkins' }
jenkins_url { 'http://jenkins.example.com/' }
project_name { 'my-project' }
username { 'jenkings-user' }
password { 'passw0rd' }
end
factory :datadog_integration, class: 'Integrations::Datadog' do
project
active { true }
......@@ -20,7 +30,7 @@ FactoryBot.define do
factory :emails_on_push_integration, class: 'Integrations::EmailsOnPush' do
project
type { 'EmailsOnPushService' }
type { 'Integrations::EmailsOnPush' }
active { true }
push_events { true }
tag_push_events { true }
......@@ -54,7 +64,7 @@ FactoryBot.define do
factory :jira_integration, class: 'Integrations::Jira' do
project
active { true }
type { 'JiraService' }
type { 'Integrations::Jira' }
transient do
create_data { true }
......@@ -88,7 +98,7 @@ FactoryBot.define do
factory :zentao_integration, class: 'Integrations::Zentao' do
project
active { true }
type { 'ZentaoService' }
type { 'Integrations::Zentao' }
transient do
create_data { true }
......@@ -167,7 +177,7 @@ FactoryBot.define do
factory :external_wiki_integration, class: 'Integrations::ExternalWiki' do
project
type { 'ExternalWikiService' }
type { 'Integrations::ExternalWiki' }
active { true }
external_wiki_url { 'http://external-wiki-url.com' }
end
......@@ -178,24 +188,39 @@ FactoryBot.define do
password { 'my-secret-password' }
end
trait :chat_notification do
webhook { 'https://example.com/webhook' }
end
trait :inactive do
active { false }
end
factory :mattermost_integration, class: 'Integrations::Mattermost' do
chat_notification
project
type { 'Integrations::Mattermost' }
active { true }
end
# avoids conflict with slack_integration factory
factory :integrations_slack, class: 'Integrations::Slack' do
chat_notification
project
active { true }
webhook { 'https://slack.service.url' }
type { 'SlackService' }
type { 'Integrations::Slack' }
end
factory :slack_slash_commands_integration, class: 'Integrations::SlackSlashCommands' do
project
active { true }
type { 'SlackSlashCommandsService' }
type { 'Integrations::SlackSlashCommands' }
end
factory :pipelines_email_integration, class: 'Integrations::PipelinesEmail' do
project
active { true }
type { 'PipelinesEmailService' }
type { 'Integrations::PipelinesEmail' }
recipients { 'test@example.com' }
end
......
......@@ -5,8 +5,7 @@ FactoryBot.define do
skip_create # non-model factories (i.e. without #save)
initialize_with do
projects = create_list(:project, 3)
projects << create(:project, :repository)
projects = create_list(:project, 4, :repository)
group = create(:group)
create(:board, project: projects[0])
create(:jira_integration, project: projects[0])
......@@ -19,16 +18,21 @@ FactoryBot.define do
create(:jira_import_state, :finished, project: projects[1], label: jira_label, imported_issues_count: 3)
create(:jira_import_state, :scheduled, project: projects[1], label: jira_label)
create(:prometheus_integration, project: projects[1])
create(:integration, project: projects[1], type: 'JenkinsService', active: true)
create(:integration, project: projects[0], type: 'SlackSlashCommandsService', active: true)
create(:integration, project: projects[1], type: 'SlackService', active: true)
create(:integration, project: projects[2], type: 'SlackService', active: true)
create(:integration, project: projects[2], type: 'MattermostService', active: false)
create(:integration, group: group, project: nil, type: 'MattermostService', active: true)
mattermost_instance = create(:integration, :instance, type: 'MattermostService', active: true)
create(:integration, project: projects[1], type: 'MattermostService', active: true, inherit_from_id: mattermost_instance.id)
create(:integration, group: group, project: nil, type: 'SlackService', active: true, inherit_from_id: mattermost_instance.id)
create(:integration, project: projects[2], type: 'CustomIssueTrackerService', active: true)
create(:jenkins_integration, project: projects[1])
# slack
create(:slack_slash_commands_integration, project: projects[0])
create(:integrations_slack, project: projects[1])
create(:integrations_slack, project: projects[2])
# mattermost
create(:mattermost_integration, project: projects[2], active: false)
create(:mattermost_integration, group: group, project: nil)
mattermost_instance = create(:mattermost_integration, :instance)
create(:mattermost_integration, project: projects[1], inherit_from_id: mattermost_instance.id)
create(:integrations_slack, group: group, project: nil, active: true, inherit_from_id: mattermost_instance.id)
create(:custom_issue_tracker_integration, project: projects[2], active: true)
create(:project_error_tracking_setting, project: projects[0])
create(:project_error_tracking_setting, project: projects[1], enabled: false)
alert_bot_issues = create_list(:incident, 2, project: projects[0], author: User.alert_bot)
......
......@@ -96,10 +96,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
context 'view merge request with external CI service' do
before do
create(:integration, project: project,
active: true,
type: 'DroneCiService',
category: 'ci')
create(:drone_ci_integration, project: project)
visit project_merge_request_path(project, merge_request)
end
......
......@@ -6,7 +6,7 @@ RSpec.describe GitlabSchema.types['BaseService'] do
specify { expect(described_class.graphql_name).to eq('BaseService') }
it 'has basic expected fields' do
expect(described_class).to have_graphql_fields(:type, :active)
expect(described_class).to have_graphql_fields(:type, :active, :service_type)
end
specify { expect(described_class).to require_graphql_authorizations(:admin_project) }
......
......@@ -6,7 +6,7 @@ RSpec.describe GitlabSchema.types['JiraService'] do
specify { expect(described_class.graphql_name).to eq('JiraService') }
it 'has basic expected fields' do
expect(described_class).to have_graphql_fields(:type, :active, :projects)
expect(described_class).to have_graphql_fields(:type, :active, :projects, :service_type)
end
specify { expect(described_class).to require_graphql_authorizations(:admin_project) }
......
......@@ -4,10 +4,52 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['ServiceType'] do
it 'exposes all the existing project services' do
expect(described_class.values.keys).to match_array(available_services_enum)
expect(described_class.values.keys).to include(*core_service_enums)
end
def available_services_enum
::Integration.available_integration_types(include_dev: false).map(&:underscore).map(&:upcase)
def core_service_enums
%w[
ASANA_SERVICE
ASSEMBLA_SERVICE
BAMBOO_SERVICE
BUGZILLA_SERVICE
BUILDKITE_SERVICE
CAMPFIRE_SERVICE
CONFLUENCE_SERVICE
CUSTOM_ISSUE_TRACKER_SERVICE
DATADOG_SERVICE
DISCORD_SERVICE
DRONE_CI_SERVICE
EMAILS_ON_PUSH_SERVICE
EWM_SERVICE
EXTERNAL_WIKI_SERVICE
FLOWDOCK_SERVICE
HANGOUTS_CHAT_SERVICE
IRKER_SERVICE
JENKINS_SERVICE
JIRA_SERVICE
MATTERMOST_SERVICE
MATTERMOST_SLASH_COMMANDS_SERVICE
MICROSOFT_TEAMS_SERVICE
PACKAGIST_SERVICE
PIPELINES_EMAIL_SERVICE
PIVOTALTRACKER_SERVICE
PROMETHEUS_SERVICE
PUSHOVER_SERVICE
REDMINE_SERVICE
SHIMO_SERVICE
SLACK_SERVICE
SLACK_SLASH_COMMANDS_SERVICE
TEAMCITY_SERVICE
UNIFY_CIRCUIT_SERVICE
WEBEX_TEAMS_SERVICE
YOUTRACK_SERVICE
ZENTAO_SERVICE
]
end
it 'coerces values correctly' do
integration = build(:jenkins_integration)
expect(described_class.coerce_isolated_result(integration.type)).to eq 'JENKINS_SERVICE'
end
end
......@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Types::Projects::ServiceType do
specify { expect(described_class).to have_graphql_fields(:type, :active) }
specify { expect(described_class).to have_graphql_fields(:type, :service_type, :active) }
describe ".resolve_type" do
it 'resolves the corresponding type for objects' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Integrations::StiType do
let(:types) { ['AsanaService', 'Integrations::Asana', Integrations::Asana] }
describe '#serialize' do
context 'SQL SELECT' do
let(:expected_sql) do
<<~SQL.strip
FROM "integrations" WHERE "integrations"."type" = 'AsanaService'
SQL
end
it 'forms SQL SELECT statements correctly' do
sql_statements = types.map do |type|
Integration.where(type: type).to_sql
end
expect(sql_statements).to all(end_with(expected_sql))
end
end
context 'SQL CREATE' do
let(:expected_sql) do
<<~SQL.strip
INSERT INTO "integrations" ("type") VALUES ('AsanaService')
SQL
end
it 'forms SQL CREATE statements correctly' do
sql_statements = types.map do |type|
record = ActiveRecord::QueryRecorder.new { Integration.insert({ type: type }) }
record.log.first
end
expect(sql_statements).to all(include(expected_sql))
end
end
context 'SQL UPDATE' do
let(:expected_sql) do
<<~SQL.strip
UPDATE "integrations" SET "type" = 'AsanaService'
SQL
end
let_it_be(:integration) { create(:integration) }
it 'forms SQL UPDATE statements correctly' do
sql_statements = types.map do |type|
record = ActiveRecord::QueryRecorder.new { integration.update_column(:type, type) }
record.log.first
end
expect(sql_statements).to all(include(expected_sql))
end
end
context 'SQL DELETE' do
let(:expected_sql) do
<<~SQL.strip
DELETE FROM "integrations" WHERE "integrations"."type" = 'AsanaService'
SQL
end
it 'forms SQL DELETE statements correctly' do
sql_statements = types.map do |type|
record = ActiveRecord::QueryRecorder.new { Integration.delete_by(type: type) }
record.log.first
end
expect(sql_statements).to all(match(expected_sql))
end
end
end
describe '#deserialize' do
specify 'it deserializes type correctly', :aggregate_failures do
types.each do |type|
service = create(:integration, type: type)
expect(service.type).to eq('AsanaService')
end
end
end
describe '#cast' do
it 'casts type as model correctly', :aggregate_failures do
create(:integration, type: 'AsanaService')
types.each do |type|
expect(Integration.find_by(type: type)).to be_kind_of(Integrations::Asana)
end
end
end
describe '#changed?' do
it 'detects changes correctly', :aggregate_failures do
service = create(:integration, type: 'AsanaService')
types.each do |type|
service.type = type
expect(service).not_to be_changed
end
service.type = 'NewType'
expect(service).to be_changed
end
end
end
......@@ -85,14 +85,14 @@ RSpec.describe Integration do
subject { described_class.by_type(type) }
context 'when type is "JiraService"' do
let(:type) { 'JiraService' }
context 'when type is "Integrations::JiraService"' do
let(:type) { 'Integrations::Jira' }
it { is_expected.to match_array([integration1, integration2]) }
end
context 'when type is "RedmineService"' do
let(:type) { 'RedmineService' }
context 'when type is "Integrations::Redmine"' do
let(:type) { 'Integrations::Redmine' }
it { is_expected.to match_array([integration3]) }
end
......@@ -103,7 +103,7 @@ RSpec.describe Integration do
let!(:integration2) { create(:jira_integration) }
it 'returns the right group integration' do
expect(described_class.for_group(group)).to match_array([integration1])
expect(described_class.for_group(group)).to contain_exactly(integration1)
end
end
......@@ -376,22 +376,24 @@ RSpec.describe Integration do
let_it_be(:instance_integration) { create(:jira_integration, :instance) }
it 'returns the instance integration' do
expect(described_class.default_integration('JiraService', project)).to eq(instance_integration)
expect(described_class.default_integration('Integrations::Jira', project)).to eq(instance_integration)
end
it 'returns nil for nonexistent integration type' do
expect(described_class.default_integration('HipchatService', project)).to eq(nil)
expect(described_class.default_integration('Integrations::Hipchat', project)).to eq(nil)
end
context 'with a group integration' do
let(:integration_name) { 'Integrations::Jira' }
let_it_be(:group_integration) { create(:jira_integration, group_id: group.id, project_id: nil) }
it 'returns the group integration for a project' do
expect(described_class.default_integration('JiraService', project)).to eq(group_integration)
expect(described_class.default_integration(integration_name, project)).to eq(group_integration)
end
it 'returns the instance integration for a group' do
expect(described_class.default_integration('JiraService', group)).to eq(instance_integration)
expect(described_class.default_integration(integration_name, group)).to eq(instance_integration)
end
context 'with a subgroup' do
......@@ -400,18 +402,18 @@ RSpec.describe Integration do
let!(:project) { create(:project, group: subgroup) }
it 'returns the closest group integration for a project' do
expect(described_class.default_integration('JiraService', project)).to eq(group_integration)
expect(described_class.default_integration(integration_name, project)).to eq(group_integration)
end
it 'returns the closest group integration for a subgroup' do
expect(described_class.default_integration('JiraService', subgroup)).to eq(group_integration)
expect(described_class.default_integration(integration_name, subgroup)).to eq(group_integration)
end
context 'having a integration with custom settings' do
let!(:subgroup_integration) { create(:jira_integration, group_id: subgroup.id, project_id: nil) }
it 'returns the closest group integration for a project' do
expect(described_class.default_integration('JiraService', project)).to eq(subgroup_integration)
expect(described_class.default_integration(integration_name, project)).to eq(subgroup_integration)
end
end
......@@ -419,7 +421,7 @@ RSpec.describe Integration do
let!(:subgroup_integration) { create(:jira_integration, group_id: subgroup.id, project_id: nil, inherit_from_id: group_integration.id) }
it 'returns the closest group integration which does not inherit from its parent for a project' do
expect(described_class.default_integration('JiraService', project)).to eq(group_integration)
expect(described_class.default_integration(integration_name, project)).to eq(group_integration)
end
end
end
......@@ -556,19 +558,26 @@ RSpec.describe Integration do
end
end
describe '.integration_name_to_model' do
it 'returns the model for the given integration name' do
expect(described_class.integration_name_to_model('asana')).to eq(Integrations::Asana)
describe '.integration_name_to_type' do
it 'handles a simple case' do
expect(described_class.integration_name_to_type(:asana)).to eq 'Integrations::Asana'
end
it 'raises an error if integration name is invalid' do
expect { described_class.integration_name_to_model('foo') }.to raise_exception(NameError, /uninitialized constant FooService/)
it 'raises an error if the name is unknown' do
expect { described_class.integration_name_to_type('foo') }
.to raise_exception(described_class::UnknownType, /foo/)
end
it 'handles all available_integration_names' do
types = described_class.available_integration_names.map { described_class.integration_name_to_type(_1) }
expect(types).to all(start_with('Integrations::'))
end
end
describe '.integration_name_to_type' do
it 'transforms the name to a type' do
expect(described_class.integration_name_to_type('asana')).to eq('AsanaService')
describe '.integration_name_to_model' do
it 'raises an error if integration name is invalid' do
expect { described_class.integration_name_to_model('foo') }.to raise_exception(described_class::UnknownType, /foo/)
end
end
......
......@@ -7,7 +7,7 @@ RSpec.describe Integrations::Slack do
describe '#execute' do
before do
stub_request(:post, "https://slack.service.url/")
stub_request(:post, slack_integration.webhook)
end
let_it_be(:slack_integration) { create(:integrations_slack, branches_to_be_notified: 'all') }
......
......@@ -1470,7 +1470,7 @@ RSpec.describe Project, factory_default: :keep do
context 'when there is an active external issue tracker integration' do
let!(:integration) do
create(:integration, project: project, type: 'JiraService', category: 'issue_tracker', active: true)
create(:jira_integration, project: project, category: 'issue_tracker')
end
specify { is_expected.to eq(true) }
......@@ -1489,7 +1489,7 @@ RSpec.describe Project, factory_default: :keep do
context 'when there are two active external issue tracker integrations' do
let_it_be(:second_integration) do
create(:integration, project: project, type: 'CustomIssueTracker', category: 'issue_tracker', active: true)
create(:custom_issue_tracker_integration, project: project, category: 'issue_tracker')
end
it 'does not become false when external issue tracker integration is destroyed' do
......
......@@ -16,6 +16,7 @@ RSpec.describe 'query Jira service' do
services(active: true, type: JIRA_SERVICE) {
nodes {
type
serviceType
}
}
}
......@@ -23,7 +24,7 @@ RSpec.describe 'query Jira service' do
)
end
let(:services) { graphql_data.dig('project', 'services', 'nodes')}
let(:services) { graphql_data.dig('project', 'services', 'nodes') }
it_behaves_like 'unauthorized users cannot read services'
......@@ -35,10 +36,8 @@ RSpec.describe 'query Jira service' do
it_behaves_like 'a working graphql query'
it 'retuns list of jira imports' do
service = services.first
expect(service['type']).to eq('JiraService')
it 'returns list of jira integrations' do
expect(services).to contain_exactly({ 'type' => 'JiraService', 'serviceType' => 'JIRA_SERVICE' })
end
end
end
......@@ -13,7 +13,7 @@ RSpec.describe Deployments::HooksWorker do
it 'executes project services for deployment_hooks' do
deployment = create(:deployment, :running)
project = deployment.project
service = create(:integration, type: 'SlackService', project: project, deployment_events: true, active: true)
service = create(:integrations_slack, project: project, deployment_events: true)
expect(ProjectServiceWorker).to receive(:perform_async).with(service.id, an_instance_of(Hash))
......@@ -23,7 +23,7 @@ RSpec.describe Deployments::HooksWorker do
it 'does not execute an inactive service' do
deployment = create(:deployment, :running)
project = deployment.project
create(:integration, type: 'SlackService', project: project, deployment_events: true, active: false)
create(:integrations_slack, project: project, deployment_events: true, active: false)
expect(ProjectServiceWorker).not_to receive(:perform_async)
......
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