Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-06-01 09:09:36 +00:00
parent b7ff336e24
commit 8ef0366928
19 changed files with 175 additions and 77 deletions

View file

@ -239,7 +239,7 @@ export default {
data-testid="count-loading-icon" data-testid="count-loading-icon"
/> />
<span v-if="showingAllItems">{{ showingAllItemsText }}</span> <span v-if="showingAllItems">{{ showingAllItemsText }}</span>
<gl-intersection-observer v-else @update="onReachingListBottom"> <gl-intersection-observer v-else @appear="onReachingListBottom">
<span>{{ paginatedIssueText }}</span> <span>{{ paginatedIssueText }}</span>
</gl-intersection-observer> </gl-intersection-observer>
</li> </li>

View file

@ -1,16 +1,12 @@
<script> <script>
import { GlNav, GlNavItemDropdown, GlDropdownForm, GlTooltip } from '@gitlab/ui'; import { GlNav, GlNavItemDropdown, GlDropdownForm } from '@gitlab/ui';
import { s__ } from '~/locale';
import TopNavDropdownMenu from './top_nav_dropdown_menu.vue'; import TopNavDropdownMenu from './top_nav_dropdown_menu.vue';
const TOOLTIP = s__('TopNav|Switch to...');
export default { export default {
components: { components: {
GlNav, GlNav,
GlNavItemDropdown, GlNavItemDropdown,
GlDropdownForm, GlDropdownForm,
GlTooltip,
TopNavDropdownMenu, TopNavDropdownMenu,
}, },
props: { props: {
@ -19,15 +15,6 @@ export default {
required: true, required: true,
}, },
}, },
methods: {
findTooltipTarget() {
// ### Why use a target function instead of `v-gl-tooltip`?
// To get the tooltip to align correctly, we need it to target the actual
// toggle button which we don't directly render.
return this.$el.querySelector('.js-top-nav-dropdown-toggle');
},
},
TOOLTIP,
}; };
</script> </script>
@ -48,12 +35,5 @@ export default {
/> />
</gl-dropdown-form> </gl-dropdown-form>
</gl-nav-item-dropdown> </gl-nav-item-dropdown>
<gl-tooltip
boundary="window"
:boundary-padding="0"
:target="findTooltipTarget"
placement="right"
:title="$options.TOOLTIP"
/>
</gl-nav> </gl-nav>
</template> </template>

View file

@ -24,8 +24,12 @@ class FeatureFlagsFinder
private private
def feature_flags def feature_flags
if exclude_legacy_flags?
project.operations_feature_flags.new_version_only
else
project.operations_feature_flags project.operations_feature_flags
end end
end
def by_scope(items) def by_scope(items)
case params[:scope] case params[:scope]
@ -37,4 +41,9 @@ class FeatureFlagsFinder
items items
end end
end end
def exclude_legacy_flags?
Feature.enabled?(:remove_legacy_flags, project, default_enabled: :yaml) &&
Feature.disabled?(:remove_legacy_flags_override, project, default_enabled: :yaml)
end
end end

View file

@ -49,6 +49,8 @@ module Operations
scope :enabled, -> { where(active: true) } scope :enabled, -> { where(active: true) }
scope :disabled, -> { where(active: false) } scope :disabled, -> { where(active: false) }
scope :new_version_only, -> { where(version: :new_version_flag)}
enum version: { enum version: {
legacy_flag: 1, legacy_flag: 1,
new_version_flag: 2 new_version_flag: 2

View file

@ -0,0 +1,8 @@
---
name: remove_legacy_flags
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62484
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332243
milestone: '14.0'
type: development
group: group::release
default_enabled: false

View file

@ -0,0 +1,8 @@
---
name: remove_legacy_flags_override
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62484
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332243
milestone: '14.0'
type: development
group: group::release
default_enabled: false

View file

@ -0,0 +1,23 @@
# frozen_string_literal: true
class DropDevopsAdoptionNamespaceUniqueness < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
INDEX_NAME = 'index_analytics_devops_adoption_segments_on_namespace_id'
NEW_INDEX_NAME = 'idx_analytics_devops_adoption_segments_on_namespace_id'
def up
add_concurrent_index :analytics_devops_adoption_segments, :namespace_id, name: NEW_INDEX_NAME
remove_concurrent_index_by_name :analytics_devops_adoption_segments, INDEX_NAME
end
def down
# Clean up duplicated records
execute "DELETE FROM analytics_devops_adoption_segments WHERE id NOT IN (SELECT MIN(id) FROM analytics_devops_adoption_segments GROUP BY namespace_id)"
add_concurrent_index :analytics_devops_adoption_segments, :namespace_id, name: INDEX_NAME, unique: true
remove_concurrent_index_by_name :analytics_devops_adoption_segments, NEW_INDEX_NAME
end
end

View file

@ -0,0 +1 @@
ecef2157c20804acbad9d74df27febcf935f7f36920946fac211f3ef8b419f26

View file

@ -22273,6 +22273,8 @@ CREATE INDEX finding_evidences_on_vulnerability_occurrence_id ON vulnerability_f
CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_finding_links USING btree (vulnerability_occurrence_id); CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_finding_links USING btree (vulnerability_occurrence_id);
CREATE INDEX idx_analytics_devops_adoption_segments_on_namespace_id ON analytics_devops_adoption_segments USING btree (namespace_id);
CREATE INDEX idx_audit_events_part_on_entity_id_desc_author_id_created_at ON ONLY audit_events USING btree (entity_id, entity_type, id DESC, author_id, created_at); CREATE INDEX idx_audit_events_part_on_entity_id_desc_author_id_created_at ON ONLY audit_events USING btree (entity_id, entity_type, id DESC, author_id, created_at);
CREATE INDEX idx_award_emoji_on_user_emoji_name_awardable_type_awardable_id ON award_emoji USING btree (user_id, name, awardable_type, awardable_id); CREATE INDEX idx_award_emoji_on_user_emoji_name_awardable_type_awardable_id ON award_emoji USING btree (user_id, name, awardable_type, awardable_id);
@ -22459,8 +22461,6 @@ CREATE UNIQUE INDEX index_analytics_ca_project_value_streams_on_project_id_and_n
CREATE INDEX index_analytics_cycle_analytics_group_stages_custom_only ON analytics_cycle_analytics_group_stages USING btree (id) WHERE (custom = true); CREATE INDEX index_analytics_cycle_analytics_group_stages_custom_only ON analytics_cycle_analytics_group_stages USING btree (id) WHERE (custom = true);
CREATE UNIQUE INDEX index_analytics_devops_adoption_segments_on_namespace_id ON analytics_devops_adoption_segments USING btree (namespace_id);
CREATE INDEX index_application_settings_on_custom_project_templates_group_id ON application_settings USING btree (custom_project_templates_group_id); CREATE INDEX index_application_settings_on_custom_project_templates_group_id ON application_settings USING btree (custom_project_templates_group_id);
CREATE INDEX index_application_settings_on_file_template_project_id ON application_settings USING btree (file_template_project_id); CREATE INDEX index_application_settings_on_file_template_project_id ON application_settings USING btree (file_template_project_id);

View file

@ -97,8 +97,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="querydevopsadoptionsegmentsdirectdescendantsonly"></a>`directDescendantsOnly` | [`Boolean`](#boolean) | Limits segments to direct descendants of specified parent. | | <a id="querydevopsadoptionsegmentsdisplaynamespaceid"></a>`displayNamespaceId` | [`NamespaceID`](#namespaceid) | Filter by display namespace. |
| <a id="querydevopsadoptionsegmentsparentnamespaceid"></a>`parentNamespaceId` | [`NamespaceID`](#namespaceid) | Filter by ancestor namespace. |
### `Query.echo` ### `Query.echo`
@ -770,6 +769,7 @@ Input type: `BulkFindOrCreateDevopsAdoptionSegmentsInput`
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="mutationbulkfindorcreatedevopsadoptionsegmentsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | <a id="mutationbulkfindorcreatedevopsadoptionsegmentsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationbulkfindorcreatedevopsadoptionsegmentsdisplaynamespaceid"></a>`displayNamespaceId` | [`NamespaceID`](#namespaceid) | Display namespace ID. |
| <a id="mutationbulkfindorcreatedevopsadoptionsegmentsnamespaceids"></a>`namespaceIds` | [`[NamespaceID!]!`](#namespaceid) | List of Namespace IDs for the segments. | | <a id="mutationbulkfindorcreatedevopsadoptionsegmentsnamespaceids"></a>`namespaceIds` | [`[NamespaceID!]!`](#namespaceid) | List of Namespace IDs for the segments. |
#### Fields #### Fields
@ -1105,6 +1105,7 @@ Input type: `CreateDevopsAdoptionSegmentInput`
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="mutationcreatedevopsadoptionsegmentclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | <a id="mutationcreatedevopsadoptionsegmentclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcreatedevopsadoptionsegmentdisplaynamespaceid"></a>`displayNamespaceId` | [`NamespaceID`](#namespaceid) | Display namespace ID. |
| <a id="mutationcreatedevopsadoptionsegmentnamespaceid"></a>`namespaceId` | [`NamespaceID!`](#namespaceid) | Namespace ID to set for the segment. | | <a id="mutationcreatedevopsadoptionsegmentnamespaceid"></a>`namespaceId` | [`NamespaceID!`](#namespaceid) | Namespace ID to set for the segment. |
#### Fields #### Fields
@ -8276,6 +8277,7 @@ Segment.
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="devopsadoptionsegmentdisplaynamespace"></a>`displayNamespace` | [`Namespace`](#namespace) | Namespace where data should be displayed. |
| <a id="devopsadoptionsegmentid"></a>`id` | [`ID!`](#id) | ID of the segment. | | <a id="devopsadoptionsegmentid"></a>`id` | [`ID!`](#id) | ID of the segment. |
| <a id="devopsadoptionsegmentlatestsnapshot"></a>`latestSnapshot` | [`DevopsAdoptionSnapshot`](#devopsadoptionsnapshot) | The latest adoption metrics for the segment. | | <a id="devopsadoptionsegmentlatestsnapshot"></a>`latestSnapshot` | [`DevopsAdoptionSnapshot`](#devopsadoptionsnapshot) | The latest adoption metrics for the segment. |
| <a id="devopsadoptionsegmentnamespace"></a>`namespace` | [`Namespace`](#namespace) | Namespace which should be calculated. | | <a id="devopsadoptionsegmentnamespace"></a>`namespace` | [`Namespace`](#namespace) | Namespace which should be calculated. |

View file

@ -213,9 +213,6 @@ actors.
Feature.enabled?(:some_feature, group) Feature.enabled?(:some_feature, group)
``` ```
**Percentage of time** rollout is not a good idea if what you want is to make sure a feature
is always on or off to the users. In that case, **Percentage of actors** rollout is a better method.
Lastly, to verify that the feature is deemed stable in as many cases as possible, Lastly, to verify that the feature is deemed stable in as many cases as possible,
you should fully roll out the feature by enabling the flag **globally** by running: you should fully roll out the feature by enabling the flag **globally** by running:
@ -226,6 +223,15 @@ you should fully roll out the feature by enabling the flag **globally** by runni
This changes the feature flag state to be **enabled** always, which overrides the This changes the feature flag state to be **enabled** always, which overrides the
existing gates (e.g. `--group=gitlab-org`) in the above processes. existing gates (e.g. `--group=gitlab-org`) in the above processes.
##### Percentage of actors vs percentage of time rollouts
If you want to make sure a feature is always on or off for users, use a **Percentage of actors**
rollout. Avoid using percentage of _time_ rollouts in this case.
A percentage of _time_ rollout can introduce inconsistent behavior when `Feature.enabled?`
is used multiple times in the code because the feature flag value is randomized each time
`Feature.enabled?` is called on your code path.
##### Disabling feature flags ##### Disabling feature flags
To disable a feature flag that has been globally enabled you can run: To disable a feature flag that has been globally enabled you can run:

View file

@ -69,11 +69,22 @@ module API
def feature_flags def feature_flags
return [] unless unleash_app_name.present? return [] unless unleash_app_name.present?
legacy_flags = Operations::FeatureFlagScope.for_unleash_client(project, unleash_app_name) legacy_flags =
if exclude_legacy_flags?
[]
else
Operations::FeatureFlagScope.for_unleash_client(project, unleash_app_name)
end
new_version_flags = Operations::FeatureFlag.for_unleash_client(project, unleash_app_name) new_version_flags = Operations::FeatureFlag.for_unleash_client(project, unleash_app_name)
legacy_flags + new_version_flags legacy_flags + new_version_flags
end end
def exclude_legacy_flags?
Feature.enabled?(:remove_legacy_flags, project, default_enabled: :yaml) &&
Feature.disabled?(:remove_legacy_flags_override, project, default_enabled: :yaml)
end
end end
end end
end end

View file

@ -32,6 +32,10 @@ module Gitlab
end end
end end
def delete_export?
false
end
private private
def send_file def send_file

View file

@ -34330,9 +34330,6 @@ msgstr ""
msgid "Too many projects enabled. You will need to manage them via the console or the API." msgid "Too many projects enabled. You will need to manage them via the console or the API."
msgstr "" msgstr ""
msgid "TopNav|Switch to..."
msgstr ""
msgid "Topics (optional)" msgid "Topics (optional)"
msgstr "" msgstr ""

View file

@ -147,14 +147,23 @@ RSpec.describe 'Project issue boards', :js do
end end
it 'infinite scrolls list' do it 'infinite scrolls list' do
create_list(:labeled_issue, 50, project: project, labels: [planning]) create_list(:labeled_issue, 30, project: project, labels: [planning])
visit_project_board_path_without_query_limit(project, board) visit_project_board_path_without_query_limit(project, board)
page.within(find('.board:nth-child(2)')) do page.within(find('.board:nth-child(2)')) do
expect(page.find('.board-header')).to have_content('58') expect(page.find('.board-header')).to have_content('38')
expect(page).to have_selector('.board-card', count: 10)
expect(page).to have_content('Showing 10 of 38 issues')
find('.board .board-list')
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
end
expect(page).to have_selector('.board-card', count: 20) expect(page).to have_selector('.board-card', count: 20)
expect(page).to have_content('Showing 20 of 58 issues') expect(page).to have_content('Showing 20 of 38 issues')
find('.board .board-list') find('.board .board-list')
@ -162,8 +171,8 @@ RSpec.describe 'Project issue boards', :js do
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
end end
expect(page).to have_selector('.board-card', count: 40) expect(page).to have_selector('.board-card', count: 30)
expect(page).to have_content('Showing 40 of 58 issues') expect(page).to have_content('Showing 30 of 38 issues')
find('.board .board-list') find('.board .board-list')
@ -171,7 +180,7 @@ RSpec.describe 'Project issue boards', :js do
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
end end
expect(page).to have_selector('.board-card', count: 58) expect(page).to have_selector('.board-card', count: 38)
expect(page).to have_content('Showing all issues') expect(page).to have_content('Showing all issues')
end end
end end
@ -464,7 +473,7 @@ RSpec.describe 'Project issue boards', :js do
end end
it 'infinite scrolls list with label filter' do it 'infinite scrolls list with label filter' do
create_list(:labeled_issue, 50, project: project, labels: [planning, testing]) create_list(:labeled_issue, 30, project: project, labels: [planning, testing])
set_filter("label", testing.title) set_filter("label", testing.title)
click_filter_link(testing.title) click_filter_link(testing.title)
@ -475,9 +484,18 @@ RSpec.describe 'Project issue boards', :js do
wait_for_requests wait_for_requests
page.within(find('.board:nth-child(2)')) do page.within(find('.board:nth-child(2)')) do
expect(page.find('.board-header')).to have_content('51') expect(page.find('.board-header')).to have_content('31')
expect(page).to have_selector('.board-card', count: 10)
expect(page).to have_content('Showing 10 of 31 issues')
find('.board .board-list')
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
end
expect(page).to have_selector('.board-card', count: 20) expect(page).to have_selector('.board-card', count: 20)
expect(page).to have_content('Showing 20 of 51 issues') expect(page).to have_content('Showing 20 of 31 issues')
find('.board .board-list') find('.board .board-list')
@ -485,15 +503,15 @@ RSpec.describe 'Project issue boards', :js do
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
end end
expect(page).to have_selector('.board-card', count: 40) expect(page).to have_selector('.board-card', count: 30)
expect(page).to have_content('Showing 40 of 51 issues') expect(page).to have_content('Showing 30 of 31 issues')
find('.board .board-list') find('.board .board-list')
inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight") evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
end end
expect(page).to have_selector('.board-card', count: 51) expect(page).to have_selector('.board-card', count: 31)
expect(page).to have_content('Showing all issues') expect(page).to have_content('Showing all issues')
end end
end end

View file

@ -24,6 +24,10 @@ RSpec.describe FeatureFlagsFinder do
let!(:feature_flag_2) { create(:operations_feature_flag, name: 'flag-b', project: project) } let!(:feature_flag_2) { create(:operations_feature_flag, name: 'flag-b', project: project) }
let(:args) { {} } let(:args) { {} }
before do
stub_feature_flags(remove_legacy_flags: false)
end
it 'returns feature flags ordered by name' do it 'returns feature flags ordered by name' do
is_expected.to eq([feature_flag_1, feature_flag_2]) is_expected.to eq([feature_flag_1, feature_flag_2])
end end
@ -79,6 +83,16 @@ RSpec.describe FeatureFlagsFinder do
it 'returns new and legacy flags' do it 'returns new and legacy flags' do
is_expected.to eq([feature_flag_1, feature_flag_2, feature_flag_3]) is_expected.to eq([feature_flag_1, feature_flag_2, feature_flag_3])
end end
context 'when legacy flags are disabled' do
before do
stub_feature_flags(remove_legacy_flags_override: false, remove_legacy_flags: true)
end
it 'returns only new flags' do
is_expected.to eq([feature_flag_3])
end
end
end end
end end
end end

View file

@ -1,5 +1,5 @@
import { GlNavItemDropdown, GlTooltip } from '@gitlab/ui'; import { GlNavItemDropdown } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import TopNavApp from '~/nav/components/top_nav_app.vue'; import TopNavApp from '~/nav/components/top_nav_app.vue';
import TopNavDropdownMenu from '~/nav/components/top_nav_dropdown_menu.vue'; import TopNavDropdownMenu from '~/nav/components/top_nav_dropdown_menu.vue';
import { TEST_NAV_DATA } from '../mock_data'; import { TEST_NAV_DATA } from '../mock_data';
@ -7,8 +7,8 @@ import { TEST_NAV_DATA } from '../mock_data';
describe('~/nav/components/top_nav_app.vue', () => { describe('~/nav/components/top_nav_app.vue', () => {
let wrapper; let wrapper;
const createComponent = (mountFn = shallowMount) => { const createComponent = () => {
wrapper = mountFn(TopNavApp, { wrapper = shallowMount(TopNavApp, {
propsData: { propsData: {
navData: TEST_NAV_DATA, navData: TEST_NAV_DATA,
}, },
@ -17,7 +17,6 @@ describe('~/nav/components/top_nav_app.vue', () => {
const findNavItemDropdown = () => wrapper.findComponent(GlNavItemDropdown); const findNavItemDropdown = () => wrapper.findComponent(GlNavItemDropdown);
const findMenu = () => wrapper.findComponent(TopNavDropdownMenu); const findMenu = () => wrapper.findComponent(TopNavDropdownMenu);
const findTooltip = () => wrapper.findComponent(GlTooltip);
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
@ -44,25 +43,5 @@ describe('~/nav/components/top_nav_app.vue', () => {
views: TEST_NAV_DATA.views, views: TEST_NAV_DATA.views,
}); });
}); });
it('renders tooltip', () => {
expect(findTooltip().attributes()).toMatchObject({
'boundary-padding': '0',
placement: 'right',
title: TopNavApp.TOOLTIP,
});
});
});
describe('when full mounted', () => {
beforeEach(() => {
createComponent(mount);
});
it('has dropdown toggle as tooltip target', () => {
const targetFn = findTooltip().props('target');
expect(targetFn()).toBe(wrapper.find('.js-top-nav-dropdown-toggle').element);
});
}); });
}); });

View file

@ -27,22 +27,32 @@ RSpec.describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do
expect(subject.new(url: example_url, http_method: 'whatever')).not_to be_valid expect(subject.new(url: example_url, http_method: 'whatever')).not_to be_valid
end end
it 'onyl allow urls as upload urls' do it 'only allow urls as upload urls' do
expect(subject.new(url: example_url)).to be_valid expect(subject.new(url: example_url)).to be_valid
expect(subject.new(url: 'whatever')).not_to be_valid expect(subject.new(url: 'whatever')).not_to be_valid
end end
end end
describe '#execute' do describe '#execute' do
it 'removes the exported project file after the upload' do context 'when upload succeeds' do
before do
allow(strategy).to receive(:send_file) allow(strategy).to receive(:send_file)
allow(strategy).to receive(:handle_response_error) allow(strategy).to receive(:handle_response_error)
end
expect(project).to receive(:remove_exports) it 'does not remove the exported project file after the upload' do
expect(project).not_to receive(:remove_exports)
strategy.execute(user, project) strategy.execute(user, project)
end end
it 'has finished export status' do
strategy.execute(user, project)
expect(project.export_status).to eq(:finished)
end
end
context 'when upload fails' do context 'when upload fails' do
it 'stores the export error' do it 'stores the export error' do
stub_full_request(example_url, method: :post).to_return(status: [404, 'Page not found']) stub_full_request(example_url, method: :post).to_return(status: [404, 'Page not found'])

View file

@ -590,6 +590,32 @@ RSpec.describe API::Unleash do
}] }]
}]) }])
end end
it 'returns new flags when legacy flags are disabled' do
stub_feature_flags(remove_legacy_flags_override: false, remove_legacy_flags: true)
feature_flag_a = create(:operations_feature_flag, :new_version_flag, project: project,
name: 'feature_a', active: true)
strategy = create(:operations_strategy, feature_flag: feature_flag_a,
name: 'userWithId', parameters: { userIds: 'user8' })
create(:operations_scope, strategy: strategy, environment_scope: 'staging')
feature_flag_b = create(:operations_feature_flag, :legacy_flag, project: project,
name: 'feature_b', active: true)
create(:operations_feature_flag_scope, feature_flag: feature_flag_b,
active: true, strategies: [{ name: 'default', parameters: {} }], environment_scope: 'staging')
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'staging' }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['features'].sort_by {|f| f['name']}).to eq([{
'name' => 'feature_a',
'enabled' => true,
'strategies' => [{
'name' => 'userWithId',
'parameters' => { 'userIds' => 'user8' }
}]
}])
end
end end
end end
end end