From 5ecefe71c0a4530259e2e6ecc1fe9660b8fb2fe9 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 8 Sep 2022 12:02:27 +0200 Subject: [PATCH] Move blob info request to startup JS Moved the blob info request to startup JS --- .../components/blob_content_viewer.vue | 28 +++++++++-- .../repository/components/table/row.vue | 2 +- .../queries/project_info.query.graphql | 14 ++++++ .../repository}/blob_info.query.graphql | 14 ++---- app/views/projects/blob/show.html.haml | 1 + .../repository/components/lock_button.vue | 10 +++- .../components/blob_content_viewer_spec.js | 14 ++++-- .../components/blob_content_viewer_spec.js | 49 ++++++++++++++----- spec/frontend/repository/mock_data.js | 1 + 9 files changed, 100 insertions(+), 33 deletions(-) create mode 100644 app/assets/javascripts/repository/queries/project_info.query.graphql rename app/{assets/javascripts/repository/queries => graphql/queries/repository}/blob_info.query.graphql (85%) diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue index 78572f11f6f35f..902077ba3e4f0f 100644 --- a/app/assets/javascripts/repository/components/blob_content_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -13,9 +13,10 @@ import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import WebIdeLink from '~/vue_shared/components/web_ide_link.vue'; import CodeIntelligence from '~/code_navigation/components/app.vue'; import LineHighlighter from '~/blob/line_highlighter'; +import blobInfoQuery from 'shared_queries/repository/blob_info.query.graphql'; import addBlameLink from '~/blob/blob_blame_link'; +import projectInfoQuery from '../queries/project_info.query.graphql'; import getRefMixin from '../mixins/get_ref'; -import blobInfoQuery from '../queries/blob_info.query.graphql'; import userInfoQuery from '../queries/user_info.query.graphql'; import applicationInfoQuery from '../queries/application_info.query.graphql'; import { DEFAULT_BLOB_INFO, TEXT_FILE_TYPE, LFS_STORAGE, LEGACY_FILE_TYPES } from '../constants'; @@ -41,6 +42,21 @@ export default { }, }, apollo: { + projectInfo: { + query: projectInfoQuery, + variables() { + return { + projectPath: this.projectPath, + }; + }, + error() { + this.displayError(); + }, + update({ project }) { + this.pathLocks = project.pathLocks || DEFAULT_BLOB_INFO.pathLocks; + this.userPermissions = project.userPermissions; + }, + }, gitpodEnabled: { query: applicationInfoQuery, error() { @@ -121,6 +137,8 @@ export default { gitpodEnabled: DEFAULT_BLOB_INFO.gitpodEnabled, currentUser: DEFAULT_BLOB_INFO.currentUser, useFallback: false, + pathLocks: DEFAULT_BLOB_INFO.pathLocks, + userPermissions: DEFAULT_BLOB_INFO.userPermissions, }; }, computed: { @@ -163,7 +181,7 @@ export default { ); }, canLock() { - const { pushCode, downloadCode } = this.project.userPermissions; + const { pushCode, downloadCode } = this.userPermissions; const currentUsername = window.gon?.current_username; if (this.pathLockedByUser && this.pathLockedByUser.username !== currentUsername) { @@ -173,12 +191,12 @@ export default { return pushCode && downloadCode; }, pathLockedByUser() { - const pathLock = this.project?.pathLocks?.nodes.find((node) => node.path === this.path); + const pathLock = this.pathLocks?.nodes.find((node) => node.path === this.path); return pathLock ? pathLock.user : null; }, showForkSuggestion() { - const { createMergeRequestIn, forkProject } = this.project.userPermissions; + const { createMergeRequestIn, forkProject } = this.userPermissions; const { canModifyBlob } = this.blobInfo; return this.isLoggedIn && !canModifyBlob && createMergeRequestIn && forkProject; @@ -338,7 +356,7 @@ export default { :name="blobInfo.name" :replace-path="blobInfo.replacePath" :delete-path="blobInfo.webPath" - :can-push-code="project.userPermissions.pushCode" + :can-push-code="userPermissions.pushCode" :can-push-to-branch="blobInfo.canCurrentUserPushToBranch" :empty-repo="project.repository.empty" :project-path="projectPath" diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue index 49a18f40db9346..c8cd64b5311dc5 100644 --- a/app/assets/javascripts/repository/components/table/row.vue +++ b/app/assets/javascripts/repository/components/table/row.vue @@ -17,8 +17,8 @@ import { TREE_PAGE_SIZE, ROW_APPEAR_DELAY } from '~/repository/constants'; import FileIcon from '~/vue_shared/components/file_icon.vue'; import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import blobInfoQuery from 'shared_queries/repository/blob_info.query.graphql'; import getRefMixin from '../../mixins/get_ref'; -import blobInfoQuery from '../../queries/blob_info.query.graphql'; import commitQuery from '../../queries/commit.query.graphql'; export default { diff --git a/app/assets/javascripts/repository/queries/project_info.query.graphql b/app/assets/javascripts/repository/queries/project_info.query.graphql new file mode 100644 index 00000000000000..7a380d25bb1dd8 --- /dev/null +++ b/app/assets/javascripts/repository/queries/project_info.query.graphql @@ -0,0 +1,14 @@ +#import "ee_else_ce/repository/queries/path_locks.fragment.graphql" + +query getProjectInfo($projectPath: ID!) { + project(fullPath: $projectPath) { + id + userPermissions { + pushCode + downloadCode + createMergeRequestIn + forkProject + } + ...ProjectPathLocksFragment + } +} diff --git a/app/assets/javascripts/repository/queries/blob_info.query.graphql b/app/graphql/queries/repository/blob_info.query.graphql similarity index 85% rename from app/assets/javascripts/repository/queries/blob_info.query.graphql rename to app/graphql/queries/repository/blob_info.query.graphql index 45a7793e5599fb..fd463436ed4a5e 100644 --- a/app/assets/javascripts/repository/queries/blob_info.query.graphql +++ b/app/graphql/queries/repository/blob_info.query.graphql @@ -1,5 +1,3 @@ -#import "ee_else_ce/repository/queries/path_locks.fragment.graphql" - query getBlobInfo( $projectPath: ID! $filePath: String! @@ -7,17 +5,15 @@ query getBlobInfo( $shouldFetchRawText: Boolean! ) { project(fullPath: $projectPath) { - userPermissions { - pushCode - downloadCode - createMergeRequestIn - forkProject - } - ...ProjectPathLocksFragment + __typename + id repository { + __typename empty blobs(paths: [$filePath], ref: $ref) { + __typename nodes { + __typename id webPath name diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 16ecc1cc5a0549..33b2229f5d166d 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -3,6 +3,7 @@ - signatures_path = namespace_project_signatures_path(namespace_id: @project.namespace.full_path, project_id: @project.path, id: @last_commit, limit: 1) - content_for :prefetch_asset_tags do - webpack_preload_asset_tag('monaco', prefetch: true) +- add_page_startup_graphql_call('repository/blob_info', { projectPath: @project.full_path, ref: current_ref, filePath: @blob.path, shouldFetchRawText: @blob.rendered_as_text? && !@blob.rich_viewer }) .js-signature-container{ data: { 'signatures-path': signatures_path } } diff --git a/ee/app/assets/javascripts/repository/components/lock_button.vue b/ee/app/assets/javascripts/repository/components/lock_button.vue index e8132d0109c086..0f54e71218216e 100644 --- a/ee/app/assets/javascripts/repository/components/lock_button.vue +++ b/ee/app/assets/javascripts/repository/components/lock_button.vue @@ -56,6 +56,11 @@ export default { }); }, }, + watch: { + isLocked(val) { + this.locked = val; + }, + }, methods: { hideModal() { this.isModalVisible = false; @@ -68,20 +73,21 @@ export default { }, toggleLock() { this.lockLoading = true; + const locked = !this.locked; this.$apollo .mutate({ mutation: lockPathMutation, variables: { filePath: this.path, projectPath: this.projectPath, - lock: !this.locked, + lock: locked, }, }) .catch((error) => { createFlash({ message: error, captureError: true, error }); }) .finally(() => { - this.locked = !this.locked; + this.locked = locked; this.lockLoading = false; }); }, diff --git a/ee/spec/frontend/repository/components/blob_content_viewer_spec.js b/ee/spec/frontend/repository/components/blob_content_viewer_spec.js index 90b59a7a6c868a..385fa9d6f30ca6 100644 --- a/ee/spec/frontend/repository/components/blob_content_viewer_spec.js +++ b/ee/spec/frontend/repository/components/blob_content_viewer_spec.js @@ -8,7 +8,8 @@ import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import BlobButtonGroup from '~/repository/components/blob_button_group.vue'; import BlobContentViewer from '~/repository/components/blob_content_viewer.vue'; -import blobInfoQuery from '~/repository/queries/blob_info.query.graphql'; +import blobInfoQuery from 'shared_queries/repository/blob_info.query.graphql'; +import projectInfoQuery from '~/repository/queries/project_info.query.graphql'; import { isLoggedIn } from '~/lib/utils/common_utils'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import { @@ -52,14 +53,21 @@ const createComponent = async (mockData = {}) => { downloadCode, createMergeRequestIn, }, - repository: { empty, blobs: { nodes: [blob] } }, + repository: { + __typename: 'Repository', + empty, + blobs: { __typename: 'RepositoryBlobConnection', nodes: [blob] }, + }, }; mockResolver = jest.fn().mockResolvedValue({ data: { isBinary, project }, }); - const fakeApollo = createMockApollo([[blobInfoQuery, mockResolver]]); + const fakeApollo = createMockApollo([ + [blobInfoQuery, mockResolver], + [projectInfoQuery, mockResolver], + ]); wrapper = mountExtended(BlobContentViewer, { store: createMockStore(), diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js index 0f7cf4e61b20fd..6ece72c41bb424 100644 --- a/spec/frontend/repository/components/blob_content_viewer_spec.js +++ b/spec/frontend/repository/components/blob_content_viewer_spec.js @@ -17,7 +17,8 @@ import { loadViewer } from '~/repository/components/blob_viewers'; import DownloadViewer from '~/repository/components/blob_viewers/download_viewer.vue'; import EmptyViewer from '~/repository/components/blob_viewers/empty_viewer.vue'; import SourceViewer from '~/vue_shared/components/source_viewer/source_viewer.vue'; -import blobInfoQuery from '~/repository/queries/blob_info.query.graphql'; +import blobInfoQuery from 'shared_queries/repository/blob_info.query.graphql'; +import projectInfoQuery from '~/repository/queries/project_info.query.graphql'; import userInfoQuery from '~/repository/queries/user_info.query.graphql'; import applicationInfoQuery from '~/repository/queries/application_info.query.graphql'; import CodeIntelligence from '~/code_navigation/components/app.vue'; @@ -45,8 +46,9 @@ jest.mock('~/lib/utils/common_utils'); jest.mock('~/blob/line_highlighter'); let wrapper; -let mockResolver; +let blobInfoMockResolver; let userInfoMockResolver; +let projectInfoMockResolver; let applicationInfoMockResolver; const mockAxios = new MockAdapter(axios); @@ -74,22 +76,40 @@ const createComponent = async (mockData = {}, mountFn = shallowMount, mockRoute highlightJs = true, } = mockData; - const project = { + const blobInfo = { ...projectMock, + repository: { + empty, + blobs: { nodes: [blob] }, + }, + }; + + const projectInfo = { + __typename: 'Project', + id: '123', userPermissions: { pushCode, forkProject, downloadCode, createMergeRequestIn, }, - repository: { - empty, - blobs: { nodes: [blob] }, + pathLocks: { + nodes: [ + { + id: 'test', + path: 'locked_file.js', + user: { id: '123', username: 'root' }, + }, + ], }, }; - mockResolver = jest.fn().mockResolvedValue({ - data: { isBinary, project }, + projectInfoMockResolver = jest.fn().mockResolvedValue({ + data: { project: projectInfo }, + }); + + blobInfoMockResolver = jest.fn().mockResolvedValue({ + data: { isBinary, project: blobInfo }, }); userInfoMockResolver = jest.fn().mockResolvedValue({ @@ -101,8 +121,9 @@ const createComponent = async (mockData = {}, mountFn = shallowMount, mockRoute }); const fakeApollo = createMockApollo([ - [blobInfoQuery, mockResolver], + [blobInfoQuery, blobInfoMockResolver], [userInfoQuery, userInfoMockResolver], + [projectInfoQuery, projectInfoMockResolver], [applicationInfoQuery, applicationInfoMockResolver], ]); @@ -129,7 +150,7 @@ const createComponent = async (mockData = {}, mountFn = shallowMount, mockRoute // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ project, isBinary }); + wrapper.setData({ project: blobInfo, isBinary }); await waitForPromises(); }; @@ -504,14 +525,16 @@ describe('Blob content viewer component', () => { async ({ highlightJs, shouldFetchRawText }) => { await createComponent({ highlightJs }); - expect(mockResolver).toHaveBeenCalledWith(expect.objectContaining({ shouldFetchRawText })); + expect(blobInfoMockResolver).toHaveBeenCalledWith( + expect.objectContaining({ shouldFetchRawText }), + ); }, ); it('is called with originalBranch value if the prop has a value', async () => { await createComponent({ inject: { originalBranch: 'some-branch' } }); - expect(mockResolver).toHaveBeenCalledWith( + expect(blobInfoMockResolver).toHaveBeenCalledWith( expect.objectContaining({ ref: 'some-branch', }), @@ -521,7 +544,7 @@ describe('Blob content viewer component', () => { it('is called with ref value if the originalBranch prop has no value', async () => { await createComponent(); - expect(mockResolver).toHaveBeenCalledWith( + expect(blobInfoMockResolver).toHaveBeenCalledWith( expect.objectContaining({ ref: 'default-ref', }), diff --git a/spec/frontend/repository/mock_data.js b/spec/frontend/repository/mock_data.js index 4db295fe0b7ca7..cda47a5b0a5c71 100644 --- a/spec/frontend/repository/mock_data.js +++ b/spec/frontend/repository/mock_data.js @@ -1,4 +1,5 @@ export const simpleViewerMock = { + __typename: 'RepositoryBlob', id: '1', name: 'some_file.js', size: 123, -- GitLab