Commit 47a66706 authored by Tim Zallmann's avatar Tim Zallmann

First version of Startup Calls

Basic test setup for REST calls to start early
Integrated Startup REST Calls into Repository View

Lint fixes for startup_js
Spec Fix and suggested optimizations for Startup.js

Needs to be a var for Startup.js
Spec setup for startup calls
parent 7cae7a47
...@@ -29,6 +29,15 @@ const defaultClient = createDefaultClient( ...@@ -29,6 +29,15 @@ const defaultClient = createDefaultClient(
}); });
}, },
readme(_, { url }) { readme(_, { url }) {
// Unfortunately hacky way of URL matching
const startupCall =
gl.startup_calls[`${url.replace(gon.gitlab_url, '')}?viewer=rich&format=json`];
if (startupCall?.fetchCall) {
return startupCall.fetchCall
.then(response => response.json())
.then(data => ({ ...data, __typename: 'ReadmeFile' }));
}
return axios return axios
.get(url, { params: { viewer: 'rich', format: 'json' } }) .get(url, { params: { viewer: 'rich', format: 'json' } })
.then(({ data }) => ({ ...data, __typename: 'ReadmeFile' })); .then(({ data }) => ({ ...data, __typename: 'ReadmeFile' }));
......
...@@ -25,34 +25,49 @@ export function fetchLogsTree(client, path, offset, resolver = null) { ...@@ -25,34 +25,49 @@ export function fetchLogsTree(client, path, offset, resolver = null) {
const { projectPath } = client.readQuery({ query: getProjectPath }); const { projectPath } = client.readQuery({ query: getProjectPath });
const { escapedRef } = client.readQuery({ query: getRef }); const { escapedRef } = client.readQuery({ query: getRef });
fetchpromise = axios const apiUrl = `${
.get( gon.relative_url_root
`${gon.relative_url_root}/${projectPath}/-/refs/${escapedRef}/logs_tree/${encodeURIComponent( }/${projectPath}/-/refs/${escapedRef}/logs_tree/${encodeURIComponent(path.replace(/^\//, ''))}`;
path.replace(/^\//, ''),
)}`,
{
params: { format: 'json', offset },
},
)
.then(({ data, headers }) => {
const headerLogsOffset = headers['more-logs-offset'];
const { commits } = client.readQuery({ query: getCommits });
const newCommitData = [...commits, ...normalizeData(data, path)];
client.writeQuery({
query: getCommits,
data: { commits: newCommitData },
});
resolvers.forEach(r => resolveCommit(newCommitData, path, r)); const parseLogsTreeResult = (data, headers) => {
const headerLogsOffset = headers['more-logs-offset'];
const { commits } = client.readQuery({ query: getCommits });
const newCommitData = [...commits, ...normalizeData(data, path)];
client.writeQuery({
query: getCommits,
data: { commits: newCommitData },
});
fetchpromise = null; resolvers.forEach(r => resolveCommit(newCommitData, path, r));
if (headerLogsOffset) { fetchpromise = null;
fetchLogsTree(client, path, headerLogsOffset);
} else { if (headerLogsOffset) {
resolvers = []; fetchLogsTree(client, path, headerLogsOffset);
} } else {
}); resolvers = [];
}
};
// Checking if the startup call was already fired (Hacky URL Setup right now)
if (offset === '0' && gl?.startup_calls[`${apiUrl}?format=json&offset=0`]) {
fetchpromise = gl.startup_calls[`${apiUrl}?format=json&offset=0`].fetchCall;
let headers;
return fetchpromise
.then(response => {
headers = response.headers;
return response.json();
})
.then(data => {
parseLogsTreeResult(data, headers);
});
}
fetchpromise = axios
.get(apiUrl, {
params: { format: 'json', offset },
})
.then(({ data, headers }) => parseLogsTreeResult(data, headers));
return fetchpromise; return fetchpromise;
} }
...@@ -335,6 +335,15 @@ module ApplicationHelper ...@@ -335,6 +335,15 @@ module ApplicationHelper
} }
end end
def page_startup_api_calls
@api_startup_calls
end
def add_page_startup_api_call(api_url)
@api_startup_calls ||= {}
@api_startup_calls[api_url] = ""
end
def autocomplete_data_sources(object, noteable_type) def autocomplete_data_sources(object, noteable_type)
return {} unless object && noteable_type return {} unless object && noteable_type
......
...@@ -25,6 +25,9 @@ ...@@ -25,6 +25,9 @@
%meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' } %meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }
- if page_startup_api_calls
= render 'layouts/startup_js'
-# Open Graph - http://ogp.me/ -# Open Graph - http://ogp.me/
%meta{ property: 'og:type', content: "object" } %meta{ property: 'og:type', content: "object" }
%meta{ property: 'og:site_name', content: site_name } %meta{ property: 'og:site_name', content: site_name }
......
- if page_startup_api_calls
= javascript_tag nonce: true do
:plain
var gl = window.gl || {};
gl.startup_calls = #{page_startup_api_calls.to_json};
if (gl.startup_calls && window.fetch) {
Object.keys(gl.startup_calls).forEach(apiCall => {
gl.startup_calls[apiCall] = {
fetchCall: fetch(apiCall)
};
});
}
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
- project = local_assigns.fetch(:project) { @project } - project = local_assigns.fetch(:project) { @project }
- content_url = local_assigns.fetch(:content_url) { @tree.readme ? project_blob_path(@project, tree_join(@ref, @tree.readme.path)) : project_tree_path(@project, @ref) } - content_url = local_assigns.fetch(:content_url) { @tree.readme ? project_blob_path(@project, tree_join(@ref, @tree.readme.path)) : project_tree_path(@project, @ref) }
- show_auto_devops_callout = show_auto_devops_callout?(@project) - show_auto_devops_callout = show_auto_devops_callout?(@project)
- add_page_startup_api_call "#{project_path(@project)}/-/refs/#{ref}/logs_tree/#{@path}?format=json&offset=0"
- if @tree.readme
- add_page_startup_api_call "#{project_blob_path(@project, tree_join(@ref, @tree.readme.path))}?viewer=rich&format=json"
#tree-holder.tree-holder.clearfix #tree-holder.tree-holder.clearfix
.nav-block .nav-block
......
...@@ -2,6 +2,10 @@ import MockAdapter from 'axios-mock-adapter'; ...@@ -2,6 +2,10 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { resolveCommit, fetchLogsTree } from '~/repository/log_tree'; import { resolveCommit, fetchLogsTree } from '~/repository/log_tree';
window.gl = window.gl || {
startup_calls: {},
};
const mockData = [ const mockData = [
{ {
commit: { commit: {
...@@ -15,6 +19,17 @@ const mockData = [ ...@@ -15,6 +19,17 @@ const mockData = [
}, },
]; ];
const expectedObject = {
__typename: 'LogTreeCommit',
commitPath: 'https://test.com',
committedDate: '2019-01-01',
fileName: 'index.js',
filePath: '/index.js',
message: 'testing message',
sha: '123',
type: 'blob',
};
describe('resolveCommit', () => { describe('resolveCommit', () => {
it('calls resolve when commit found', () => { it('calls resolve when commit found', () => {
const resolver = { const resolver = {
...@@ -84,20 +99,34 @@ describe('fetchLogsTree', () => { ...@@ -84,20 +99,34 @@ describe('fetchLogsTree', () => {
expect(axios.get.mock.calls.length).toEqual(1); expect(axios.get.mock.calls.length).toEqual(1);
})); }));
describe('using startupCall', () => {
const responseMock = {
status: 200,
headers: {},
json: () => new Promise(resolve => resolve(mockData)),
};
afterAll(() => {
window.gl.startup_calls = {};
});
it('uses startup call fetch when it is not done yet', () => {
window.gl.startup_calls = {
'/gitlab-org/gitlab-foss/-/refs/master/logs_tree/?format=json&offset=0': {
fetchCall: new Promise(resolve => resolve(responseMock)),
},
};
return fetchLogsTree(client, '', '0', resolver).then(() => {
expect(resolver.resolve).toHaveBeenCalledWith(expect.objectContaining(expectedObject));
expect(axios.get.mock.calls.length).toEqual(0);
});
});
});
it('calls entry resolver', () => it('calls entry resolver', () =>
fetchLogsTree(client, '', '0', resolver).then(() => { fetchLogsTree(client, '', '0', resolver).then(() => {
expect(resolver.resolve).toHaveBeenCalledWith( expect(resolver.resolve).toHaveBeenCalledWith(expect.objectContaining(expectedObject));
expect.objectContaining({
__typename: 'LogTreeCommit',
commitPath: 'https://test.com',
committedDate: '2019-01-01',
fileName: 'index.js',
filePath: '/index.js',
message: 'testing message',
sha: '123',
type: 'blob',
}),
);
})); }));
it('writes query to client', () => it('writes query to client', () =>
...@@ -105,18 +134,7 @@ describe('fetchLogsTree', () => { ...@@ -105,18 +134,7 @@ describe('fetchLogsTree', () => {
expect(client.writeQuery).toHaveBeenCalledWith({ expect(client.writeQuery).toHaveBeenCalledWith({
query: expect.anything(), query: expect.anything(),
data: { data: {
commits: [ commits: [expect.objectContaining(expectedObject)],
expect.objectContaining({
__typename: 'LogTreeCommit',
commitPath: 'https://test.com',
committedDate: '2019-01-01',
fileName: 'index.js',
filePath: '/index.js',
message: 'testing message',
sha: '123',
type: 'blob',
}),
],
}, },
}); });
})); }));
......
...@@ -209,6 +209,14 @@ describe ApplicationHelper do ...@@ -209,6 +209,14 @@ describe ApplicationHelper do
end end
end end
describe '#page_startup_api_calls' do
it 'returns map containing JS Page Startup Calls' do
helper.add_page_startup_api_call("testURL")
startup_calls = helper.page_startup_api_calls
expect(startup_calls["testURL"]).to eq("")
end
end
describe '#autocomplete_data_sources' do describe '#autocomplete_data_sources' do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:noteable_type) { Issue } let(:noteable_type) { Issue }
......
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