Commit c43c891e authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 1762728e 015c466b
......@@ -83,7 +83,7 @@ export default {
<gl-badge variant="warning">{{ __('pending deletion') }}</gl-badge>
</div>
<div v-if="isProject" class="last-updated">
<time-ago-tooltip :time="item.updatedAt" tooltip-placement="bottom" />
<time-ago-tooltip :time="item.lastActivityAt" tooltip-placement="bottom" />
</div>
</div>
</template>
......@@ -98,6 +98,9 @@ export default class GroupsStore {
updatedAt: rawGroupItem.updated_at,
pendingRemoval: rawGroupItem.marked_for_deletion,
microdata: this.showSchemaMarkup ? getGroupItemMicrodata(rawGroupItem) : {},
lastActivityAt: rawGroupItem.last_activity_at
? rawGroupItem.last_activity_at
: rawGroupItem.updated_at,
};
if (!isEmpty(rawGroupItem.compliance_management_framework)) {
......
......@@ -33,6 +33,8 @@ class GroupChildEntity < Grape::Entity
end
# Project only attributes
expose :last_activity_at, if: lambda { |instance| project? }
expose :star_count, :archived,
if: lambda { |_instance, _options| project? }
......
# frozen_string_literal: true
class RemoveForeignKeyCiRunnerNamespacesNamespaceId < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
CONSTRAINT_NAME = 'fk_rails_f9d9ed3308'
def up
with_lock_retries do
remove_foreign_key_if_exists(:ci_runner_namespaces, :namespaces, name: CONSTRAINT_NAME)
end
end
def down
add_concurrent_foreign_key :ci_runner_namespaces, :namespaces, column: :namespace_id, on_delete: :cascade, name: CONSTRAINT_NAME
end
end
34f966723cae63e831f7fc9d965cda90f1fd7bca522fc58e78a0de4b959a47a2
\ No newline at end of file
......@@ -31438,9 +31438,6 @@ ALTER TABLE ONLY merge_requests_closing_issues
ALTER TABLE ONLY merge_trains
ADD CONSTRAINT fk_rails_f90820cb08 FOREIGN KEY (pipeline_id) REFERENCES ci_pipelines(id) ON DELETE SET NULL;
ALTER TABLE ONLY ci_runner_namespaces
ADD CONSTRAINT fk_rails_f9d9ed3308 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY banned_users
ADD CONSTRAINT fk_rails_fa5bb598e5 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
......@@ -171,6 +171,7 @@ Each feature flag is defined in a separate YAML file consisting of a number of f
| `default_enabled` | yes | The default state of the feature flag that is strictly validated, with `default_enabled:` passed as an argument. |
| `introduced_by_url` | no | The URL to the Merge Request that introduced the feature flag. |
| `rollout_issue_url` | no | The URL to the Issue covering the feature flag rollout. |
| `milestone` | no | Milestone in which the feature was added. |
| `group` | no | The [group](https://about.gitlab.com/handbook/product/categories/#devops-stages) that owns the feature flag. |
NOTE:
......
<script>
import { GlButton } from '@gitlab/ui';
import { GlButton, GlModal } from '@gitlab/ui';
import createFlash from '~/flash';
import { sprintf, __ } from '~/locale';
import lockPathMutation from '~/repository/mutations/lock_path.mutation.graphql';
......@@ -8,9 +8,13 @@ export default {
i18n: {
lock: __('Lock'),
unlock: __('Unlock'),
modalTitle: __('Lock File?'),
actionPrimary: __('Okay'),
actionCancel: __('Cancel'),
},
components: {
GlButton,
GlModal,
},
props: {
name: {
......@@ -36,6 +40,7 @@ export default {
},
data() {
return {
isModalVisible: false,
lockLoading: false,
locked: this.isLocked,
};
......@@ -52,11 +57,14 @@ export default {
},
},
methods: {
onLockToggle() {
// eslint-disable-next-line no-alert
if (window.confirm(this.lockConfirmText)) {
this.toggleLock();
}
hideModal() {
this.isModalVisible = false;
},
handleModalPrimary() {
this.toggleLock();
},
showModal() {
this.isModalVisible = true;
},
toggleLock() {
this.lockLoading = true;
......@@ -82,7 +90,23 @@ export default {
</script>
<template>
<gl-button :disabled="!canLock" :loading="lockLoading" @click="onLockToggle">
{{ lockButtonTitle }}
</gl-button>
<span>
<gl-button :disabled="!canLock" :loading="lockLoading" @click="showModal">
{{ lockButtonTitle }}
</gl-button>
<gl-modal
modal-id="lock-file-modal"
:visible="isModalVisible"
:title="$options.i18n.modalTitle"
:action-primary="{ text: $options.i18n.actionPrimary }"
:action-cancel="{ text: $options.i18n.actionCancel }"
@primary="handleModalPrimary"
@hide="hideModal"
>
<p>
{{ lockConfirmText }}
</p>
</gl-modal>
</span>
</template>
import { GlButton } from '@gitlab/ui';
import { GlButton, GlModal } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
......@@ -39,18 +39,17 @@ describe('LockButton component', () => {
});
describe('lock button', () => {
let confirmSpy;
let lockMutationMock;
const mockEvent = { preventDefault: jest.fn() };
const findLockButton = () => wrapper.find(GlButton);
const findModal = () => wrapper.findComponent(GlModal);
const clickSubmit = () => findModal().vm.$emit('primary', mockEvent);
const clickHide = () => findModal().vm.$emit('hide', mockEvent);
beforeEach(() => {
confirmSpy = jest.spyOn(window, 'confirm');
confirmSpy.mockImplementation(jest.fn());
lockMutationMock = jest.fn();
});
afterEach(() => confirmSpy.mockRestore());
it('disables the lock button if canLock is set to false', () => {
createComponent({ canLock: false });
......@@ -78,18 +77,24 @@ describe('LockButton component', () => {
expect(findLockButton().props('loading')).toBe(true);
});
it('displays a confirm dialog when the lock button is clicked', () => {
it('displays a confirm modal when the lock button is clicked', () => {
createComponent();
findLockButton().vm.$emit('click');
expect(findModal().text()).toBe('Are you sure you want to lock some_file.js?');
});
expect(confirmSpy).toHaveBeenCalledWith('Are you sure you want to lock some_file.js?');
it('should hide the confirm modal when a hide action is triggered', () => {
createComponent();
findLockButton().vm.$emit('click');
expect(wrapper.vm.isModalVisible).toBe(true);
clickHide();
expect(wrapper.vm.isModalVisible).toBe(false);
});
it('executes a lock mutation once lock is confirmed', () => {
confirmSpy.mockReturnValue(true);
it('executes a lock mutation once lock is confirmed', async () => {
createComponent({}, lockMutationMock);
findLockButton().vm.$emit('click');
clickSubmit();
expect(lockMutationMock).toHaveBeenCalledWith({
filePath: 'some/path',
lock: true,
......@@ -98,7 +103,6 @@ describe('LockButton component', () => {
});
it('does not execute a lock mutation if lock not confirmed', () => {
confirmSpy.mockReturnValue(false);
createComponent({}, lockMutationMock);
findLockButton().vm.$emit('click');
......
dast_site_profiles_pipelines:
- table: ci_pipelines
column: ci_pipeline_id
on_delete: async_delete
vulnerability_feedback:
- table: ci_pipelines
column: pipeline_id
on_delete: async_nullify
ci_pipeline_chat_data:
- table: chat_names
column: chat_name_id
......
......@@ -21426,6 +21426,9 @@ msgstr ""
msgid "Lock %{issuableDisplayName}"
msgstr ""
msgid "Lock File?"
msgstr ""
msgid "Lock memberships to LDAP synchronization"
msgstr ""
......
......@@ -100,6 +100,7 @@ describe('GroupItemComponent', () => {
wrapper.destroy();
group.type = 'project';
group.lastActivityAt = '2017-04-09T18:40:39.101Z';
wrapper = createComponent({ group });
expect(wrapper.vm.isGroup).toBe(false);
......
......@@ -38,6 +38,7 @@ describe('ItemStats', () => {
...mockParentGroupItem,
type: ITEM_TYPE.PROJECT,
starCount: 4,
lastActivityAt: '2017-04-09T18:40:39.101Z',
};
createComponent({ item });
......
......@@ -6,4 +6,10 @@ RSpec.describe Ci::RunnerNamespace do
it_behaves_like 'includes Limitable concern' do
subject { build(:ci_runner_namespace, group: create(:group, :nested), runner: create(:ci_runner, :group)) }
end
it_behaves_like 'cleanup by a loose foreign key' do
let!(:model) { create(:ci_runner_namespace) }
let!(:parent) { model.namespace }
end
end
......@@ -62,6 +62,10 @@ RSpec.describe GroupChildEntity do
expect(json[:edit_path]).to eq(edit_project_path(object))
end
it 'includes the last activity at' do
expect(json[:last_activity_at]).to be_present
end
it_behaves_like 'group child json'
end
......
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