summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2019-06-25 15:26:55 +0100
committerPhil Hughes <me@iamphill.com>2019-06-25 15:26:55 +0100
commit89278cffdf50d1ddeb146d7ad924f213f69fa102 (patch)
tree0532657cd40dd4760a9a05e9b8fc7e71af7e736b
parentf53b05cfde5aeac73a7c81a2fe951b5642338ae0 (diff)
downloadgitlab-ce-89278cffdf50d1ddeb146d7ad924f213f69fa102.tar.gz
Fetch commit message with log_tree endpoint
-rw-r--r--app/assets/javascripts/repository/components/breadcrumbs.vue2
-rw-r--r--app/assets/javascripts/repository/components/table/index.vue2
-rw-r--r--app/assets/javascripts/repository/components/table/row.vue49
-rw-r--r--app/assets/javascripts/repository/graphql.js17
-rw-r--r--app/assets/javascripts/repository/index.js1
-rw-r--r--app/assets/javascripts/repository/log_tree.js64
-rw-r--r--app/assets/javascripts/repository/queries/getCommit.query.graphql10
-rw-r--r--app/assets/javascripts/repository/queries/getCommits.query.graphql10
-rw-r--r--app/assets/javascripts/repository/queries/getFiles.query.graphql1
-rw-r--r--app/controllers/projects/refs_controller.rb1
-rw-r--r--spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap14
-rw-r--r--spec/frontend/repository/components/table/row_spec.js2
-rw-r--r--spec/frontend/repository/log_tree_spec.js129
13 files changed, 293 insertions, 9 deletions
diff --git a/app/assets/javascripts/repository/components/breadcrumbs.vue b/app/assets/javascripts/repository/components/breadcrumbs.vue
index 0d4d431855c..67963dc1923 100644
--- a/app/assets/javascripts/repository/components/breadcrumbs.vue
+++ b/app/assets/javascripts/repository/components/breadcrumbs.vue
@@ -36,7 +36,7 @@ export default {
to: `/tree/${this.ref}${path}`,
});
},
- [{ name: this.projectShortPath, path: '/', to: `/tree/${this.ref}` }],
+ [{ name: this.projectShortPath, path: '/', to: `/tree/${this.ref}/` }],
);
},
},
diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue
index 891e3fe9d16..1e66ccbfa29 100644
--- a/app/assets/javascripts/repository/components/table/index.vue
+++ b/app/assets/javascripts/repository/components/table/index.vue
@@ -131,7 +131,9 @@ export default {
v-for="entry in val"
:id="entry.id"
:key="`${entry.flatPath}-${entry.id}`"
+ :project-path="projectPath"
:current-path="path"
+ :name="entry.name"
:path="entry.flatPath"
:type="entry.type"
:url="entry.webUrl"
diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue
index 4519f82fc93..c31e7fa71a2 100644
--- a/app/assets/javascripts/repository/components/table/row.vue
+++ b/app/assets/javascripts/repository/components/table/row.vue
@@ -1,12 +1,30 @@
<script>
-import { GlBadge } from '@gitlab/ui';
+import { GlBadge, GlLink, GlSkeletonLoading } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
+import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { getIconName } from '../../utils/icon';
import getRefMixin from '../../mixins/get_ref';
+import getCommit from '../../queries/getCommit.query.graphql';
export default {
components: {
GlBadge,
+ GlLink,
+ GlSkeletonLoading,
+ TimeagoTooltip,
+ },
+ apollo: {
+ commit: {
+ query: getCommit,
+ variables() {
+ return {
+ fileName: this.name,
+ type: this.type,
+ path: this.currentPath,
+ projectPath: this.projectPath,
+ };
+ },
+ },
},
mixins: [getRefMixin],
props: {
@@ -14,10 +32,18 @@ export default {
type: String,
required: true,
},
+ projectPath: {
+ type: String,
+ required: true,
+ },
currentPath: {
type: String,
required: true,
},
+ name: {
+ type: String,
+ required: true,
+ },
path: {
type: String,
required: true,
@@ -37,6 +63,11 @@ export default {
default: null,
},
},
+ data() {
+ return {
+ commit: null,
+ };
+ },
computed: {
routerLinkTo() {
return this.isFolder ? { path: `/tree/${this.ref}/${this.path}` } : null;
@@ -73,7 +104,7 @@ export default {
</script>
<template>
- <tr v-once :class="`file_${id}`" class="tree-item" @click="openRow">
+ <tr :class="`file_${id}`" class="tree-item" @click="openRow">
<td class="tree-item-file-name">
<i :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i>
<component :is="linkComponent" :to="routerLinkTo" :href="url" class="str-truncated">
@@ -83,10 +114,18 @@ export default {
LFS
</gl-badge>
<template v-if="isSubmodule">
- @ <a href="#" class="commit-sha">{{ shortSha }}</a>
+ @ <gl-link href="#" class="commit-sha">{{ shortSha }}</gl-link>
</template>
</td>
- <td class="d-none d-sm-table-cell tree-commit"></td>
- <td class="tree-time-ago text-right"></td>
+ <td class="d-none d-sm-table-cell tree-commit">
+ <gl-link v-if="commit" :href="commit.commitPath" class="str-truncated-100 tree-commit-link">
+ {{ commit.message }}
+ </gl-link>
+ <gl-skeleton-loading v-else :lines="1" class="h-auto" />
+ </td>
+ <td class="tree-time-ago text-right">
+ <timeago-tooltip v-if="commit" :time="commit.committedDate" tooltip-placement="bottom" />
+ <gl-skeleton-loading v-else :lines="1" class="ml-auto h-auto w-50" />
+ </td>
</tr>
</template>
diff --git a/app/assets/javascripts/repository/graphql.js b/app/assets/javascripts/repository/graphql.js
index ef147ec15cb..6cb253c8169 100644
--- a/app/assets/javascripts/repository/graphql.js
+++ b/app/assets/javascripts/repository/graphql.js
@@ -3,6 +3,7 @@ import VueApollo from 'vue-apollo';
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import createDefaultClient from '~/lib/graphql';
import introspectionQueryResultData from './fragmentTypes.json';
+import { fetchLogsTree } from './log_tree';
Vue.use(VueApollo);
@@ -13,7 +14,21 @@ const fragmentMatcher = new IntrospectionFragmentMatcher({
});
const defaultClient = createDefaultClient(
- {},
+ {
+ Query: {
+ commit(_, { path, fileName, type }) {
+ return new Promise(resolve => {
+ fetchLogsTree(defaultClient, path, '0', {
+ resolve,
+ entry: {
+ name: fileName,
+ type,
+ },
+ });
+ });
+ },
+ },
+ },
{
cacheConfig: {
fragmentMatcher,
diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js
index d9216e88676..6280977b05b 100644
--- a/app/assets/javascripts/repository/index.js
+++ b/app/assets/javascripts/repository/index.js
@@ -16,6 +16,7 @@ export default function setupVueRepositoryList() {
projectPath,
projectShortPath,
ref,
+ commits: [],
},
});
diff --git a/app/assets/javascripts/repository/log_tree.js b/app/assets/javascripts/repository/log_tree.js
new file mode 100644
index 00000000000..2c19aca2397
--- /dev/null
+++ b/app/assets/javascripts/repository/log_tree.js
@@ -0,0 +1,64 @@
+import axios from '~/lib/utils/axios_utils';
+import getCommits from './queries/getCommits.query.graphql';
+import getProjectPath from './queries/getProjectPath.query.graphql';
+import getRef from './queries/getRef.query.graphql';
+
+let fetchpromise;
+let resolvers = [];
+
+export function normalizeData(data) {
+ return data.map(d => ({
+ sha: d.commit.id,
+ message: d.commit.message,
+ committedDate: d.commit.committed_date,
+ commitPath: d.commit_path,
+ fileName: d.file_name,
+ type: d.type,
+ __typename: 'LogTreeCommit',
+ }));
+}
+
+export function resolveCommit(commits, { resolve, entry }) {
+ const commit = commits.find(c => c.fileName === entry.name && c.type === entry.type);
+
+ if (commit) {
+ resolve(commit);
+ }
+}
+
+export function fetchLogsTree(client, path, offset, resolver = null) {
+ if (resolver) {
+ resolvers.push(resolver);
+ }
+
+ if (fetchpromise) return fetchpromise;
+
+ const { projectPath } = client.readQuery({ query: getProjectPath });
+ const { ref } = client.readQuery({ query: getRef });
+
+ fetchpromise = axios
+ .get(`${gon.gitlab_url}/${projectPath}/refs/${ref}/logs_tree${path ? `/${path}` : ''}`, {
+ params: { format: 'json', offset },
+ })
+ .then(({ data, headers }) => {
+ const headerLogsOffset = headers['more-logs-offset'];
+ const { commits } = client.readQuery({ query: getCommits });
+ const newCommitData = [...commits, ...normalizeData(data)];
+ client.writeQuery({
+ query: getCommits,
+ data: { commits: newCommitData },
+ });
+
+ resolvers.forEach(r => resolveCommit(newCommitData, r));
+
+ fetchpromise = null;
+
+ if (headerLogsOffset) {
+ fetchLogsTree(client, path, headerLogsOffset);
+ } else {
+ resolvers = [];
+ }
+ });
+
+ return fetchpromise;
+}
diff --git a/app/assets/javascripts/repository/queries/getCommit.query.graphql b/app/assets/javascripts/repository/queries/getCommit.query.graphql
new file mode 100644
index 00000000000..e2a2d831e47
--- /dev/null
+++ b/app/assets/javascripts/repository/queries/getCommit.query.graphql
@@ -0,0 +1,10 @@
+query getCommit($fileName: String!, $type: String!, $path: String!) {
+ commit(path: $path, fileName: $fileName, type: $type) @client {
+ sha
+ message
+ committedDate
+ commitPath
+ fileName
+ type
+ }
+}
diff --git a/app/assets/javascripts/repository/queries/getCommits.query.graphql b/app/assets/javascripts/repository/queries/getCommits.query.graphql
new file mode 100644
index 00000000000..df9e67cc440
--- /dev/null
+++ b/app/assets/javascripts/repository/queries/getCommits.query.graphql
@@ -0,0 +1,10 @@
+query getCommits {
+ commits @client {
+ sha
+ message
+ committedDate
+ commitPath
+ fileName
+ type
+ }
+}
diff --git a/app/assets/javascripts/repository/queries/getFiles.query.graphql b/app/assets/javascripts/repository/queries/getFiles.query.graphql
index ef924fde556..4c24fc4087f 100644
--- a/app/assets/javascripts/repository/queries/getFiles.query.graphql
+++ b/app/assets/javascripts/repository/queries/getFiles.query.graphql
@@ -1,5 +1,6 @@
fragment TreeEntry on Entry {
id
+ name
flatPath
type
}
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index b3447812ef2..b4ca9074ca9 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -55,6 +55,7 @@ class Projects::RefsController < Projects::ApplicationController
format.html { render_404 }
format.json do
response.headers["More-Logs-Url"] = @more_log_url if summary.more?
+ response.headers["More-Logs-Offset"] = summary.next_offset if summary.more?
render json: @logs
end
diff --git a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
index 1f06d693411..d55dc553031 100644
--- a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
+++ b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
@@ -29,10 +29,20 @@ exports[`Repository table row component renders table row 1`] = `
<td
class="d-none d-sm-table-cell tree-commit"
- />
+ >
+ <glskeletonloading-stub
+ class="h-auto"
+ lines="1"
+ />
+ </td>
<td
class="tree-time-ago text-right"
- />
+ >
+ <glskeletonloading-stub
+ class="ml-auto h-auto w-50"
+ lines="1"
+ />
+ </td>
</tr>
`;
diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js
index 5a345ddeacd..c566057ad3f 100644
--- a/spec/frontend/repository/components/table/row_spec.js
+++ b/spec/frontend/repository/components/table/row_spec.js
@@ -16,6 +16,8 @@ function factory(propsData = {}) {
vm = shallowMount(TableRow, {
propsData: {
...propsData,
+ name: propsData.path,
+ projectPath: 'gitlab-org/gitlab-ce',
url: `https://test.com`,
},
mocks: {
diff --git a/spec/frontend/repository/log_tree_spec.js b/spec/frontend/repository/log_tree_spec.js
new file mode 100644
index 00000000000..a9499f7c61b
--- /dev/null
+++ b/spec/frontend/repository/log_tree_spec.js
@@ -0,0 +1,129 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import { normalizeData, resolveCommit, fetchLogsTree } from '~/repository/log_tree';
+
+const mockData = [
+ {
+ commit: {
+ id: '123',
+ message: 'testing message',
+ committed_date: '2019-01-01',
+ },
+ commit_path: `https://test.com`,
+ file_name: 'index.js',
+ type: 'blob',
+ },
+];
+
+describe('normalizeData', () => {
+ it('normalizes data into LogTreeCommit object', () => {
+ expect(normalizeData(mockData)).toEqual([
+ {
+ sha: '123',
+ message: 'testing message',
+ committedDate: '2019-01-01',
+ commitPath: 'https://test.com',
+ fileName: 'index.js',
+ type: 'blob',
+ __typename: 'LogTreeCommit',
+ },
+ ]);
+ });
+});
+
+describe('resolveCommit', () => {
+ it('calls resolve when commit found', () => {
+ const resolver = {
+ entry: { name: 'index.js', type: 'blob' },
+ resolve: jest.fn(),
+ };
+ const commits = [{ fileName: 'index.js', type: 'blob' }];
+
+ resolveCommit(commits, resolver);
+
+ expect(resolver.resolve).toHaveBeenCalledWith({ fileName: 'index.js', type: 'blob' });
+ });
+});
+
+describe('fetchLogsTree', () => {
+ let mock;
+ let client;
+ let resolver;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+
+ mock.onGet(/(.*)/).reply(200, mockData, {});
+
+ jest.spyOn(axios, 'get');
+
+ global.gon = { gitlab_url: 'https://test.com' };
+
+ client = {
+ readQuery: () => ({
+ projectPath: 'gitlab-org/gitlab-ce',
+ ref: 'master',
+ commits: [],
+ }),
+ writeQuery: jest.fn(),
+ };
+
+ resolver = {
+ entry: { name: 'index.js', type: 'blob' },
+ resolve: jest.fn(),
+ };
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('calls axios get', () =>
+ fetchLogsTree(client, '', '0', resolver).then(() => {
+ expect(axios.get).toHaveBeenCalledWith(
+ 'https://test.com/gitlab-org/gitlab-ce/refs/master/logs_tree',
+ { params: { format: 'json', offset: '0' } },
+ );
+ }));
+
+ it('calls axios get once', () =>
+ Promise.all([
+ fetchLogsTree(client, '', '0', resolver),
+ fetchLogsTree(client, '', '0', resolver),
+ ]).then(() => {
+ expect(axios.get.mock.calls.length).toEqual(1);
+ }));
+
+ it('calls entry resolver', () =>
+ fetchLogsTree(client, '', '0', resolver).then(() => {
+ expect(resolver.resolve).toHaveBeenCalledWith({
+ __typename: 'LogTreeCommit',
+ commitPath: 'https://test.com',
+ committedDate: '2019-01-01',
+ fileName: 'index.js',
+ message: 'testing message',
+ sha: '123',
+ type: 'blob',
+ });
+ }));
+
+ it('writes query to client', () =>
+ fetchLogsTree(client, '', '0', resolver).then(() => {
+ expect(client.writeQuery).toHaveBeenCalledWith({
+ query: expect.anything(),
+ data: {
+ commits: [
+ {
+ __typename: 'LogTreeCommit',
+ commitPath: 'https://test.com',
+ committedDate: '2019-01-01',
+ fileName: 'index.js',
+ message: 'testing message',
+ sha: '123',
+ type: 'blob',
+ },
+ ],
+ },
+ });
+ }));
+});