Skip to content
Snippets Groups Projects
Commit 286fe610 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 4cb5e501
No related branches found
No related tags found
No related merge requests found
Showing
with 299 additions and 272 deletions
Loading
Loading
@@ -87,7 +87,7 @@ gem 'grape-entity', '~> 0.7.1'
gem 'rack-cors', '~> 1.0.6', require: 'rack/cors'
 
# GraphQL API
gem 'graphql', '~> 1.9.12'
gem 'graphql', '~> 1.9.19'
# NOTE: graphiql-rails v1.5+ doesn't work: https://gitlab.com/gitlab-org/gitlab/issues/31771
# TODO: remove app/views/graphiql/rails/editors/show.html.erb when https://github.com/rmosolgo/graphiql-rails/pull/71 is released:
# https://gitlab.com/gitlab-org/gitlab/issues/31747
Loading
Loading
Loading
Loading
@@ -456,7 +456,7 @@ GEM
graphiql-rails (1.4.10)
railties
sprockets-rails
graphql (1.9.12)
graphql (1.9.19)
graphql-docs (1.6.0)
commonmarker (~> 0.16)
escape_utils (~> 1.2)
Loading
Loading
@@ -1252,7 +1252,7 @@ DEPENDENCIES
grape-path-helpers (~> 1.2)
grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10)
graphql (~> 1.9.12)
graphql (~> 1.9.19)
graphql-docs (~> 1.6.0)
grpc (~> 1.24.0)
gssapi
Loading
Loading
Loading
Loading
@@ -492,41 +492,6 @@ const Api = {
buildUrl(url) {
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
},
/**
* Returns pods logs for an environment with an optional pod and container
*
* @param {Object} params
* @param {Object} param.environment - Environment object
* @param {string=} params.podName - Pod name, if not set the backend assumes a default one
* @param {string=} params.containerName - Container name, if not set the backend assumes a default one
* @param {string=} params.start - Starting date to query the logs in ISO format
* @param {string=} params.end - Ending date to query the logs in ISO format
* @returns {Promise} Axios promise for the result of a GET request of logs
*/
getPodLogs({ environment, podName, containerName, search, start, end }) {
const url = this.buildUrl(environment.logs_api_path);
const params = {};
if (podName) {
params.pod_name = podName;
}
if (containerName) {
params.container_name = containerName;
}
if (search) {
params.search = search;
}
if (start) {
params.start = start;
}
if (end) {
params.end = end;
}
return axios.get(url, { params });
},
};
 
