diff options
31 files changed, 261 insertions, 52 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dfbbc730b0..e0493d54fc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,29 +2,6 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. -## 12.8.3 - -### Fixed (8 changes) - -- Fix Group Import API file upload when object storage is disabled. !25715 -- Fix Web IDE fork modal showing no text. !25842 -- Fixed regression when URL was encoded in a loop. !25849 -- Fixed repository browsing for folders with non-ascii characters. !25877 -- Fix search for Sentry error list. !26129 -- Send credentials with GraphQL fetch requests. !26386 -- Show CI status in project dashboards. !26403 -- Rescue invalid URLs during badge retrieval in asset proxy. !26524 - -### Performance (2 changes) - -- Disable Marginalia line backtrace in production. !26199 -- Remove unnecessary Redis deletes for broadcast messages. !26541 - -### Other (1 change, 1 of them is from the community) - -- Fix fixtures for Error Tracking Web UI. !26233 (Takuya Noguchi) - - ## 12.8.2 ### Security (17 changes) diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue index 048dc274cf2..8babc05f1ce 100644 --- a/app/assets/javascripts/diffs/components/diff_file.vue +++ b/app/assets/javascripts/diffs/components/diff_file.vue @@ -179,18 +179,19 @@ export default { <div v-if="errorMessage" class="diff-viewer"> <div class="nothing-here-block" v-html="errorMessage"></div> </div> - <div v-else-if="isCollapsed" class="nothing-here-block diff-collapsed"> - {{ __('This diff is collapsed.') }} - <a class="click-to-expand js-click-to-expand" href="#" @click.prevent="handleToggle">{{ - __('Click to expand it.') - }}</a> - </div> - <diff-content - v-else - :class="{ hidden: isCollapsed || isFileTooLarge }" - :diff-file="file" - :help-page-path="helpPagePath" - /> + <template v-else> + <div v-show="isCollapsed" class="nothing-here-block diff-collapsed"> + {{ __('This diff is collapsed.') }} + <a class="click-to-expand js-click-to-expand" href="#" @click.prevent="handleToggle">{{ + __('Click to expand it.') + }}</a> + </div> + <diff-content + v-show="!isCollapsed && !isFileTooLarge" + :diff-file="file" + :help-page-path="helpPagePath" + /> + </template> </div> </template> </div> diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index e8d3d5f62cb..1f15ac42260 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -16,10 +16,18 @@ module AuthHelper Gitlab::Auth.omniauth_enabled? end - def provider_has_icon?(name) + def provider_has_custom_icon?(name) + icon_for_provider(name.to_s) + end + + def provider_has_builtin_icon?(name) PROVIDERS_WITH_ICONS.include?(name.to_s) end + def provider_has_icon?(name) + provider_has_builtin_icon?(name) || provider_has_custom_icon?(name) + end + def qa_class_for_provider(provider) { saml: 'qa-saml-login-button', @@ -35,6 +43,10 @@ module AuthHelper Gitlab::Auth::OAuth::Provider.label_for(name) end + def icon_for_provider(name) + Gitlab::Auth::OAuth::Provider.icon_for(name) + end + def form_based_provider_priority ['crowd', /^ldap/, 'kerberos'] end @@ -109,7 +121,9 @@ module AuthHelper def provider_image_tag(provider, size = 64) label = label_for_provider(provider) - if provider_has_icon?(provider) + if provider_has_custom_icon?(provider) + image_tag(icon_for_provider(provider), alt: label, title: "Sign in with #{label}") + elsif provider_has_builtin_icon?(provider) file_name = "#{provider.to_s.split('_').first}_#{size}.png" image_tag("auth_buttons/#{file_name}", alt: label, title: "Sign in with #{label}") diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb index 00b06ae2595..75dfad4f3df 100644 --- a/app/models/project_services/prometheus_service.rb +++ b/app/models/project_services/prometheus_service.rb @@ -88,7 +88,9 @@ class PrometheusService < MonitoringService return false if template? return false unless project - project.all_clusters.enabled.any? { |cluster| cluster.application_prometheus_available? } + project.all_clusters.enabled.eager_load(:application_prometheus).any? do |cluster| + cluster.application_prometheus&.available? + end end def allow_local_api_url? diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 7bf68e7d315..cef86e9763c 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -98,6 +98,7 @@ module Projects setup_authorizations current_user.invalidate_personal_projects_count + create_prometheus_service create_readme if @initialize_with_readme end @@ -169,6 +170,20 @@ module Projects end # rubocop: enable CodeReuse/ActiveRecord + def create_prometheus_service + service = @project.find_or_initialize_service(::PrometheusService.to_param) + + if service.prometheus_available? + service.save! + else + @project.prometheus_service = nil + end + + rescue ActiveRecord::RecordInvalid => e + Gitlab::ErrorTracking.track_exception(e, extra: { project_id: project.id }) + @project.prometheus_service = nil + end + def set_project_name_from_path # Set project name from path if @project.name.present? && @project.path.present? diff --git a/changelogs/unreleased/207837-circular-encoding.yml b/changelogs/unreleased/207837-circular-encoding.yml new file mode 100644 index 00000000000..30aa7bf88a5 --- /dev/null +++ b/changelogs/unreleased/207837-circular-encoding.yml @@ -0,0 +1,5 @@ +--- +title: Fixed regression when URL was encoded in a loop +merge_request: 25849 +author: +type: fixed diff --git a/changelogs/unreleased/207857-fix-web-ide-modal-no-text.yml b/changelogs/unreleased/207857-fix-web-ide-modal-no-text.yml new file mode 100644 index 00000000000..74bbb312f19 --- /dev/null +++ b/changelogs/unreleased/207857-fix-web-ide-modal-no-text.yml @@ -0,0 +1,5 @@ +--- +title: Fix Web IDE fork modal showing no text +merge_request: 25842 +author: +type: fixed diff --git a/changelogs/unreleased/208548-better-spec-test-for-error-tracking-web-ui.yml b/changelogs/unreleased/208548-better-spec-test-for-error-tracking-web-ui.yml new file mode 100644 index 00000000000..6b6d479e815 --- /dev/null +++ b/changelogs/unreleased/208548-better-spec-test-for-error-tracking-web-ui.yml @@ -0,0 +1,5 @@ +--- +title: Fix fixtures for Error Tracking Web UI +merge_request: 26233 +author: Takuya Noguchi +type: other diff --git a/changelogs/unreleased/25744-optional-custom-icon-in-omniauth-login-label.yml b/changelogs/unreleased/25744-optional-custom-icon-in-omniauth-login-label.yml new file mode 100644 index 00000000000..c82c5e0407c --- /dev/null +++ b/changelogs/unreleased/25744-optional-custom-icon-in-omniauth-login-label.yml @@ -0,0 +1,5 @@ +--- +title: Optional custom icon in the OmniAuth login labels +merge_request: 25744 +author: Tobias Wawryniuk, Luca Leonardo Scorcia +type: added diff --git a/changelogs/unreleased/georgekoltsov-fix-import-export-uploader.yml b/changelogs/unreleased/georgekoltsov-fix-import-export-uploader.yml new file mode 100644 index 00000000000..0c43c93ce89 --- /dev/null +++ b/changelogs/unreleased/georgekoltsov-fix-import-export-uploader.yml @@ -0,0 +1,5 @@ +--- +title: Fix Group Import API file upload when object storage is disabled +merge_request: 25715 +author: +type: fixed diff --git a/changelogs/unreleased/jdb-hide-dont-remove-collapsed-files.yml b/changelogs/unreleased/jdb-hide-dont-remove-collapsed-files.yml new file mode 100644 index 00000000000..0ce204583ce --- /dev/null +++ b/changelogs/unreleased/jdb-hide-dont-remove-collapsed-files.yml @@ -0,0 +1,5 @@ +--- +title: Improved MR toggle file performance by hiding instead of removing +merge_request: 26181 +author: +type: performance diff --git a/changelogs/unreleased/lm-fix-error-query.yml b/changelogs/unreleased/lm-fix-error-query.yml new file mode 100644 index 00000000000..2baa316dd5a --- /dev/null +++ b/changelogs/unreleased/lm-fix-error-query.yml @@ -0,0 +1,5 @@ +--- +title: Fix search for Sentry error list +merge_request: 26129 +author: +type: fixed diff --git a/changelogs/unreleased/mwaw-activate_shared_services_on_project_creation.yml b/changelogs/unreleased/mwaw-activate_shared_services_on_project_creation.yml new file mode 100644 index 00000000000..b7641675f88 --- /dev/null +++ b/changelogs/unreleased/mwaw-activate_shared_services_on_project_creation.yml @@ -0,0 +1,6 @@ +--- +title: Activate Prometheus integration service for newly created project if this project + has access to shared Prometheus application. +merge_request: 24676 +author: +type: fixed diff --git a/changelogs/unreleased/ph-p207499-fixNonAsciiChars.yml b/changelogs/unreleased/ph-p207499-fixNonAsciiChars.yml new file mode 100644 index 00000000000..806f4372f89 --- /dev/null +++ b/changelogs/unreleased/ph-p207499-fixNonAsciiChars.yml @@ -0,0 +1,5 @@ +--- +title: Fixed repository browsing for folders with non-ascii characters +merge_request: 25877 +author: +type: fixed diff --git a/changelogs/unreleased/revert-e0613e64.yml b/changelogs/unreleased/revert-e0613e64.yml new file mode 100644 index 00000000000..e94f1df2f4b --- /dev/null +++ b/changelogs/unreleased/revert-e0613e64.yml @@ -0,0 +1,5 @@ +--- +title: Show CI status in project dashboards +merge_request: 26403 +author: +type: fixed diff --git a/changelogs/unreleased/sh-disable-line-in-marginalia.yml b/changelogs/unreleased/sh-disable-line-in-marginalia.yml new file mode 100644 index 00000000000..51be4db1965 --- /dev/null +++ b/changelogs/unreleased/sh-disable-line-in-marginalia.yml @@ -0,0 +1,5 @@ +--- +title: Disable Marginalia line backtrace in production +merge_request: 26199 +author: +type: performance diff --git a/changelogs/unreleased/sh-optimize-broadcast-message-redis.yml b/changelogs/unreleased/sh-optimize-broadcast-message-redis.yml new file mode 100644 index 00000000000..c69f3ded73d --- /dev/null +++ b/changelogs/unreleased/sh-optimize-broadcast-message-redis.yml @@ -0,0 +1,5 @@ +--- +title: Remove unnecessary Redis deletes for broadcast messages +merge_request: 26541 +author: +type: performance diff --git a/changelogs/unreleased/use-addressable-for-asset-proxy-badge-render.yml b/changelogs/unreleased/use-addressable-for-asset-proxy-badge-render.yml new file mode 100644 index 00000000000..6a021df8027 --- /dev/null +++ b/changelogs/unreleased/use-addressable-for-asset-proxy-badge-render.yml @@ -0,0 +1,5 @@ +--- +title: Rescue invalid URLs during badge retrieval in asset proxy +merge_request: 26524 +author: +type: fixed diff --git a/changelogs/unreleased/vs-add-credentials-option-for-apollo-link.yml b/changelogs/unreleased/vs-add-credentials-option-for-apollo-link.yml new file mode 100644 index 00000000000..1863f0de26f --- /dev/null +++ b/changelogs/unreleased/vs-add-credentials-option-for-apollo-link.yml @@ -0,0 +1,5 @@ +--- +title: Send credentials with GraphQL fetch requests +merge_request: 26386 +author: +type: fixed diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md index 0160db1b388..728be699772 100644 --- a/doc/administration/auth/oidc.md +++ b/doc/administration/auth/oidc.md @@ -34,6 +34,7 @@ The OpenID Connect will provide you with a client details and secret for you to gitlab_rails['omniauth_providers'] = [ { 'name' => 'openid_connect', 'label' => '<your_oidc_label>', + 'icon' => '<custom_provider_icon>', 'args' => { 'name' => 'openid_connect', 'scope' => ['openid','profile'], @@ -58,6 +59,7 @@ The OpenID Connect will provide you with a client details and secret for you to ```yaml - { name: 'openid_connect', label: '<your_oidc_label>', + icon: '<custom_provider_icon>', args: { name: 'openid_connect', scope: ['openid','profile'], @@ -82,6 +84,8 @@ The OpenID Connect will provide you with a client details and secret for you to 1. For the configuration above, change the values for the provider to match your OpenID Connect client setup. Use the following as a guide: - `<your_oidc_label>` is the label that will be displayed on the login page. + - `<custom_provider_icon>` (optional) is the icon that will be displayed on the login page. Icons for the major social login platforms are built-in into GitLab, + but can be overridden by specifying this parameter. Both local paths and absolute URLs are accepted. - `<your_oidc_url>` (optional) is the URL that points to the OpenID Connect provider. For example, `https://example.com/auth/realms/your-realm`. If this value is not provided, the URL is constructed from the `client_options` in the following format: `<client_options.scheme>://<client_options.host>:<client_options.port>`. - If `discovery` is set to `true`, the OpenID Connect provider will try to auto discover the client options using `<your_oidc_url>/.well-known/openid-configuration`. Defaults to `false`. diff --git a/doc/api/groups.md b/doc/api/groups.md index a4fc3f95c5c..235f7f4081a 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -484,6 +484,7 @@ Parameters: | `name` | string | yes | The name of the group. | | `path` | string | yes | The path of the group. | | `description` | string | no | The group's description. | +| `membership_lock` | boolean | no | **(STARTER)** Prevent adding new members to project membership within this group. | | `visibility` | string | no | The group's visibility. Can be `private`, `internal`, or `public`. | | `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. | | `require_two_factor_authentication` | boolean | no | Require all users in this group to setup Two-factor authentication. | diff --git a/doc/development/fe_guide/vuex.md b/doc/development/fe_guide/vuex.md index cd18091abdf..b69bbb8b527 100644 --- a/doc/development/fe_guide/vuex.md +++ b/doc/development/fe_guide/vuex.md @@ -1,6 +1,13 @@ # Vuex -To manage the state of an application you should use [Vuex][vuex-docs]. +When there's a clear benefit to separating state management from components (e.g. due to state complexity) we recommend using [Vuex][vuex-docs] over any other Flux pattern. Otherwise, feel free to manage state within the components. + +Vuex should be strongly considered when: +- You expect multiple parts of the application to react to state changes +- There's a need to share data between multiple components +- There are complex interactions with Backend, e.g. multiple API calls +- The app involves interacting with backend via both traditional REST API and GraphQL (especially when moving the REST API over to GraphQL is a pending backend task) + _Note:_ All of the below is explained in more detail in the official [Vuex documentation][vuex-docs]. diff --git a/lib/api/projects.rb b/lib/api/projects.rb index f9d08881acf..ca55e6ac010 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -177,6 +177,7 @@ module API use :create_params end post do + Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/21139') attrs = declared_params(include_missing: false) attrs = translate_params_for_compatibility(attrs) filter_attributes_using_license!(attrs) @@ -209,6 +210,7 @@ module API end # rubocop: disable CodeReuse/ActiveRecord post "user/:user_id" do + Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/21139') authenticated_as_admin! user = User.find_by(id: params.delete(:user_id)) not_found!('User') unless user diff --git a/lib/gitlab/auth/o_auth/provider.rb b/lib/gitlab/auth/o_auth/provider.rb index e7d758353bf..4d6b95ecd1b 100644 --- a/lib/gitlab/auth/o_auth/provider.rb +++ b/lib/gitlab/auth/o_auth/provider.rb @@ -75,6 +75,12 @@ module Gitlab config = config_for(name) (config && config['label']) || LABELS[name] || name.titleize end + + def self.icon_for(name) + name = name.to_s + config = config_for(name) + config && config['icon'] + end end end end diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 4eb1ccf32ba..4b5455c0ec9 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -141,6 +141,20 @@ module Gitlab # kwargs.merge(deadline: Time.now + 10) # end # + # The optional remote_storage keyword argument is used to enable + # inter-gitaly calls. Say you have an RPC that needs to pull data from + # one repository to another. For example, to fetch a branch from a + # (non-deduplicated) fork into the fork parent. In that case you would + # send an RPC call to the Gitaly server hosting the fork parent, and in + # the request, you would tell that Gitaly server to pull Git data from + # the fork. How does that Gitaly server connect to the Gitaly server the + # forked repo lives on? This is the problem `remote_storage:` solves: it + # adds address and authentication information to the call, as gRPC + # metadata (under the `gitaly-servers` header). The request would say + # "pull from repo X on gitaly-2". In the Ruby code you pass + # `remote_storage: 'gitaly-2'`. And then the metadata would say + # "gitaly-2 is at network address tcp://10.0.1.2:8075". + # def self.call(storage, service, rpc, request, remote_storage: nil, timeout: default_timeout, &block) self.measure_timings(service, rpc, request) do self.execute(storage, service, rpc, request, remote_storage: remote_storage, timeout: timeout, &block) diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb index fb7cca3997b..c669119fa4e 100644 --- a/spec/controllers/projects/services_controller_spec.rb +++ b/spec/controllers/projects/services_controller_spec.rb @@ -153,16 +153,6 @@ describe Projects::ServicesController do expect(flash[:notice]).to eq 'Jira settings saved, but not activated.' end end - - context 'when activating Jira service from a template' do - let(:service) do - create(:jira_service, project: project, template: true) - end - - it 'activate Jira service from template' do - expect(flash[:notice]).to eq 'Jira activated.' - end - end end describe 'as JSON' do diff --git a/spec/features/merge_request/user_manages_subscription_spec.rb b/spec/features/merge_request/user_manages_subscription_spec.rb index 5c6072c57ff..54d27a06bb1 100644 --- a/spec/features/merge_request/user_manages_subscription_spec.rb +++ b/spec/features/merge_request/user_manages_subscription_spec.rb @@ -16,6 +16,8 @@ describe 'User manages subscription', :js do it 'toggles subscription' do page.within('.js-issuable-subscribe-button') do + wait_for_requests + expect(page).to have_css 'button:not(.is-checked)' find('button:not(.is-checked)').click diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb index 9179019cd6a..073ce581741 100644 --- a/spec/helpers/auth_helper_spec.rb +++ b/spec/helpers/auth_helper_spec.rb @@ -154,4 +154,34 @@ describe AuthHelper do expect(helper.unlink_provider_allowed?(provider)).to eq 'policy_unlink_result' end end + + describe '#provider_has_icon?' do + it 'returns true for defined providers' do + expect(helper.provider_has_icon?(described_class::PROVIDERS_WITH_ICONS.sample)).to eq true + end + + it 'returns false for undefined providers' do + expect(helper.provider_has_icon?('test')).to be_falsey + end + + context 'when provider is defined by config' do + before do + allow(Gitlab::Auth::OAuth::Provider).to receive(:icon_for).with('test').and_return('icon') + end + + it 'returns true' do + expect(helper.provider_has_icon?('test')).to be_truthy + end + end + + context 'when provider is not defined by config' do + before do + allow(Gitlab::Auth::OAuth::Provider).to receive(:icon_for).with('test').and_return(nil) + end + + it 'returns true' do + expect(helper.provider_has_icon?('test')).to be_falsey + end + end + end end diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js index eab4f4fb17f..e2b64a5418e 100644 --- a/spec/javascripts/diffs/components/diff_file_spec.js +++ b/spec/javascripts/diffs/components/diff_file_spec.js @@ -23,6 +23,9 @@ describe('DiffFile', () => { vm.$destroy(); }); + const findDiffContent = () => vm.$el.querySelector('.diff-content'); + const isVisible = el => el.style.display !== 'none'; + describe('template', () => { it('should render component with file header, file content components', done => { const el = vm.$el; @@ -69,13 +72,13 @@ describe('DiffFile', () => { describe('collapsed', () => { it('should not have file content', done => { - expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(1); + expect(isVisible(findDiffContent())).toBe(true); expect(vm.isCollapsed).toEqual(false); vm.isCollapsed = true; vm.file.renderIt = true; vm.$nextTick(() => { - expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(0); + expect(isVisible(findDiffContent())).toBe(false); done(); }); diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb index 1922bb065cf..fd4783a60f2 100644 --- a/spec/models/project_services/prometheus_service_spec.rb +++ b/spec/models/project_services/prometheus_service_spec.rb @@ -176,6 +176,15 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do it 'returns true' do expect(service.prometheus_available?).to be(true) end + + it 'avoids N+1 queries' do + service + 5.times do |i| + other_cluster = create(:cluster_for_group, :with_installed_helm, groups: [group], environment_scope: i) + create(:clusters_applications_prometheus, :installing, cluster: other_cluster) + end + expect { service.prometheus_available? }.not_to exceed_query_limit(1) + end end context 'cluster belongs to gitlab instance' do diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index a8e7919dc81..c5c16130edc 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -344,7 +344,7 @@ describe Projects::CreateService, '#execute' do context 'when there is an active service template' do before do - create(:service, project: nil, template: true, active: true) + create(:prometheus_service, project: nil, template: true, active: true) end it 'creates a service from this template' do @@ -394,6 +394,67 @@ describe Projects::CreateService, '#execute' do expect(rugged.config['gitlab.fullpath']).to eq project.full_path end + context 'when project has access to shared service' do + context 'Prometheus application is shared via group cluster' do + let(:cluster) { create(:cluster, :group, groups: [group]) } + let(:group) do + create(:group).tap do |group| + group.add_owner(user) + end + end + + before do + create(:clusters_applications_prometheus, :installed, cluster: cluster) + end + + it 'creates PrometheusService record', :aggregate_failures do + project = create_project(user, opts.merge!(namespace_id: group.id)) + service = project.prometheus_service + + expect(service.active).to be true + expect(service.manual_configuration?).to be false + expect(service.persisted?).to be true + end + end + + context 'Prometheus application is shared via instance cluster' do + let(:cluster) { create(:cluster, :instance) } + + before do + create(:clusters_applications_prometheus, :installed, cluster: cluster) + end + + it 'creates PrometheusService record', :aggregate_failures do + project = create_project(user, opts) + service = project.prometheus_service + + expect(service.active).to be true + expect(service.manual_configuration?).to be false + expect(service.persisted?).to be true + end + + it 'cleans invalid record and logs warning', :aggregate_failures do + invalid_service_record = build(:prometheus_service, properties: { api_url: nil, manual_configuration: true }.to_json) + allow_next_instance_of(Project) do |instance| + allow(instance).to receive(:build_prometheus_service).and_return(invalid_service_record) + end + + expect(Gitlab::ErrorTracking).to receive(:track_exception).with(an_instance_of(ActiveRecord::RecordInvalid), include(extra: { project_id: a_kind_of(Integer) })) + project = create_project(user, opts) + + expect(project.prometheus_service).to be_nil + end + end + + context 'shared Prometheus application is not available' do + it 'does not persist PrometheusService record', :aggregate_failures do + project = create_project(user, opts) + + expect(project.prometheus_service).to be_nil + end + end + end + context 'with external authorization enabled' do before do enable_external_authorization_service_check |