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(
});
},
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
.get(url, { params: { viewer: 'rich', format: 'json' } })
.then(({ data }) => ({ ...data, __typename: 'ReadmeFile' }));
......
......@@ -25,34 +25,49 @@ export function fetchLogsTree(client, path, offset, resolver = null) {
const { projectPath } = client.readQuery({ query: getProjectPath });
const { escapedRef } = client.readQuery({ query: getRef });
fetchpromise = axios
.get(
`${gon.relative_url_root}/${projectPath}/-/refs/${escapedRef}/logs_tree/${encodeURIComponent(
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 },
});
const apiUrl = `${
gon.relative_url_root
}/${projectPath}/-/refs/${escapedRef}/logs_tree/${encodeURIComponent(path.replace(/^\//, ''))}`;
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) {
fetchLogsTree(client, path, headerLogsOffset);
} else {
resolvers = [];
}
});
fetchpromise = null;
if (headerLogsOffset) {
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;
}
......@@ -335,6 +335,15 @@ module ApplicationHelper
}
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)
return {} unless object && noteable_type
......
......@@ -25,6 +25,9 @@
%meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }
- if page_startup_api_calls
= render 'layouts/startup_js'
-# Open Graph - http://ogp.me/
%meta{ property: 'og:type', content: "object" }
%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 @@
- 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) }
- 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
.nav-block
......
......@@ -2,6 +2,10 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import { resolveCommit, fetchLogsTree } from '~/repository/log_tree';
window.gl = window.gl || {
startup_calls: {},
};
const mockData = [
{
commit: {
......@@ -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', () => {
it('calls resolve when commit found', () => {
const resolver = {
......@@ -84,20 +99,34 @@ describe('fetchLogsTree', () => {
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', () =>
fetchLogsTree(client, '', '0', resolver).then(() => {
expect(resolver.resolve).toHaveBeenCalledWith(
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',
}),
);
expect(resolver.resolve).toHaveBeenCalledWith(expect.objectContaining(expectedObject));
}));
it('writes query to client', () =>
......@@ -105,18 +134,7 @@ describe('fetchLogsTree', () => {
expect(client.writeQuery).toHaveBeenCalledWith({
query: expect.anything(),
data: {
commits: [
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',
}),
],
commits: [expect.objectContaining(expectedObject)],
},
});
}));
......
......@@ -209,6 +209,14 @@ describe ApplicationHelper do
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
let(:project) { create(:project) }
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