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

Add latest changes from gitlab-org/gitlab@master

parent 67cdfd26
No related branches found
No related tags found
No related merge requests found
Showing
with 262 additions and 112 deletions
Loading
Loading
@@ -58,7 +58,7 @@ export default {
 
<template>
<div class="frequent-items-list-container">
<ul class="list-unstyled">
<ul ref="frequentItemsList" class="list-unstyled">
<li v-if="isListEmpty" :class="{ 'section-failure': isFetchFailed }" class="section-empty">
{{ listEmptyMessage }}
</li>
Loading
Loading
Loading
Loading
@@ -117,3 +117,23 @@ export const scaledSIFormatter = (unit = '', prefixOffset = 0) => {
 
return scaledFormatter(units);
};
/**
* Returns a function that formats a number scaled using SI units notation.
*/
export const scaledBinaryFormatter = (unit = '', prefixOffset = 0) => {
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
const multiplicative = ['Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
const symbols = ['', ...multiplicative];
const units = symbols.slice(prefixOffset).map(prefix => {
return `${prefix}${unit}`;
});
if (!units.length) {
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
throw new RangeError('The unit cannot be converted, please try a different scale');
}
return scaledFormatter(units, 1024);
};
import { s__ } from '~/locale';
 
import { suffixFormatter, scaledSIFormatter, numberFormatter } from './formatter_factory';
import {
suffixFormatter,
scaledSIFormatter,
scaledBinaryFormatter,
numberFormatter,
} from './formatter_factory';
 
/**
* Supported formats
*
* Based on:
*
* https://tc39.es/proposal-unified-intl-numberformat/section6/locales-currencies-tz_proposed_out.html#sec-issanctionedsimpleunitidentifier
*/
export const SUPPORTED_FORMATS = {
// Number
Loading
Loading
@@ -13,15 +22,23 @@ export const SUPPORTED_FORMATS = {
 
// Duration
seconds: 'seconds',
miliseconds: 'miliseconds',
milliseconds: 'milliseconds',
 
// Digital
bytes: 'bytes',
// Digital (Metric)
decimalBytes: 'decimalBytes',
kilobytes: 'kilobytes',
megabytes: 'megabytes',
gigabytes: 'gigabytes',
terabytes: 'terabytes',
petabytes: 'petabytes',
// Digital (IEC)
bytes: 'bytes',
kibibytes: 'kibibytes',
mebibytes: 'mebibytes',
gibibytes: 'gibibytes',
tebibytes: 'tebibytes',
pebibytes: 'pebibytes',
};
 
/**
Loading
Loading
@@ -32,6 +49,7 @@ export const SUPPORTED_FORMATS = {
*/
export const getFormatter = (format = SUPPORTED_FORMATS.number) => {
// Number
if (format === SUPPORTED_FORMATS.number) {
/**
* Formats a number
Loading
Loading
@@ -70,6 +88,7 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => {
}
 
// Durations
if (format === SUPPORTED_FORMATS.seconds) {
/**
* Formats a number of seconds
Loading
Loading
@@ -82,9 +101,9 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => {
*/
return suffixFormatter(s__('Units|s'));
}
if (format === SUPPORTED_FORMATS.miliseconds) {
if (format === SUPPORTED_FORMATS.milliseconds) {
/**
* Formats a number of miliseconds with ms as units
* Formats a number of milliseconds with ms as units
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1ms`
Loading
Loading
@@ -95,8 +114,9 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => {
return suffixFormatter(s__('Units|ms'));
}
 
// Digital
if (format === SUPPORTED_FORMATS.bytes) {
// Digital (Metric)
if (format === SUPPORTED_FORMATS.decimalBytes) {
/**
* Formats a number of bytes scaled up to larger digital
* units for larger numbers.
Loading
Loading
@@ -162,6 +182,76 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => {
*/
return scaledSIFormatter('B', 5);
}
// Digital (IEC)
if (format === SUPPORTED_FORMATS.bytes) {
/**
* Formats a number of bytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1B`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B');
}
if (format === SUPPORTED_FORMATS.kibibytes) {
/**
* Formats a number of kilobytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1kB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 1);
}
if (format === SUPPORTED_FORMATS.mebibytes) {
/**
* Formats a number of megabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1MB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 2);
}
if (format === SUPPORTED_FORMATS.gibibytes) {
/**
* Formats a number of gigabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 3);
}
if (format === SUPPORTED_FORMATS.tebibytes) {
/**
* Formats a number of terabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 4);
}
if (format === SUPPORTED_FORMATS.pebibytes) {
/**
* Formats a number of petabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1PB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 5);
}
// Fail so client library addresses issue
throw TypeError(`${format} is not a valid number format`);
};
import axios from '~/lib/utils/axios_utils';
import * as constants from '../constants';
export default {
fetchDiscussions(endpoint, filter, persistFilter = true) {
const config =
filter !== undefined
? { params: { notes_filter: filter, persist_filter: persistFilter } }
: null;
return axios.get(endpoint, config);
},
replyToDiscussion(endpoint, data) {
return axios.post(endpoint, data);
},
updateNote(endpoint, data) {
return axios.put(endpoint, data);
},
createNewNote(endpoint, data) {
return axios.post(endpoint, data);
},
toggleResolveNote(endpoint, isResolved) {
const { RESOLVE_NOTE_METHOD_NAME, UNRESOLVE_NOTE_METHOD_NAME } = constants;
const method = isResolved ? UNRESOLVE_NOTE_METHOD_NAME : RESOLVE_NOTE_METHOD_NAME;
return axios[method](endpoint);
},
poll(data = {}) {
const endpoint = data.notesData.notesPath;
const { lastFetchedAt } = data;
const options = {
headers: {
'X-Last-Fetched-At': lastFetchedAt ? `${lastFetchedAt}` : undefined,
},
};
return axios.get(endpoint, options);
},
toggleIssueState(endpoint, data) {
return axios.put(endpoint, data);
},
};
Loading
Loading
@@ -8,7 +8,6 @@ import Poll from '../../lib/utils/poll';
import * as types from './mutation_types';
import * as utils from './utils';
import * as constants from '../constants';
import service from '../services/notes_service';
import loadAwardsHandler from '../../awards_handler';
import sidebarTimeTrackingEventHub from '../../sidebar/event_hub';
import { isInViewport, scrollToElement, isInMRPage } from '../../lib/utils/common_utils';
Loading
Loading
@@ -47,11 +46,17 @@ export const setNotesFetchedState = ({ commit }, state) =>
 
export const toggleDiscussion = ({ commit }, data) => commit(types.TOGGLE_DISCUSSION, data);
 
export const fetchDiscussions = ({ commit, dispatch }, { path, filter, persistFilter }) =>
service.fetchDiscussions(path, filter, persistFilter).then(({ data }) => {
export const fetchDiscussions = ({ commit, dispatch }, { path, filter, persistFilter }) => {
const config =
filter !== undefined
? { params: { notes_filter: filter, persist_filter: persistFilter } }
: null;
return axios.get(path, config).then(({ data }) => {
commit(types.SET_INITIAL_DISCUSSIONS, data);
dispatch('updateResolvableDiscussionsCounts');
});
};
 
export const updateDiscussion = ({ commit, state }, discussion) => {
commit(types.UPDATE_DISCUSSION, discussion);
Loading
Loading
@@ -78,7 +83,7 @@ export const deleteNote = ({ dispatch }, note) =>
});
 
export const updateNote = ({ commit, dispatch }, { endpoint, note }) =>
service.updateNote(endpoint, note).then(({ data }) => {
axios.put(endpoint, note).then(({ data }) => {
commit(types.UPDATE_NOTE, data);
dispatch('startTaskList');
});
Loading
Loading
@@ -109,7 +114,7 @@ export const replyToDiscussion = (
{ commit, state, getters, dispatch },
{ endpoint, data: reply },
) =>
service.replyToDiscussion(endpoint, reply).then(({ data }) => {
axios.post(endpoint, reply).then(({ data }) => {
if (data.discussion) {
commit(types.UPDATE_DISCUSSION, data.discussion);
 
Loading
Loading
@@ -126,7 +131,7 @@ export const replyToDiscussion = (
});
 
export const createNewNote = ({ commit, dispatch }, { endpoint, data: reply }) =>
service.createNewNote(endpoint, reply).then(({ data }) => {
axios.post(endpoint, reply).then(({ data }) => {
if (!data.errors) {
commit(types.ADD_NEW_NOTE, data);
 
Loading
Loading
@@ -156,20 +161,24 @@ export const resolveDiscussion = ({ state, dispatch, getters }, { discussionId }
});
};
 
export const toggleResolveNote = ({ commit, dispatch }, { endpoint, isResolved, discussion }) =>
service.toggleResolveNote(endpoint, isResolved).then(({ data }) => {
const mutationType = discussion ? types.UPDATE_DISCUSSION : types.UPDATE_NOTE;
export const toggleResolveNote = ({ commit, dispatch }, { endpoint, isResolved, discussion }) => {
const method = isResolved
? constants.UNRESOLVE_NOTE_METHOD_NAME
: constants.RESOLVE_NOTE_METHOD_NAME;
const mutationType = discussion ? types.UPDATE_DISCUSSION : types.UPDATE_NOTE;
 
return axios[method](endpoint).then(({ data }) => {
commit(mutationType, data);
 
dispatch('updateResolvableDiscussionsCounts');
 
dispatch('updateMergeRequestWidget');
});
};
 
export const closeIssue = ({ commit, dispatch, state }) => {
dispatch('toggleStateButtonLoading', true);
return service.toggleIssueState(state.notesData.closePath).then(({ data }) => {
return axios.put(state.notesData.closePath).then(({ data }) => {
commit(types.CLOSE_ISSUE);
dispatch('emitStateChangedEvent', data);
dispatch('toggleStateButtonLoading', false);
Loading
Loading
@@ -178,7 +187,7 @@ export const closeIssue = ({ commit, dispatch, state }) => {
 
export const reopenIssue = ({ commit, dispatch, state }) => {
dispatch('toggleStateButtonLoading', true);
return service.toggleIssueState(state.notesData.reopenPath).then(({ data }) => {
return axios.put(state.notesData.reopenPath).then(({ data }) => {
commit(types.REOPEN_ISSUE);
dispatch('emitStateChangedEvent', data);
dispatch('toggleStateButtonLoading', false);
Loading
Loading
@@ -355,11 +364,35 @@ const pollSuccessCallBack = (resp, commit, state, getters, dispatch) => {
return resp;
};
 
const getFetchDataParams = state => {
const endpoint = state.notesData.notesPath;
const options = {
headers: {
'X-Last-Fetched-At': state.lastFetchedAt ? `${state.lastFetchedAt}` : undefined,
},
};
return { endpoint, options };
};
export const fetchData = ({ commit, state, getters }) => {
const { endpoint, options } = getFetchDataParams(state);
axios
.get(endpoint, options)
.then(({ data }) => pollSuccessCallBack(data, commit, state, getters))
.catch(() => Flash(__('Something went wrong while fetching latest comments.')));
};
export const poll = ({ commit, state, getters, dispatch }) => {
eTagPoll = new Poll({
resource: service,
resource: {
poll: () => {
const { endpoint, options } = getFetchDataParams(state);
return axios.get(endpoint, options);
},
},
method: 'poll',
data: state,
successCallback: ({ data }) => pollSuccessCallBack(data, commit, state, getters, dispatch),
errorCallback: () => Flash(__('Something went wrong while fetching latest comments.')),
});
Loading
Loading
@@ -367,7 +400,7 @@ export const poll = ({ commit, state, getters, dispatch }) => {
if (!Visibility.hidden()) {
eTagPoll.makeRequest();
} else {
service.poll(state);
fetchData({ commit, state, getters });
}
 
Visibility.change(() => {
Loading
Loading
@@ -387,18 +420,6 @@ export const restartPolling = () => {
if (eTagPoll) eTagPoll.restart();
};
 
export const fetchData = ({ commit, state, getters }) => {
const requestData = {
endpoint: state.notesData.notesPath,
lastFetchedAt: state.lastFetchedAt,
};
service
.poll(requestData)
.then(({ data }) => pollSuccessCallBack(data, commit, state, getters))
.catch(() => Flash(__('Something went wrong while fetching latest comments.')));
};
export const toggleAward = ({ commit, getters }, { awardName, noteId }) => {
commit(types.TOGGLE_AWARD, { awardName, note: getters.notesById[noteId] });
};
Loading
Loading
import $ from 'jquery';
import _ from 'underscore';
import { escape } from 'lodash';
import { s__, n__, sprintf } from '~/locale';
import axios from '../lib/utils/axios_utils';
import PANEL_STATE from './constants';
Loading
Loading
@@ -69,13 +69,13 @@ export default class PrometheusMetrics {
if (metric.active_metrics > 0) {
totalExporters += 1;
this.$monitoredMetricsList.append(
`<li>${_.escape(metric.group)}<span class="badge">${_.escape(
`<li>${escape(metric.group)}<span class="badge">${escape(
metric.active_metrics,
)}</span></li>`,
);
totalMonitoredMetrics += metric.active_metrics;
if (metric.metrics_missing_requirements > 0) {
this.$missingEnvVarMetricsList.append(`<li>${_.escape(metric.group)}</li>`);
this.$missingEnvVarMetricsList.append(`<li>${escape(metric.group)}</li>`);
totalMissingEnvVarMetrics += 1;
}
}
Loading
Loading
Loading
Loading
@@ -59,7 +59,7 @@
max-width: 100%;
}
 
&:not(.md-file) img:not(.emoji) {
&:not(.md) img:not(.emoji) {
border: 1px solid $white-normal;
padding: 5px;
margin: 5px 0;
Loading
Loading
Loading
Loading
@@ -312,6 +312,7 @@ class ProjectPolicy < BasePolicy
enable :destroy_artifacts
enable :daily_statistics
enable :admin_operations
enable :read_deploy_token
end
 
rule { (mirror_available & can?(:admin_project)) | admin }.enable :admin_remote_mirror
Loading
Loading
- if @wiki_home.present?
%div{ class: container_class }
.md.md-file.prepend-top-default.append-bottom-default
.md.prepend-top-default.append-bottom-default
= render_wiki_content(@wiki_home)
- else
- can_create_wiki = can?(current_user, :create_wiki, @project)
Loading
Loading
- if markup?(@blob.name)
.file-content.md.md-file
.file-content.md
= markup(@blob.name, @content)
- else
.diff-file
Loading
Loading
- blob = viewer.blob
- context = blob.respond_to?(:rendered_markup) ? { rendered: blob.rendered_markup } : {}
.file-content.md.md-file
.file-content.md
= markup(blob.name, blob.data, context)
Loading
Loading
@@ -26,7 +26,7 @@
= (s_("WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}.") % { most_recent_link: most_recent_link, history_link: history_link }).html_safe
 
.prepend-top-default.append-bottom-default
.md.md-file{ data: { qa_selector: 'wiki_page_content' } }
.md{ data: { qa_selector: 'wiki_page_content' } }
= render_wiki_content(@page)
 
= render 'sidebar'
Loading
Loading
@@ -23,7 +23,7 @@
%i.fa.fa-file
%strong= snippet.file_name
- if markup?(snippet.file_name)
.file-content.md.md-file
.file-content.md
- snippet_chunks.each do |chunk|
- unless chunk[:data].empty?
= markup(snippet.file_name, chunk[:data])
Loading
Loading
---
title: Adds new activity panel to package details page
merge_request: 25534
author:
type: added
---
title: Empty state for Code Review Analytics
merge_request: 25793
author:
type: added
---
title: Add endpoint for listing all deploy tokens for a project
merge_request: 25186
author:
type: added
---
title: Update DAST auto-deploy-image to v0.10.0
merge_request: 25922
author:
type: other
Loading
Loading
@@ -2,15 +2,14 @@
 
## List all deploy tokens
 
Get a list of all deploy tokens across all projects of the GitLab instance.
Get a list of all deploy tokens across the GitLab instance. This endpoint requires admin access.
 
>**Note:**
> This endpoint requires admin access.
```
```plaintext
GET /deploy_tokens
```
 
Example request:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/deploy_tokens"
```
Loading
Loading
@@ -24,7 +23,47 @@ Example response:
"name": "MyToken",
"username": "gitlab+deploy-token-1",
"expires_at": "2020-02-14T00:00:00.000Z",
"token": "jMRvtPNxrn3crTAGukpZ",
"scopes": [
"read_repository",
"read_registry"
]
}
]
```
## Project deploy tokens
Project deploy token API endpoints require project maintainer access or higher.
### List project deploy tokens
Get a list of a project's deploy tokens.
```plaintext
GET /projects/:id/deploy_tokens
```
Parameters:
| Attribute | Type | Required | Description |
|:---------------|:---------------|:---------|:-----------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
Example request:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/deploy_tokens"
```
Example response:
```json
[
{
"id": 1,
"name": "MyToken",
"username": "gitlab+deploy-token-1",
"expires_at": "2020-02-14T00:00:00.000Z",
"scopes": [
"read_repository",
"read_registry"
Loading
Loading
Loading
Loading
@@ -2609,12 +2609,12 @@ input EpicSetSubscriptionInput {
clientMutationId: String
 
"""
The group the epic to (un)subscribe is in
The group the epic to mutate is in
"""
groupPath: ID!
 
"""
The iid of the epic to (un)subscribe
The iid of the epic to mutate
"""
iid: ID!
 
Loading
Loading
@@ -7820,7 +7820,7 @@ input UpdateEpicInput {
"""
The iid of the epic to mutate
"""
iid: String!
iid: ID!
 
"""
The IDs of labels to be removed from the epic.
Loading
Loading
Loading
Loading
@@ -24409,6 +24409,20 @@
"description": "Autogenerated input type of UpdateEpic",
"fields": null,
"inputFields": [
{
"name": "iid",
"description": "The iid of the epic to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "groupPath",
"description": "The group the epic to mutate is in",
Loading
Loading
@@ -24519,20 +24533,6 @@
},
"defaultValue": null
},
{
"name": "iid",
"description": "The iid of the epic to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "stateEvent",
"description": "State event for the epic",
Loading
Loading
@@ -24863,8 +24863,8 @@
"fields": null,
"inputFields": [
{
"name": "groupPath",
"description": "The group the epic to (un)subscribe is in",
"name": "iid",
"description": "The iid of the epic to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
Loading
Loading
@@ -24877,8 +24877,8 @@
"defaultValue": null
},
{
"name": "iid",
"description": "The iid of the epic to (un)subscribe",
"name": "groupPath",
"description": "The group the epic to mutate is in",
"type": {
"kind": "NON_NULL",
"name": null,
Loading
Loading
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