Commit 3bbf1bb1 authored by Brandon Labuschagne's avatar Brandon Labuschagne

Merge branch '341016-lock-button-bug-fix' into 'master'

Blob refactor: Disable lock button when locked by another user

See merge request gitlab-org/gitlab!76229
parents 7714f397 9fcb7542
...@@ -157,11 +157,18 @@ export default { ...@@ -157,11 +157,18 @@ export default {
}, },
canLock() { canLock() {
const { pushCode, downloadCode } = this.project.userPermissions; const { pushCode, downloadCode } = this.project.userPermissions;
const currentUsername = window.gon?.current_username;
if (this.pathLockedByUser && this.pathLockedByUser.username !== currentUsername) {
return false;
}
return pushCode && downloadCode; return pushCode && downloadCode;
}, },
isLocked() { pathLockedByUser() {
return this.project.pathLocks.nodes.some((node) => node.path === this.path); const pathLock = this.project.pathLocks.nodes.find((node) => node.path === this.path);
return pathLock ? pathLock.user : null;
}, },
showForkSuggestion() { showForkSuggestion() {
const { createMergeRequestIn, forkProject } = this.project.userPermissions; const { createMergeRequestIn, forkProject } = this.project.userPermissions;
...@@ -270,7 +277,7 @@ export default { ...@@ -270,7 +277,7 @@ export default {
:can-push-to-branch="blobInfo.canCurrentUserPushToBranch" :can-push-to-branch="blobInfo.canCurrentUserPushToBranch"
:empty-repo="project.repository.empty" :empty-repo="project.repository.empty"
:project-path="projectPath" :project-path="projectPath"
:is-locked="isLocked" :is-locked="Boolean(pathLockedByUser)"
:can-lock="canLock" :can-lock="canLock"
/> />
</template> </template>
......
...@@ -11,6 +11,10 @@ query getBlobInfo($projectPath: ID!, $filePath: String!, $ref: String!) { ...@@ -11,6 +11,10 @@ query getBlobInfo($projectPath: ID!, $filePath: String!, $ref: String!) {
nodes { nodes {
id id
path path
user {
id
username
}
} }
} }
repository { repository {
......
...@@ -38,6 +38,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -38,6 +38,7 @@ class ProjectsController < Projects::ApplicationController
push_frontend_feature_flag(:highlight_js, @project, default_enabled: :yaml) push_frontend_feature_flag(:highlight_js, @project, default_enabled: :yaml)
push_frontend_feature_flag(:increase_page_size_exponentially, @project, default_enabled: :yaml) push_frontend_feature_flag(:increase_page_size_exponentially, @project, default_enabled: :yaml)
push_frontend_feature_flag(:new_dir_modal, @project, default_enabled: :yaml) push_frontend_feature_flag(:new_dir_modal, @project, default_enabled: :yaml)
push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks)
end end
layout :determine_layout layout :determine_layout
......
...@@ -37,11 +37,12 @@ export default { ...@@ -37,11 +37,12 @@ export default {
data() { data() {
return { return {
lockLoading: false, lockLoading: false,
locked: this.isLocked,
}; };
}, },
computed: { computed: {
lockButtonTitle() { lockButtonTitle() {
return this.isLocked ? this.$options.i18n.unlock : this.$options.i18n.lock; return this.locked ? this.$options.i18n.unlock : this.$options.i18n.lock;
}, },
lockConfirmText() { lockConfirmText() {
return sprintf(__('Are you sure you want to %{action} %{name}?'), { return sprintf(__('Are you sure you want to %{action} %{name}?'), {
...@@ -65,13 +66,14 @@ export default { ...@@ -65,13 +66,14 @@ export default {
variables: { variables: {
filePath: this.path, filePath: this.path,
projectPath: this.projectPath, projectPath: this.projectPath,
lock: !this.isLocked, lock: !this.locked,
}, },
}) })
.catch((error) => { .catch((error) => {
createFlash({ message: error, captureError: true, error }); createFlash({ message: error, captureError: true, error });
}) })
.finally(() => { .finally(() => {
this.locked = !this.locked;
this.lockLoading = false; this.lockLoading = false;
}); });
}, },
...@@ -80,7 +82,7 @@ export default { ...@@ -80,7 +82,7 @@ export default {
</script> </script>
<template> <template>
<gl-button v-if="canLock" :loading="lockLoading" @click="onLockToggle"> <gl-button :disabled="!canLock" :loading="lockLoading" @click="onLockToggle">
{{ lockButtonTitle }} {{ lockButtonTitle }}
</gl-button> </gl-button>
</template> </template>
...@@ -51,10 +51,10 @@ describe('LockButton component', () => { ...@@ -51,10 +51,10 @@ describe('LockButton component', () => {
afterEach(() => confirmSpy.mockRestore()); afterEach(() => confirmSpy.mockRestore());
it('does not render if canLock is set to false', () => { it('disables the lock button if canLock is set to false', () => {
createComponent({ canLock: false }); createComponent({ canLock: false });
expect(findLockButton().exists()).toBe(false); expect(findLockButton().props('disabled')).toBe(true);
}); });
it.each` it.each`
......
...@@ -318,8 +318,14 @@ describe('Blob content viewer component', () => { ...@@ -318,8 +318,14 @@ describe('Blob content viewer component', () => {
repository: { empty }, repository: { empty },
} = projectMock; } = projectMock;
afterEach(() => {
delete gon.current_user_id;
delete gon.current_username;
});
it('renders component', async () => { it('renders component', async () => {
window.gon.current_user_id = 1; window.gon.current_user_id = 1;
window.gon.current_username = 'root';
await createComponent({ pushCode, downloadCode, empty }, mount); await createComponent({ pushCode, downloadCode, empty }, mount);
...@@ -330,28 +336,34 @@ describe('Blob content viewer component', () => { ...@@ -330,28 +336,34 @@ describe('Blob content viewer component', () => {
deletePath: webPath, deletePath: webPath,
canPushCode: pushCode, canPushCode: pushCode,
canLock: true, canLock: true,
isLocked: false, isLocked: true,
emptyRepo: empty, emptyRepo: empty,
}); });
}); });
it.each` it.each`
canPushCode | canDownloadCode | canLock canPushCode | canDownloadCode | username | canLock
${true} | ${true} | ${true} ${true} | ${true} | ${'root'} | ${true}
${false} | ${true} | ${false} ${false} | ${true} | ${'root'} | ${false}
${true} | ${false} | ${false} ${true} | ${false} | ${'root'} | ${false}
`('passes the correct lock states', async ({ canPushCode, canDownloadCode, canLock }) => { ${true} | ${true} | ${'peter'} | ${false}
await createComponent( `(
{ 'passes the correct lock states',
pushCode: canPushCode, async ({ canPushCode, canDownloadCode, username, canLock }) => {
downloadCode: canDownloadCode, gon.current_username = username;
empty,
}, await createComponent(
mount, {
); pushCode: canPushCode,
downloadCode: canDownloadCode,
empty,
},
mount,
);
expect(findBlobButtonGroup().props('canLock')).toBe(canLock); expect(findBlobButtonGroup().props('canLock')).toBe(canLock);
}); },
);
it('does not render if not logged in', async () => { it('does not render if not logged in', async () => {
isLoggedIn.mockReturnValueOnce(false); isLoggedIn.mockReturnValueOnce(false);
......
...@@ -47,7 +47,13 @@ export const projectMock = { ...@@ -47,7 +47,13 @@ export const projectMock = {
id: '1234', id: '1234',
userPermissions: userPermissionsMock, userPermissions: userPermissionsMock,
pathLocks: { pathLocks: {
nodes: [], nodes: [
{
id: 'test',
path: simpleViewerMock.path,
user: { id: '123', username: 'root' },
},
],
}, },
repository: { repository: {
empty: false, empty: false,
......
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