export default Api;
Loading
Loading
@@ -24,25 +24,19 @@ export default {
discardModalTitle() {
return sprintf(__('Discard changes to %{path}?'), { path: this.activeFile.path });
},
actionButtonText() {
return this.activeFile.staged ? __('Unstage') : __('Stage');
},
isStaged() {
return !this.activeFile.changed && this.activeFile.staged;
},
},
methods: {
...mapActions(['stageChange', 'unstageChange', 'discardFileChanges']),
actionButtonClicked() {
if (this.activeFile.staged) {
this.unstageChange(this.activeFile.path);
} else {
this.stageChange(this.activeFile.path);
}
},
showDiscardModal() {
this.$refs.discardModal.show();
},
discardChanges(path) {
this.unstageChange(path);
this.discardFileChanges(path);
},
},
};
</script>
Loading
Loading
@@ -65,19 +59,7 @@ export default {
class="btn btn-remove btn-inverted append-right-8"
@click="showDiscardModal"
>
{{ __('Discard') }}
</button>
<button
ref="actionButton"
:class="{
'btn-success': !isStaged,
'btn-warning': isStaged,
}"
type="button"
class="btn btn-inverted"
@click="actionButtonClicked"
>
{{ actionButtonText }}
{{ __('Discard changes') }}
</button>
</div>
<gl-modal
Loading
Loading
@@ -87,7 +69,7 @@ export default {
:ok-title="__('Discard changes')"
:modal-id="discardModalId"
:title="discardModalTitle"
@ok="discardFileChanges(activeFile.path)"
@ok="discardChanges(activeFile.path)"
>
{{ __("You will lose all changes you've made to this file. This action cannot be undone.") }}
</gl-modal>
Loading
Loading
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
import { sprintf, __ } from '~/locale';
import { n__, __ } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import CommitMessageField from './message_field.vue';
import Actions from './actions.vue';
Loading
Loading
@@ -26,15 +26,7 @@ export default {
...mapGetters(['hasChanges']),
...mapGetters('commit', ['discardDraftButtonDisabled', 'preBuiltCommitMessage']),
overviewText() {
return sprintf(
__(
'<strong>%{stagedFilesLength} staged</strong> and <strong>%{changedFilesLength} unstaged</strong> changes',
),
{
stagedFilesLength: this.stagedFiles.length,
changedFilesLength: this.changedFiles.length,
},
);
return n__('%d changed file', '%d changed files', this.stagedFiles.length);
},
commitButtonText() {
return this.stagedFiles.length ? __('Commit') : __('Stage & Commit');
Loading
Loading
@@ -125,7 +117,7 @@ export default {
>
{{ __('Commit…') }}
</button>
<p class="text-center" v-html="overviewText"></p>
<p class="text-center bold">{{ overviewText }}</p>
</div>
<form v-if="!isCompact" ref="formEl" @submit.prevent.stop="commitChanges">
<transition name="fade"> <success-message v-show="lastCommitMsg" /> </transition>
Loading
Loading
Loading
Loading
@@ -17,10 +17,6 @@ export default {
tooltip,
},
props: {
title: {
type: String,
required: true,
},
fileList: {
type: Array,
required: true,
Loading
Loading
@@ -29,18 +25,6 @@ export default {
type: String,
required: true,
},
action: {
type: String,
required: true,
},
actionBtnText: {
type: String,
required: true,
},
actionBtnIcon: {
type: String,
required: true,
},
stagedList: {
type: Boolean,
required: false,
Loading
Loading
@@ -63,9 +47,9 @@ export default {
},
computed: {
titleText() {
return sprintf(__('%{title} changes'), {
title: this.title,
});
if (!this.title) return __('Changes');
return sprintf(__('%{title} changes'), { title: this.title });
},
filesLength() {
return this.fileList.length;
Loading
Loading
@@ -73,17 +57,16 @@ export default {
},
methods: {
...mapActions(['stageAllChanges', 'unstageAllChanges', 'discardAllChanges']),
actionBtnClicked() {
this[this.action]();
$(this.$refs.actionBtn).tooltip('hide');
},
openDiscardModal() {
$('#discard-all-changes').modal('show');
},
unstageAndDiscardAllChanges() {
this.unstageAllChanges();
this.discardAllChanges();
},
},
discardModalText: __(
"You will lose all the unstaged changes you've made in this project. This action cannot be undone.",
"You will lose all uncommitted changes you've made in this project. This action cannot be undone.",
),
};
</script>
Loading
Loading
@@ -95,24 +78,6 @@ export default {
<icon v-once :name="iconName" :size="18" class="append-right-8" />
<strong> {{ titleText }} </strong>
<div class="d-flex ml-auto">
<button
ref="actionBtn"
v-tooltip
:title="actionBtnText"
:aria-label="actionBtnText"
:disabled="!filesLength"
:class="{
'disabled-content': !filesLength,
}"
type="button"
class="d-flex ide-staged-action-btn p-0 border-0 align-items-center"
data-placement="bottom"
data-container="body"
data-boundary="viewport"
@click="actionBtnClicked"
>
<icon :name="actionBtnIcon" :size="16" class="ml-auto mr-auto" />
</button>
<button
v-if="!stagedList"
v-tooltip
Loading
Loading
@@ -151,9 +116,9 @@ export default {
v-if="!stagedList"
id="discard-all-changes"
:footer-primary-button-text="__('Discard all changes')"
:header-title-text="__('Discard all unstaged changes?')"
:header-title-text="__('Discard all changes?')"
footer-primary-button-variant="danger"
@submit="discardAllChanges"
@submit="unstageAndDiscardAllChanges"
>
{{ $options.discardModalText }}
</gl-modal>
Loading
Loading
Loading
Loading
@@ -57,13 +57,7 @@ export default {
},
},
methods: {
...mapActions([
'discardFileChanges',
'updateViewer',
'openPendingTab',
'unstageChange',
'stageChange',
]),
...mapActions(['discardFileChanges', 'updateViewer', 'openPendingTab']),
openFileInEditor() {
if (this.file.type === 'tree') return null;
 
Loading
Loading
@@ -76,13 +70,6 @@ export default {
}
});
},
fileAction() {
if (this.file.staged) {
this.unstageChange(this.file.path);
} else {
this.stageChange(this.file.path);
}
},
},
};
</script>
Loading
Loading
@@ -97,7 +84,6 @@ export default {
}"
class="multi-file-commit-list-path w-100 border-0 ml-0 mr-0"
role="button"
@dblclick="fileAction"
@click="openFileInEditor"
>
<span class="multi-file-commit-list-file-path d-flex align-items-center">
Loading
Loading
<script>
import { mapGetters } from 'vuex';
import { n__, __, sprintf } from '~/locale';
import { n__ } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import ChangedFileIcon from '~/vue_shared/components/changed_file_icon.vue';
Loading
Loading
@@ -49,16 +49,7 @@ export default {
folderChangesTooltip() {
if (this.changesCount === 0) return undefined;
 
if (this.folderUnstagedCount > 0 && this.folderStagedCount === 0) {
return n__('%d unstaged change', '%d unstaged changes', this.folderUnstagedCount);
} else if (this.folderUnstagedCount === 0 && this.folderStagedCount > 0) {
return n__('%d staged change', '%d staged changes', this.folderStagedCount);
}
return sprintf(__('%{staged} staged and %{unstaged} unstaged changes'), {
unstaged: this.folderUnstagedCount,
staged: this.folderStagedCount,
});
return n__('%d changed file', '%d changed files', this.changesCount);
},
showTreeChangesCount() {
return this.isTree && this.changesCount > 0 && !this.file.opened;
Loading
Loading
Loading
Loading
@@ -86,28 +86,12 @@ export default {
</deprecated-modal>
<template v-if="showStageUnstageArea">
<commit-files-list
:title="__('Unstaged')"
:key-prefix="$options.stageKeys.unstaged"
:file-list="changedFiles"
:action-btn-text="__('Stage all changes')"
:active-file-key="activeFileKey"
:empty-state-text="__('There are no unstaged changes')"
action="stageAllChanges"
action-btn-icon="stage-all"
class="is-first"
icon-name="unstaged"
/>
<commit-files-list
:title="__('Staged')"
:key-prefix="$options.stageKeys.staged"
:file-list="stagedFiles"
:action-btn-text="__('Unstage all changes')"
:staged-list="true"
:active-file-key="activeFileKey"
:empty-state-text="__('There are no staged changes')"
action="unstageAllChanges"
action-btn-icon="unstage-all"
icon-name="staged"
:empty-state-text="__('There are no changes')"
class="is-first"
icon-name="unstaged"
/>
</template>
<empty-state v-if="unusedSeal" />
Loading
Loading
<script>
import { throttle } from 'lodash';
import { mapActions, mapState, mapGetters } from 'vuex';
import { GlDropdown, GlDropdownItem, GlFormGroup, GlSearchBoxByClick, GlAlert } from '@gitlab/ui';
import {
GlSprintf,
GlAlert,
GlDropdown,
GlDropdownItem,
GlFormGroup,
GlSearchBoxByClick,
GlInfiniteScroll,
} from '@gitlab/ui';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import { scrollDown } from '~/lib/utils/scroll_utils';
import LogControlButtons from './log_control_buttons.vue';
 
import { timeRanges, defaultTimeRange } from '~/monitoring/constants';
import { timeRangeFromUrl } from '~/monitoring/utils';
import { formatDate } from '../utils';
 
export default {
components: {
GlSprintf,
GlAlert,
GlDropdown,
GlDropdownItem,
GlFormGroup,
GlSearchBoxByClick,
GlInfiniteScroll,
DateTimePicker,
LogControlButtons,
},
filters: {
formatDate,
},
props: {
environmentName: {
type: String,
Loading
Loading
@@ -39,11 +53,13 @@ export default {
required: true,
},
},
traceHeight: 600,
data() {
return {
searchQuery: '',
timeRanges,
isElasticStackCalloutDismissed: false,
scrollDownButtonDisabled: true,
};
},
computed: {
Loading
Loading
@@ -52,7 +68,7 @@ export default {
 
timeRangeModel: {
get() {
return this.timeRange.current;
return this.timeRange.selected;
},
set(val) {
this.setTimeRange(val);
Loading
Loading
@@ -60,7 +76,7 @@ export default {
},
 
showLoader() {
return this.logs.isLoading || !this.logs.isComplete;
return this.logs.isLoading;
},
advancedFeaturesEnabled() {
const environment = this.environments.options.find(
Loading
Loading
@@ -75,16 +91,6 @@ export default {
return !this.isElasticStackCalloutDismissed && this.disableAdvancedControls;
},
},
watch: {
trace(val) {
this.$nextTick(() => {
if (val) {
scrollDown();
}
this.$refs.scrollButtons.update();
});
},
},
mounted() {
this.setInitData({
timeRange: timeRangeFromUrl() || defaultTimeRange,
Loading
Loading
@@ -102,12 +108,26 @@ export default {
'showPodLogs',
'showEnvironment',
'fetchEnvironments',
'fetchMoreLogsPrepend',
]),
topReached() {
if (!this.logs.isLoading) {
this.fetchMoreLogsPrepend();
}
},
scrollDown() {
this.$refs.infiniteScroll.scrollDown();
},
scroll: throttle(function scrollThrottled({ target = {} }) {
const { scrollTop = 0, clientHeight = 0, scrollHeight = 0 } = target;
this.scrollDownButtonDisabled = scrollTop + clientHeight === scrollHeight;
}, 200),
},
};
</script>
<template>
<div class="build-page-pod-logs mt-3">
<div class="environment-logs-viewer mt-3">
<gl-alert
v-if="shouldShowElasticStackCallout"
class="mb-3 js-elasticsearch-alert"
Loading
Loading
@@ -209,14 +229,50 @@ export default {
<log-control-buttons
ref="scrollButtons"
class="controllers align-self-end mb-1"
:scroll-down-button-disabled="scrollDownButtonDisabled"
@refresh="showPodLogs(pods.current)"
@scrollDown="scrollDown"
/>
</div>
<pre class="build-trace js-log-trace"><code class="bash js-build-output">{{trace}}
<div v-if="showLoader" class="build-loader-animation js-build-loader-animation">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div></code></pre>
<gl-infinite-scroll
ref="infiniteScroll"
class="log-lines"
:style="{ height: `${$options.traceHeight}px` }"
:max-list-height="$options.traceHeight"
:fetched-items="logs.lines.length"
@topReached="topReached"
@scroll="scroll"
>
<template #items>
<pre
class="build-trace js-log-trace"
><code class="bash js-build-output"><div v-if="showLoader" class="build-loader-animation js-build-loader-animation">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>{{trace}}
</code></pre>
</template>
<template #default
><div></div
></template>
</gl-infinite-scroll>
<div ref="logFooter" class="log-footer py-2 px-3">
<gl-sprintf :message="s__('Environments|Logs from %{start} to %{end}.')">
<template #start>{{ timeRange.current.start | formatDate }}</template>
<template #end>{{ timeRange.current.end | formatDate }}</template>
</gl-sprintf>
<gl-sprintf
v-if="!logs.isComplete"
:message="s__('Environments|Currently showing %{fetched} results.')"
>
<template #fetched>{{ logs.lines.length }}</template>
</gl-sprintf>
<template v-else>
{{ s__('Environments|Currently showing all results.') }}</template
>
</div>
</div>
</template>
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import {
canScroll,
isScrolledToTop,
isScrolledToBottom,
scrollDown,
scrollUp,
} from '~/lib/utils/scroll_utils';
import Icon from '~/vue_shared/components/icon.vue';
 
export default {
Loading
Loading
@@ -17,32 +10,34 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
scrollUpButtonDisabled: {
type: Boolean,
required: false,
default: false,
},
scrollDownButtonDisabled: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
scrollToTopEnabled: false,
scrollToBottomEnabled: false,
scrollUpAvailable: Boolean(this.$listeners.scrollUp),
scrollDownAvailable: Boolean(this.$listeners.scrollDown),
};
},
created() {
window.addEventListener('scroll', this.update);
},
destroyed() {
window.removeEventListener('scroll', this.update);
},
methods: {
/**
* Checks if page can be scrolled and updates
* enabled/disabled state of buttons accordingly
*/
update() {
this.scrollToTopEnabled = canScroll() && !isScrolledToTop();
this.scrollToBottomEnabled = canScroll() && !isScrolledToBottom();
},
handleRefreshClick() {
this.$emit('refresh');
},
scrollUp,
scrollDown,
handleScrollUp() {
this.$emit('scrollUp');
},
handleScrollDown() {
this.$emit('scrollDown');
},
},
};
</script>
Loading
Loading
@@ -50,6 +45,7 @@ export default {
<template>
<div>
<div
v-if="scrollUpAvailable"
v-gl-tooltip
class="controllers-buttons"
:title="__('Scroll to top')"
Loading
Loading
@@ -59,13 +55,15 @@ export default {
id="scroll-to-top"
class="btn-blank js-scroll-to-top"
:aria-label="__('Scroll to top')"
:disabled="!scrollToTopEnabled"
@click="scrollUp()"
:disabled="scrollUpButtonDisabled"
@click="handleScrollUp()"
><icon name="scroll_up"
/></gl-button>
</div>
<div
v-if="scrollDownAvailable"
v-gl-tooltip
:disabled="scrollUpButtonDisabled"
class="controllers-buttons"
:title="__('Scroll to bottom')"
aria-labelledby="scroll-to-bottom"
Loading
Loading
@@ -74,8 +72,9 @@ export default {
id="scroll-to-bottom"
class="btn-blank js-scroll-to-bottom"
:aria-label="__('Scroll to bottom')"
:disabled="!scrollToBottomEnabled"
@click="scrollDown()"
:v-if="scrollDownAvailable"
:disabled="scrollDownButtonDisabled"
@click="handleScrollDown()"
><icon name="scroll_down"
/></gl-button>
</div>
Loading
Loading
import Api from '~/api';
import { backOff } from '~/lib/utils/common_utils';
import httpStatusCodes from '~/lib/utils/http_status';
import axios from '~/lib/utils/axios_utils';
Loading
Loading
@@ -16,9 +15,10 @@ const flashLogsError = () => {
flash(s__('Metrics|There was an error fetching the logs, please try again'));
};
 
const requestLogsUntilData = params =>
const requestUntilData = (url, params) =>
backOff((next, stop) => {
Api.getPodLogs(params)
axios
.get(url, { params })
.then(res => {
if (res.status === httpStatusCodes.ACCEPTED) {
next();
Loading
Loading
@@ -31,10 +31,36 @@ const requestLogsUntilData = params =>
});
});
 
export const setInitData = ({ commit }, { timeRange, environmentName, podName }) => {
if (timeRange) {
commit(types.SET_TIME_RANGE, timeRange);
const requestLogsUntilData = state => {
const params = {};
const { logs_api_path } = state.environments.options.find(
({ name }) => name === state.environments.current,
);
if (state.pods.current) {
params.pod_name = state.pods.current;
}
if (state.search) {
params.search = state.search;
}
if (state.timeRange.current) {
try {
const { start, end } = convertToFixedRange(state.timeRange.current);
params.start = start;
params.end = end;
} catch {
flashTimeRangeWarning();
}
}
if (state.logs.cursor) {
params.cursor = state.logs.cursor;
}
return requestUntilData(logs_api_path, params);
};
export const setInitData = ({ commit }, { timeRange, environmentName, podName }) => {
commit(types.SET_TIME_RANGE, timeRange);
commit(types.SET_PROJECT_ENVIRONMENT, environmentName);
commit(types.SET_CURRENT_POD_NAME, podName);
};
Loading
Loading
@@ -60,10 +86,15 @@ export const showEnvironment = ({ dispatch, commit }, environmentName) => {
dispatch('fetchLogs');
};
 
/**
* Fetch environments data and initial logs
* @param {Object} store
* @param {String} environmentsPath
*/
export const fetchEnvironments = ({ commit, dispatch }, environmentsPath) => {
commit(types.REQUEST_ENVIRONMENTS_DATA);
 
axios
return axios
.get(environmentsPath)
.then(({ data }) => {
commit(types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS, data.environments);
Loading
Loading
@@ -76,32 +107,16 @@ export const fetchEnvironments = ({ commit, dispatch }, environmentsPath) => {
};
 
export const fetchLogs = ({ commit, state }) => {
const params = {
environment: state.environments.options.find(({ name }) => name === state.environments.current),
podName: state.pods.current,
search: state.search,
};
if (state.timeRange.current) {
try {
const { start, end } = convertToFixedRange(state.timeRange.current);
params.start = start;
params.end = end;
} catch {
flashTimeRangeWarning();
}
}
commit(types.REQUEST_PODS_DATA);
commit(types.REQUEST_LOGS_DATA);
 
return requestLogsUntilData(params)
return requestLogsUntilData(state)
.then(({ data }) => {
const { pod_name, pods, logs } = data;
const { pod_name, pods, logs, cursor } = data;
commit(types.SET_CURRENT_POD_NAME, pod_name);
 
commit(types.RECEIVE_PODS_DATA_SUCCESS, pods);
commit(types.RECEIVE_LOGS_DATA_SUCCESS, logs);
commit(types.RECEIVE_LOGS_DATA_SUCCESS, { logs, cursor });
})
.catch(() => {
commit(types.RECEIVE_PODS_DATA_ERROR);
Loading
Loading
@@ -110,5 +125,24 @@ export const fetchLogs = ({ commit, state }) => {
});
};
 
export const fetchMoreLogsPrepend = ({ commit, state }) => {
if (state.logs.isComplete) {
// return when all logs are loaded
return Promise.resolve();
}
commit(types.REQUEST_LOGS_DATA_PREPEND);
return requestLogsUntilData(state)
.then(({ data }) => {
const { logs, cursor } = data;
commit(types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS, { logs, cursor });
})
.catch(() => {
commit(types.RECEIVE_LOGS_DATA_PREPEND_ERROR);
flashLogsError();
});
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
import dateFormat from 'dateformat';
import { formatDate } from '../utils';
 
export const trace = state =>
state.logs.lines
.map(item => [dateFormat(item.timestamp, 'UTC:mmm dd HH:MM:ss.l"Z"'), item.message].join(' | '))
.join('\n');
const mapTrace = ({ timestamp = null, message = '' }) =>
[timestamp ? formatDate(timestamp) : '', message].join(' | ');
export const trace = state => state.logs.lines.map(mapTrace).join('\n');
 
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
Loading
Loading
@@ -10,6 +10,9 @@ export const RECEIVE_ENVIRONMENTS_DATA_ERROR = 'RECEIVE_ENVIRONMENTS_DATA_ERROR'
export const REQUEST_LOGS_DATA = 'REQUEST_LOGS_DATA';
export const RECEIVE_LOGS_DATA_SUCCESS = 'RECEIVE_LOGS_DATA_SUCCESS';
export const RECEIVE_LOGS_DATA_ERROR = 'RECEIVE_LOGS_DATA_ERROR';
export const REQUEST_LOGS_DATA_PREPEND = 'REQUEST_LOGS_DATA_PREPEND';
export const RECEIVE_LOGS_DATA_PREPEND_SUCCESS = 'RECEIVE_LOGS_DATA_PREPEND_SUCCESS';
export const RECEIVE_LOGS_DATA_PREPEND_ERROR = 'RECEIVE_LOGS_DATA_PREPEND_ERROR';
 
export const REQUEST_PODS_DATA = 'REQUEST_PODS_DATA';
export const RECEIVE_PODS_DATA_SUCCESS = 'RECEIVE_PODS_DATA_SUCCESS';
Loading
Loading
import * as types from './mutation_types';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
const mapLine = ({ timestamp, message }) => ({
timestamp,
message,
});
 
export default {
/** Search data */
// Search Data
[types.SET_SEARCH](state, searchQuery) {
state.search = searchQuery;
},
 
/** Time Range data */
// Time Range Data
[types.SET_TIME_RANGE](state, timeRange) {
state.timeRange.current = timeRange;
state.timeRange.selected = timeRange;
state.timeRange.current = convertToFixedRange(timeRange);
},
 
/** Environments data */
// Environments Data
[types.SET_PROJECT_ENVIRONMENT](state, environmentName) {
state.environments.current = environmentName;
},
Loading
Loading
@@ -28,24 +35,49 @@ export default {
state.environments.isLoading = false;
},
 
/** Logs data */
// Logs data
[types.REQUEST_LOGS_DATA](state) {
state.timeRange.current = convertToFixedRange(state.timeRange.selected);
state.logs.lines = [];
state.logs.isLoading = true;
// start pagination from the beginning
state.logs.cursor = null;
state.logs.isComplete = false;
},
[types.RECEIVE_LOGS_DATA_SUCCESS](state, lines) {
state.logs.lines = lines;
[types.RECEIVE_LOGS_DATA_SUCCESS](state, { logs = [], cursor }) {
state.logs.lines = logs.map(mapLine);
state.logs.isLoading = false;
state.logs.isComplete = true;
state.logs.cursor = cursor;
if (!cursor) {
state.logs.isComplete = true;
}
},
[types.RECEIVE_LOGS_DATA_ERROR](state) {
state.logs.lines = [];
state.logs.isLoading = false;
state.logs.isComplete = true;
},
 
/** Pods data */
[types.REQUEST_LOGS_DATA_PREPEND](state) {
state.logs.isLoading = true;
},
[types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS](state, { logs = [], cursor }) {
const lines = logs.map(mapLine);
state.logs.lines = lines.concat(state.logs.lines);
state.logs.isLoading = false;
state.logs.cursor = cursor;
if (!cursor) {
state.logs.isComplete = true;
}
},
[types.RECEIVE_LOGS_DATA_PREPEND_ERROR](state) {
state.logs.isLoading = false;
},
// Pods data
[types.SET_CURRENT_POD_NAME](state, podName) {
state.pods.current = podName;
},
Loading
Loading
import { timeRanges, defaultTimeRange } from '~/monitoring/constants';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
 
export default () => ({
/**
Loading
Loading
@@ -11,7 +12,10 @@ export default () => ({
*/
timeRange: {
options: timeRanges,
current: defaultTimeRange,
// Selected time range, can be fixed or relative
selected: defaultTimeRange,
// Current time range, must be fixed
current: convertToFixedRange(defaultTimeRange),
},
 
/**
Loading
Loading
@@ -29,7 +33,12 @@ export default () => ({
logs: {
lines: [],
isLoading: false,
isComplete: true,
/**
* Logs `cursor` represents the current pagination position,
* Should be sent in next batch (page) of logs to be fetched
*/
cursor: null,
isComplete: false,
},
 
/**
Loading
Loading
import { secondsToMilliseconds } from '~/lib/utils/datetime_utility';
import dateFormat from 'dateformat';
const dateFormatMask = 'UTC:mmm dd HH:MM:ss.l"Z"';
 
/**
* Returns a time range (`start`, `end`) where `start` is the
Loading
Loading
@@ -20,4 +23,6 @@ export const getTimeRange = (seconds = 0) => {
};
};
 
export const formatDate = timestamp => dateFormat(timestamp, dateFormatMask);
export default {};
import '~/pages/sessions/index';
<script>
import { mapState, mapActions } from 'vuex';
import { GlSkeletonLoading, GlEmptyState } from '@gitlab/ui';
import { GlSkeletonLoading, GlEmptyState, GlLink } from '@gitlab/ui';
import {
getParameterByName,
historyPushState,
buildUrlWithCurrentLocation,
} from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import ReleaseBlock from './release_block.vue';
 
Loading
Loading
@@ -16,13 +17,14 @@ export default {
GlEmptyState,
ReleaseBlock,
TablePagination,
GlLink,
},
props: {
projectId: {
type: String,
required: true,
},
documentationLink: {
documentationPath: {
type: String,
required: true,
},
Loading
Loading
@@ -30,6 +32,11 @@ export default {
type: String,
required: true,
},
newReleasePath: {
type: String,
required: false,
default: '',
},
},
computed: {
...mapState('list', ['isLoading', 'releases', 'hasError', 'pageInfo']),
Loading
Loading
@@ -39,6 +46,11 @@ export default {
shouldRenderSuccessState() {
return this.releases.length && !this.isLoading && !this.hasError;
},
emptyStateText() {
return __(
"Releases are based on Git tags and mark specific points in a project's development history. They can contain information about the type of changes and can also deliver binaries, like compiled versions of your software.",
);
},
},
created() {
this.fetchReleases({
Loading
Loading
@@ -56,7 +68,16 @@ export default {
};
</script>
<template>
<div class="prepend-top-default">
<div class="flex flex-column mt-2">
<gl-link
v-if="newReleasePath"
:href="newReleasePath"
:aria-describedby="shouldRenderEmptyState && 'releases-description'"
class="btn btn-success align-self-end mb-2 js-new-release-btn"
>
{{ __('New release') }}
</gl-link>
<gl-skeleton-loading v-if="isLoading" class="js-loading" />
 
<gl-empty-state
Loading
Loading
@@ -64,14 +85,20 @@ export default {
class="js-empty-state"
:title="__('Getting started with releases')"
:svg-path="illustrationPath"
:description="
__(
'Releases are based on Git tags and mark specific points in a project\'s development history. They can contain information about the type of changes and can also deliver binaries, like compiled versions of your software.',
)
"
:primary-button-link="documentationLink"
:primary-button-text="__('Open Documentation')"
/>
>
<template #description>
<span id="releases-description">
{{ emptyStateText }}
<gl-link
:href="documentationPath"
:aria-label="__('Releases documentation')"
target="_blank"
>
{{ __('More information') }}
</gl-link>
</span>
</template>
</gl-empty-state>
 
<div v-else-if="shouldRenderSuccessState" class="js-success-state">
<release-block
Loading
Loading
Loading
Loading
@@ -15,11 +15,7 @@ export default () => {
}),
render: h =>
h(ReleaseListApp, {
props: {
projectId: el.dataset.projectId,
documentationLink: el.dataset.documentationPath,
illustrationPath: el.dataset.illustrationPath,
},
props: el.dataset,
}),
});
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment