summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js4
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue34
-rw-r--r--app/assets/javascripts/monitoring/components/embed.vue18
-rw-r--r--app/assets/javascripts/monitoring/constants.js22
-rw-r--r--app/assets/javascripts/monitoring/stores/actions.js2
-rw-r--r--app/assets/javascripts/monitoring/utils.js45
-rw-r--r--changelogs/unreleased/62973-specify-time-frame-in-shareable-link-for-embedding-metrics.yml5
-rw-r--r--changelogs/unreleased/sh-add-missing-csp-report-uri.yml5
-rw-r--r--doc/api/README.md147
-rw-r--r--doc/api/api_resources.md144
-rw-r--r--doc/api/projects.md10
-rw-r--r--lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml2
-rw-r--r--lib/gitlab/content_security_policy/config_loader.rb2
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js35
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js16
-rw-r--r--spec/javascripts/monitoring/store/actions_spec.js4
-rw-r--r--spec/javascripts/monitoring/utils_spec.js28
-rw-r--r--spec/lib/gitlab/content_security_policy/config_loader_spec.rb4
18 files changed, 307 insertions, 220 deletions
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 32fd0990374..1336b6a5461 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -2,8 +2,8 @@ import { join as joinPaths } from 'path';
// Returns an array containing the value(s) of the
// of the key passed as an argument
-export function getParameterValues(sParam) {
- const sPageURL = decodeURIComponent(window.location.search.substring(1));
+export function getParameterValues(sParam, url = window.location) {
+ const sPageURL = decodeURIComponent(new URL(url).search.substring(1));
return sPageURL.split('&').reduce((acc, urlParam) => {
const sParameterName = urlParam.split('=');
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 2feb545199b..5892c18ac91 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -18,8 +18,8 @@ import MonitorSingleStatChart from './charts/single_stat.vue';
import PanelType from './panel_type.vue';
import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue';
-import { sidebarAnimationDuration, timeWindows, timeWindowsKeyNames } from '../constants';
-import { getTimeDiff } from '../utils';
+import { sidebarAnimationDuration, timeWindows } from '../constants';
+import { getTimeDiff, getTimeWindow } from '../utils';
let sidebarMutationObserver;
@@ -147,6 +147,7 @@ export default {
selectedTimeWindow: '',
selectedTimeWindowKey: '',
formIsValid: null,
+ timeWindows: {},
};
},
computed: {
@@ -184,17 +185,6 @@ export default {
currentDashboard: this.currentDashboard,
projectPath: this.projectPath,
});
-
- this.timeWindows = timeWindows;
- this.selectedTimeWindowKey =
- _.escape(getParameterValues('time_window')[0]) || timeWindowsKeyNames.eightHours;
-
- // Set default time window if the selectedTimeWindowKey is bogus
- if (!Object.keys(this.timeWindows).includes(this.selectedTimeWindowKey)) {
- this.selectedTimeWindowKey = timeWindowsKeyNames.eightHours;
- }
-
- this.selectedTimeWindow = this.timeWindows[this.selectedTimeWindowKey];
},
beforeDestroy() {
if (sidebarMutationObserver) {
@@ -205,7 +195,20 @@ export default {
if (!this.hasMetrics) {
this.setGettingStartedEmptyState();
} else {
- this.fetchData(getTimeDiff(this.selectedTimeWindow));
+ const defaultRange = getTimeDiff();
+ const start = getParameterValues('start')[0] || defaultRange.start;
+ const end = getParameterValues('end')[0] || defaultRange.end;
+
+ const range = {
+ start,
+ end,
+ };
+
+ this.timeWindows = timeWindows;
+ this.selectedTimeWindowKey = getTimeWindow(range);
+ this.selectedTimeWindow = this.timeWindows[this.selectedTimeWindowKey];
+
+ this.fetchData(range);
sidebarMutationObserver = new MutationObserver(this.onSidebarMutation);
sidebarMutationObserver.observe(document.querySelector('.layout-page'), {
@@ -259,7 +262,8 @@ export default {
return this.timeWindows[key] === this.selectedTimeWindow;
},
setTimeWindowParameter(key) {
- return `?time_window=${key}`;
+ const { start, end } = getTimeDiff(key);
+ return `?start=${encodeURIComponent(start)}&end=${encodeURIComponent(end)}`;
},
groupHasData(group) {
return this.chartsWithData(group.metrics).length > 0;
diff --git a/app/assets/javascripts/monitoring/components/embed.vue b/app/assets/javascripts/monitoring/components/embed.vue
index e17f03de0fd..9e85b0633fe 100644
--- a/app/assets/javascripts/monitoring/components/embed.vue
+++ b/app/assets/javascripts/monitoring/components/embed.vue
@@ -1,8 +1,9 @@
<script>
import { mapActions, mapState } from 'vuex';
+import { getParameterValues, removeParams } from '~/lib/utils/url_utility';
import GraphGroup from './graph_group.vue';
import MonitorAreaChart from './charts/area.vue';
-import { sidebarAnimationDuration, timeWindowsKeyNames, timeWindows } from '../constants';
+import { sidebarAnimationDuration } from '../constants';
import { getTimeDiff } from '../utils';
let sidebarMutationObserver;
@@ -19,10 +20,17 @@ export default {
},
},
data() {
+ const defaultRange = getTimeDiff();
+ const start = getParameterValues('start', this.dashboardUrl)[0] || defaultRange.start;
+ const end = getParameterValues('end', this.dashboardUrl)[0] || defaultRange.end;
+
+ const params = {
+ start,
+ end,
+ };
+
return {
- params: {
- ...getTimeDiff(timeWindows[timeWindowsKeyNames.eightHours]),
- },
+ params,
elWidth: 0,
};
},
@@ -73,7 +81,7 @@ export default {
prometheusEndpointEnabled: true,
});
this.setEndpoints({
- dashboardEndpoint: this.dashboardUrl,
+ dashboardEndpoint: removeParams(['start', 'end'], this.dashboardUrl),
});
this.setShowErrorBanner(false);
},
diff --git a/app/assets/javascripts/monitoring/constants.js b/app/assets/javascripts/monitoring/constants.js
index 605c95e6da5..d7d89522732 100644
--- a/app/assets/javascripts/monitoring/constants.js
+++ b/app/assets/javascripts/monitoring/constants.js
@@ -21,11 +21,19 @@ export const timeWindows = {
oneWeek: __('1 week'),
};
-export const timeWindowsKeyNames = {
- thirtyMinutes: 'thirtyMinutes',
- threeHours: 'threeHours',
- eightHours: 'eightHours',
- oneDay: 'oneDay',
- threeDays: 'threeDays',
- oneWeek: 'oneWeek',
+export const secondsIn = {
+ thirtyMinutes: 60 * 30,
+ threeHours: 60 * 60 * 3,
+ eightHours: 60 * 60 * 8,
+ oneDay: 60 * 60 * 24 * 1,
+ threeDays: 60 * 60 * 24 * 3,
+ oneWeek: 60 * 60 * 24 * 7 * 1,
};
+
+export const timeWindowsKeyNames = Object.keys(secondsIn).reduce(
+ (otherTimeWindows, timeWindow) => ({
+ ...otherTimeWindows,
+ [timeWindow]: timeWindow,
+ }),
+ {},
+);
diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js
index 245cc2eaca3..0cbad179f17 100644
--- a/app/assets/javascripts/monitoring/stores/actions.js
+++ b/app/assets/javascripts/monitoring/stores/actions.js
@@ -151,7 +151,7 @@ function fetchPrometheusResult(prometheusEndpoint, params) {
*/
export const fetchPrometheusMetric = ({ commit }, { metric, params }) => {
const { start, end } = params;
- const timeDiff = end - start;
+ const timeDiff = (new Date(end) - new Date(start)) / 1000;
const minStep = 60;
const queryDataPoints = 600;
diff --git a/app/assets/javascripts/monitoring/utils.js b/app/assets/javascripts/monitoring/utils.js
index 478e2b3d06c..46b01f753f8 100644
--- a/app/assets/javascripts/monitoring/utils.js
+++ b/app/assets/javascripts/monitoring/utils.js
@@ -1,35 +1,24 @@
-import { timeWindows } from './constants';
+import { secondsIn, timeWindowsKeyNames } from './constants';
-/**
- * method that converts a predetermined time window to minutes
- * defaults to 8 hours as the default option
- * @param {String} timeWindow - The time window to convert to minutes
- * @returns {number} The time window in minutes
- */
-const getTimeDifferenceSeconds = timeWindow => {
- switch (timeWindow) {
- case timeWindows.thirtyMinutes:
- return 60 * 30;
- case timeWindows.threeHours:
- return 60 * 60 * 3;
- case timeWindows.oneDay:
- return 60 * 60 * 24 * 1;
- case timeWindows.threeDays:
- return 60 * 60 * 24 * 3;
- case timeWindows.oneWeek:
- return 60 * 60 * 24 * 7 * 1;
- default:
- return 60 * 60 * 8;
- }
-};
+export const getTimeDiff = timeWindow => {
+ const end = Math.floor(Date.now() / 1000); // convert milliseconds to seconds
+ const difference = secondsIn[timeWindow] || secondsIn.eightHours;
+ const start = end - difference;
-export const getTimeDiff = selectedTimeWindow => {
- const end = Date.now() / 1000; // convert milliseconds to seconds
- const start = end - getTimeDifferenceSeconds(selectedTimeWindow);
-
- return { start, end };
+ return {
+ start: new Date(start * 1000).toISOString(),
+ end: new Date(end * 1000).toISOString(),
+ };
};
+export const getTimeWindow = ({ start, end }) =>
+ Object.entries(secondsIn).reduce((acc, [timeRange, value]) => {
+ if (end - start === value) {
+ return timeRange;
+ }
+ return acc;
+ }, timeWindowsKeyNames.eightHours);
+
/**
* This method is used to validate if the graph data format for a chart component
* that needs a time series as a response from a prometheus query (query_range) is
diff --git a/changelogs/unreleased/62973-specify-time-frame-in-shareable-link-for-embedding-metrics.yml b/changelogs/unreleased/62973-specify-time-frame-in-shareable-link-for-embedding-metrics.yml
new file mode 100644
index 00000000000..aaf0ddfa48d
--- /dev/null
+++ b/changelogs/unreleased/62973-specify-time-frame-in-shareable-link-for-embedding-metrics.yml
@@ -0,0 +1,5 @@
+---
+title: Allow links to metrics dashboard at a specific time
+merge_request: 31283
+author:
+type: added
diff --git a/changelogs/unreleased/sh-add-missing-csp-report-uri.yml b/changelogs/unreleased/sh-add-missing-csp-report-uri.yml
new file mode 100644
index 00000000000..656eb8e9c37
--- /dev/null
+++ b/changelogs/unreleased/sh-add-missing-csp-report-uri.yml
@@ -0,0 +1,5 @@
+---
+title: Add missing report-uri to CSP config
+merge_request: 31593
+author:
+type: fixed
diff --git a/doc/api/README.md b/doc/api/README.md
index 9f23af3f723..0195ce3912a 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -4,149 +4,10 @@ Automate GitLab via a simple and powerful API.
The main GitLab API is a [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) API. Therefore, documentation in this section assumes knowledge of REST concepts.
-## API resources
-
-Available API resources can be grouped in the following contexts:
-
-- [Projects](#project-resources).
-- [Groups](#group-resources).
-- [Standalone](#standalone-resources).
-
-See also:
-
-- [V3 to V4](v3_to_v4.md).
-- Adding [deploy keys for multiple projects](deploy_key_multiple_projects.md).
-
-### Project resources
-
-The following API resources are available in the project context:
-
-| Resource | Available endpoints |
-|:--------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| [Access requests](access_requests.md) | `/projects/:id/access_requests` (also available for groups) |
-| [Award emoji](award_emoji.md) | `/projects/:id/issues/.../award_emoji`, `/projects/:id/merge_requests/.../award_emoji`, `/projects/:id/snippets/.../award_emoji` |
-| [Branches](branches.md) | `/projects/:id/repository/branches/`, `/projects/:id/repository/merged_branches` |
-| [Commits](commits.md) | `/projects/:id/repository/commits`, `/projects/:id/statuses` |
-| [Container Registry](container_registry.md) | `/projects/:id/registry/repositories` |
-| [Custom attributes](custom_attributes.md) | `/projects/:id/custom_attributes` (also available for groups and users) |
-| [Dependencies](dependencies.md) **(ULTIMATE)** | `/projects/:id/dependencies`
-| [Deploy keys](deploy_keys.md) | `/projects/:id/deploy_keys` (also available standalone) |
-| [Deployments](deployments.md) | `/projects/:id/deployments` |
-| [Discussions](discussions.md) (threaded comments) | `/projects/:id/issues/.../discussions`, `/projects/:id/snippets/.../discussions`, `/projects/:id/merge_requests/.../discussions`, `/projects/:id/commits/.../discussions` (also available for groups) |
-| [Environments](environments.md) | `/projects/:id/environments` |
-| [Events](events.md) | `/projects/:id/events` (also available for users and standalone) |
-| [Issues](issues.md) | `/projects/:id/issues` (also available for groups and standalone) |
-| [Issues Statistics](issues_statistics.md) | `/projects/:id/issues_statistics` (also available for groups and standalone) |
-| [Issue boards](boards.md) | `/projects/:id/boards` |
-| [Issue links](issue_links.md) **(STARTER)** | `/projects/:id/issues/.../links` |
-| [Jobs](jobs.md) | `/projects/:id/jobs`, `/projects/:id/pipelines/.../jobs` |
-| [Labels](labels.md) | `/projects/:id/labels` |
-| [Managed licenses](managed_licenses.md) **(ULTIMATE)** | `/projects/:id/managed_licenses` |
-| [Members](members.md) | `/projects/:id/members` (also available for groups) |
-| [Merge request approvals](merge_request_approvals.md) **(STARTER)** | `/projects/:id/approvals`, `/projects/:id/merge_requests/.../approvals` |
-| [Merge requests](merge_requests.md) | `/projects/:id/merge_requests` (also available for groups and standalone) |
-| [Notes](notes.md) (comments) | `/projects/:id/issues/.../notes`, `/projects/:id/snippets/.../notes`, `/projects/:id/merge_requests/.../notes` (also available for groups) |
-| [Notification settings](notification_settings.md) | `/projects/:id/notification_settings` (also available for groups and standalone) |
-| [Packages](packages.md) **(PREMIUM)** | `/projects/:id/packages` |
-| [Pages domains](pages_domains.md) | `/projects/:id/pages` (also available standalone) |
-| [Pipelines](pipelines.md) | `/projects/:id/pipelines` |
-| [Pipeline schedules](pipeline_schedules.md) | `/projects/:id/pipeline_schedules` |
-| [Pipeline triggers](pipeline_triggers.md) | `/projects/:id/triggers` |
-| [Projects](projects.md) including setting Webhooks | `/projects`, `/projects/:id/hooks` (also available for users) |
-| [Project badges](project_badges.md) | `/projects/:id/badges` |
-| [Project clusters](project_clusters.md) | `/projects/:id/clusters` |
-| [Project-level variables](project_level_variables.md) | `/projects/:id/variables` |
-| [Project import/export](project_import_export.md) | `/projects/:id/export`, `/projects/import`, `/projects/:id/import` |
-| [Project milestones](milestones.md) | `/projects/:id/milestones` |
-| [Project snippets](project_snippets.md) | `/projects/:id/snippets` |
-| [Project templates](project_templates.md) | `/projects/:id/templates` |
-| [Protected branches](protected_branches.md) | `/projects/:id/protected_branches` |
-| [Protected tags](protected_tags.md) | `/projects/:id/protected_tags` |
-| [Releases](releases/index.md) | `/projects/:id/releases` |
-| [Release links](releases/links.md) | `/projects/:id/releases/.../assets/links` |
-| [Repositories](repositories.md) | `/projects/:id/repository` |
-| [Repository files](repository_files.md) | `/projects/:id/repository/files` |
-| [Repository submodules](repository_submodules.md) | `/projects/:id/repository/submodules` |
-| [Resource label events](resource_label_events.md) | `/projects/:id/issues/.../resource_label_events`, `/projects/:id/merge_requests/.../resource_label_events` (also available for groups) |
-| [Runners](runners.md) | `/projects/:id/runners` (also available standalone) |
-| [Search](search.md) | `/projects/:id/search` (also available for groups and standalone) |
-| [Services](services.md) | `/projects/:id/services` |
-| [Tags](tags.md) | `/projects/:id/repository/tags` |
-| [Vulnerabilities](vulnerabilities.md) **(ULTIMATE)** | `/projects/:id/vulnerabilities` (also available for groups) |
-| [Wikis](wikis.md) | `/projects/:id/wikis` |
-
-### Group resources
-
-The following API resources are available in the group context:
-
-| Resource | Available endpoints |
-|:-----------------------------------------------------------------|:---------------------------------------------------------------------------------|
-| [Access requests](access_requests.md) | `/groups/:id/access_requests/` (also available for projects) |
-| [Custom attributes](custom_attributes.md) | `/groups/:id/custom_attributes` (also available for projects and users) |
-| [Discussions](discussions.md) (threaded comments) **(ULTIMATE)** | `/groups/:id/epics/.../discussions` (also available for projects) |
-| [Epic issues](epic_issues.md) **(ULTIMATE)** | `/groups/:id/epics/.../issues` |
-| [Epic links](epic_links.md) **(ULTIMATE)** | `/groups/:id/epics/.../epics` |
-| [Epics](epics.md) **(ULTIMATE)** | `/groups/:id/epics` |
-| [Groups](groups.md) | `/groups`, `/groups/.../subgroups` |
-| [Group badges](group_badges.md) | `/groups/:id/badges` |
-| [Group issue boards](group_boards.md) | `/groups/:id/boards` |
-| [Group labels](group_labels.md) | `/groups/:id/labels` |
-| [Group-level variables](group_level_variables.md) | `/groups/:id/variables` |
-| [Group milestones](group_milestones.md) | `/groups/:id/milestones` |
-| [Issues](issues.md) | `/groups/:id/issues` (also available for projects and standalone) |
-| [Issues Statistics](issues_statistics.md) | `/groups/:id/issues_statistics` (also available for projects and standalone) |
-| [Members](members.md) | `/groups/:id/members` (also available for projects) |
-| [Merge requests](merge_requests.md) | `/groups/:id/merge_requests` (also available for projects and standalone) |
-| [Notes](notes.md) (comments) | `/groups/:id/epics/.../notes` (also available for projects) |
-| [Notification settings](notification_settings.md) | `/groups/:id/notification_settings` (also available for projects and standalone) |
-| [Resource label events](resource_label_events.md) | `/groups/:id/epics/.../resource_label_events` (also available for projects) |
-| [Search](search.md) | `/groups/:id/search` (also available for projects and standalone) |
-
-### Standalone resources
-
-The following API resources are available outside of project and group contexts (including `/users`):
-
-| Resource | Available endpoints |
-|:--------------------------------------------------|:------------------------------------------------------------------------|
-| [Applications](applications.md) | `/applications` |
-| [Avatar](avatar.md) | `/avatar` |
-| [Broadcast messages](broadcast_messages.md) | `/broadcast_messages` |
-| [Code snippets](snippets.md) | `/snippets` |
-| [Custom attributes](custom_attributes.md) | `/users/:id/custom_attributes` (also available for groups and projects) |
-| [Deploy keys](deploy_keys.md) | `/deploy_keys` (also available for projects) |
-| [Events](events.md) | `/events`, `/users/:id/events` (also available for projects) |
-| [Feature flags](features.md) | `/features` |
-| [Geo Nodes](geo_nodes.md) **(PREMIUM ONLY)** | `/geo_nodes` |
-| [Import repository from GitHub](import.md) | `/import/github` |
-| [Issues](issues.md) | `/issues` (also available for groups and projects) |
-| [Issues Statistics](issues_statistics.md) | `/issues_statistics` (also available for groups and projects) |
-| [Keys](keys.md) | `/keys` |
-| [License](license.md) **(CORE ONLY)** | `/license` |
-| [Markdown](markdown.md) | `/markdown` |
-| [Merge requests](merge_requests.md) | `/merge_requests` (also available for groups and projects) |
-| [Namespaces](namespaces.md) | `/namespaces` |
-| [Notification settings](notification_settings.md) | `/notification_settings` (also available for groups and projects) |
-| [Pages domains](pages_domains.md) | `/pages/domains` (also available for projects) |
-| [Projects](projects.md) | `/users/:id/projects` (also available for projects) |
-| [Runners](runners.md) | `/runners` (also available for projects) |
-| [Search](search.md) | `/search` (also available for groups and projects) |
-| [Settings](settings.md) | `/application/settings` |
-| [Sidekiq metrics](sidekiq_metrics.md) | `/sidekiq` |
-| [Suggestions](suggestions.md) | `/suggestions` |
-| [System hooks](system_hooks.md) | `/hooks` |
-| [Todos](todos.md) | `/todos` |
-| [Users](users.md) | `/users` |
-| [Validate `.gitlab-ci.yml` file](lint.md) | `/lint` |
-| [Version](version.md) | `/version` |
-
-### Templates API resources
-
-Endpoints are available for:
-
-- [Dockerfile templates](templates/dockerfiles.md).
-- [`.gitignore` templates](templates/gitignores.md).
-- [GitLab CI YAML templates](templates/gitlab_ci_ymls.md).
-- [Open source license templates](templates/licenses.md).
+## Available API resources
+
+For a list of the available resources and their endpoints, see
+[API resources](api_resources.md).
## SCIM **(SILVER ONLY)**
diff --git a/doc/api/api_resources.md b/doc/api/api_resources.md
new file mode 100644
index 00000000000..b32f11464ef
--- /dev/null
+++ b/doc/api/api_resources.md
@@ -0,0 +1,144 @@
+# API resources
+
+Available resources for the [GitLab API](README.md) can be grouped in the following contexts:
+
+- [Projects](#project-resources).
+- [Groups](#group-resources).
+- [Standalone](#standalone-resources).
+
+See also:
+
+- [V3 to V4](v3_to_v4.md).
+- Adding [deploy keys for multiple projects](deploy_key_multiple_projects.md).
+- [API Resources for various templates](#templates-api-resources).
+
+## Project resources
+
+The following API resources are available in the project context:
+
+| Resource | Available endpoints |
+|:--------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [Access requests](access_requests.md) | `/projects/:id/access_requests` (also available for groups) |
+| [Award emoji](award_emoji.md) | `/projects/:id/issues/.../award_emoji`, `/projects/:id/merge_requests/.../award_emoji`, `/projects/:id/snippets/.../award_emoji` |
+| [Branches](branches.md) | `/projects/:id/repository/branches/`, `/projects/:id/repository/merged_branches` |
+| [Commits](commits.md) | `/projects/:id/repository/commits`, `/projects/:id/statuses` |
+| [Container Registry](container_registry.md) | `/projects/:id/registry/repositories` |
+| [Custom attributes](custom_attributes.md) | `/projects/:id/custom_attributes` (also available for groups and users) |
+| [Dependencies](dependencies.md) **(ULTIMATE)** | `/projects/:id/dependencies`
+| [Deploy keys](deploy_keys.md) | `/projects/:id/deploy_keys` (also available standalone) |
+| [Deployments](deployments.md) | `/projects/:id/deployments` |
+| [Discussions](discussions.md) (threaded comments) | `/projects/:id/issues/.../discussions`, `/projects/:id/snippets/.../discussions`, `/projects/:id/merge_requests/.../discussions`, `/projects/:id/commits/.../discussions` (also available for groups) |
+| [Environments](environments.md) | `/projects/:id/environments` |
+| [Events](events.md) | `/projects/:id/events` (also available for users and standalone) |
+| [Issues](issues.md) | `/projects/:id/issues` (also available for groups and standalone) |
+| [Issues Statistics](issues_statistics.md) | `/projects/:id/issues_statistics` (also available for groups and standalone) |
+| [Issue boards](boards.md) | `/projects/:id/boards` |
+| [Issue links](issue_links.md) **(STARTER)** | `/projects/:id/issues/.../links` |
+| [Jobs](jobs.md) | `/projects/:id/jobs`, `/projects/:id/pipelines/.../jobs` |
+| [Labels](labels.md) | `/projects/:id/labels` |
+| [Managed licenses](managed_licenses.md) **(ULTIMATE)** | `/projects/:id/managed_licenses` |
+| [Members](members.md) | `/projects/:id/members` (also available for groups) |
+| [Merge request approvals](merge_request_approvals.md) **(STARTER)** | `/projects/:id/approvals`, `/projects/:id/merge_requests/.../approvals` |
+| [Merge requests](merge_requests.md) | `/projects/:id/merge_requests` (also available for groups and standalone) |
+| [Notes](notes.md) (comments) | `/projects/:id/issues/.../notes`, `/projects/:id/snippets/.../notes`, `/projects/:id/merge_requests/.../notes` (also available for groups) |
+| [Notification settings](notification_settings.md) | `/projects/:id/notification_settings` (also available for groups and standalone) |
+| [Packages](packages.md) **(PREMIUM)** | `/projects/:id/packages` |
+| [Pages domains](pages_domains.md) | `/projects/:id/pages` (also available standalone) |
+| [Pipelines](pipelines.md) | `/projects/:id/pipelines` |
+| [Pipeline schedules](pipeline_schedules.md) | `/projects/:id/pipeline_schedules` |
+| [Pipeline triggers](pipeline_triggers.md) | `/projects/:id/triggers` |
+| [Projects](projects.md) including setting Webhooks | `/projects`, `/projects/:id/hooks` (also available for users) |
+| [Project badges](project_badges.md) | `/projects/:id/badges` |
+| [Project clusters](project_clusters.md) | `/projects/:id/clusters` |
+| [Project-level variables](project_level_variables.md) | `/projects/:id/variables` |
+| [Project import/export](project_import_export.md) | `/projects/:id/export`, `/projects/import`, `/projects/:id/import` |
+| [Project milestones](milestones.md) | `/projects/:id/milestones` |
+| [Project snippets](project_snippets.md) | `/projects/:id/snippets` |
+| [Project templates](project_templates.md) | `/projects/:id/templates` |
+| [Protected branches](protected_branches.md) | `/projects/:id/protected_branches` |
+| [Protected tags](protected_tags.md) | `/projects/:id/protected_tags` |
+| [Releases](releases/index.md) | `/projects/:id/releases` |
+| [Release links](releases/links.md) | `/projects/:id/releases/.../assets/links` |
+| [Repositories](repositories.md) | `/projects/:id/repository` |
+| [Repository files](repository_files.md) | `/projects/:id/repository/files` |
+| [Repository submodules](repository_submodules.md) | `/projects/:id/repository/submodules` |
+| [Resource label events](resource_label_events.md) | `/projects/:id/issues/.../resource_label_events`, `/projects/:id/merge_requests/.../resource_label_events` (also available for groups) |
+| [Runners](runners.md) | `/projects/:id/runners` (also available standalone) |
+| [Search](search.md) | `/projects/:id/search` (also available for groups and standalone) |
+| [Services](services.md) | `/projects/:id/services` |
+| [Tags](tags.md) | `/projects/:id/repository/tags` |
+| [Vulnerabilities](vulnerabilities.md) **(ULTIMATE)** | `/projects/:id/vulnerabilities` (also available for groups) |
+| [Wikis](wikis.md) | `/projects/:id/wikis` |
+
+## Group resources
+
+The following API resources are available in the group context:
+
+| Resource | Available endpoints |
+|:-----------------------------------------------------------------|:---------------------------------------------------------------------------------|
+| [Access requests](access_requests.md) | `/groups/:id/access_requests/` (also available for projects) |
+| [Custom attributes](custom_attributes.md) | `/groups/:id/custom_attributes` (also available for projects and users) |
+| [Discussions](discussions.md) (threaded comments) **(ULTIMATE)** | `/groups/:id/epics/.../discussions` (also available for projects) |
+| [Epic issues](epic_issues.md) **(ULTIMATE)** | `/groups/:id/epics/.../issues` |
+| [Epic links](epic_links.md) **(ULTIMATE)** | `/groups/:id/epics/.../epics` |
+| [Epics](epics.md) **(ULTIMATE)** | `/groups/:id/epics` |
+| [Groups](groups.md) | `/groups`, `/groups/.../subgroups` |
+| [Group badges](group_badges.md) | `/groups/:id/badges` |
+| [Group issue boards](group_boards.md) | `/groups/:id/boards` |
+| [Group labels](group_labels.md) | `/groups/:id/labels` |
+| [Group-level variables](group_level_variables.md) | `/groups/:id/variables` |
+| [Group milestones](group_milestones.md) | `/groups/:id/milestones` |
+| [Issues](issues.md) | `/groups/:id/issues` (also available for projects and standalone) |
+| [Issues Statistics](issues_statistics.md) | `/groups/:id/issues_statistics` (also available for projects and standalone) |
+| [Members](members.md) | `/groups/:id/members` (also available for projects) |
+| [Merge requests](merge_requests.md) | `/groups/:id/merge_requests` (also available for projects and standalone) |
+| [Notes](notes.md) (comments) | `/groups/:id/epics/.../notes` (also available for projects) |
+| [Notification settings](notification_settings.md) | `/groups/:id/notification_settings` (also available for projects and standalone) |
+| [Resource label events](resource_label_events.md) | `/groups/:id/epics/.../resource_label_events` (also available for projects) |
+| [Search](search.md) | `/groups/:id/search` (also available for projects and standalone) |
+
+## Standalone resources
+
+The following API resources are available outside of project and group contexts (including `/users`):
+
+| Resource | Available endpoints |
+|:--------------------------------------------------|:------------------------------------------------------------------------|
+| [Applications](applications.md) | `/applications` |
+| [Avatar](avatar.md) | `/avatar` |
+| [Broadcast messages](broadcast_messages.md) | `/broadcast_messages` |
+| [Code snippets](snippets.md) | `/snippets` |
+| [Custom attributes](custom_attributes.md) | `/users/:id/custom_attributes` (also available for groups and projects) |
+| [Deploy keys](deploy_keys.md) | `/deploy_keys` (also available for projects) |
+| [Events](events.md) | `/events`, `/users/:id/events` (also available for projects) |
+| [Feature flags](features.md) | `/features` |
+| [Geo Nodes](geo_nodes.md) **(PREMIUM ONLY)** | `/geo_nodes` |
+| [Import repository from GitHub](import.md) | `/import/github` |
+| [Issues](issues.md) | `/issues` (also available for groups and projects) |
+| [Issues Statistics](issues_statistics.md) | `/issues_statistics` (also available for groups and projects) |
+| [Keys](keys.md) | `/keys` |
+| [License](license.md) **(CORE ONLY)** | `/license` |
+| [Markdown](markdown.md) | `/markdown` |
+| [Merge requests](merge_requests.md) | `/merge_requests` (also available for groups and projects) |
+| [Namespaces](namespaces.md) | `/namespaces` |
+| [Notification settings](notification_settings.md) | `/notification_settings` (also available for groups and projects) |
+| [Pages domains](pages_domains.md) | `/pages/domains` (also available for projects) |
+| [Projects](projects.md) | `/users/:id/projects` (also available for projects) |
+| [Runners](runners.md) | `/runners` (also available for projects) |
+| [Search](search.md) | `/search` (also available for groups and projects) |
+| [Settings](settings.md) | `/application/settings` |
+| [Sidekiq metrics](sidekiq_metrics.md) | `/sidekiq` |
+| [Suggestions](suggestions.md) | `/suggestions` |
+| [System hooks](system_hooks.md) | `/hooks` |
+| [Todos](todos.md) | `/todos` |
+| [Users](users.md) | `/users` |
+| [Validate `.gitlab-ci.yml` file](lint.md) | `/lint` |
+| [Version](version.md) | `/version` |
+
+## Templates API resources
+
+Endpoints are available for:
+
+- [Dockerfile templates](templates/dockerfiles.md).
+- [`.gitignore` templates](templates/gitignores.md).
+- [GitLab CI YAML templates](templates/gitlab_ci_ymls.md).
+- [Open source license templates](templates/licenses.md).
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 8f68557edb3..70df44ec0fd 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -1909,18 +1909,20 @@ GET /projects/:id/push_rule
"author_email_regex": "",
"file_name_regex": "",
"max_file_size": 5,
- "commit_committer_check": false
+ "commit_committer_check": false,
+ "reject_unsigned_commits": false
}
```
Users on GitLab [Premium, Silver, or higher](https://about.gitlab.com/pricing/) will also see
-the `commit_committer_check` parameter:
+the `commit_committer_check` and `reject_unsigned_commits` parameters:
```json
{
"id": 1,
"project_id": 3,
- "commit_committer_check": false
+ "commit_committer_check": false,
+ "reject_unsigned_commits": false
...
}
```
@@ -1946,6 +1948,7 @@ POST /projects/:id/push_rule
| `file_name_regex` **(STARTER)** | string | no | All commited filenames must **not** match this, e.g. `(jar|exe)$` |
| `max_file_size` **(STARTER)** | integer | no | Maximum file size (MB) |
| `commit_committer_check` **(PREMIUM)** | boolean | no | Users can only push commits to this repository that were committed with one of their own verified emails. |
+| `reject_unsigned_commits` **(PREMIUM)** | boolean | no | Reject commit when it is not signed through GPG. |
### Edit project push rule
@@ -1968,6 +1971,7 @@ PUT /projects/:id/push_rule
| `file_name_regex` **(STARTER)** | string | no | All commited filenames must **not** match this, e.g. `(jar|exe)$` |
| `max_file_size` **(STARTER)** | integer | no | Maximum file size (MB) |
| `commit_committer_check` **(PREMIUM)** | boolean | no | Users can only push commits to this repository that were committed with one of their own verified emails. |
+| `reject_unsigned_commits` **(PREMIUM)** | boolean | no | Reject commits when they are not GPG signed. |
### Delete project push rule
diff --git a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
index c963d6ed1c4..2afc99d0bf8 100644
--- a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
@@ -25,6 +25,7 @@ container_scanning:
# https://hub.docker.com/r/arminc/clair-local-scan/tags
CLAIR_LOCAL_SCAN_VERSION: v2.0.8_0ed98e9ead65a51ba53f7cc53fa5e80c92169207
CLAIR_EXECUTABLE_VERSION: v12
+ CLAIR_EXECUTABLE_SHA: 44f2a3fdd7b0d102c98510e7586f6956edc89ab72c6943980f92f4979f7f4081
## Disable the proxy for clair-local-scan, otherwise Container Scanning will
## fail when a proxy is used.
NO_PROXY: ${DOCKER_SERVICE},localhost
@@ -44,6 +45,7 @@ container_scanning:
- apk add -U wget ca-certificates
- docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG}
- wget https://github.com/arminc/clair-scanner/releases/download/${CLAIR_EXECUTABLE_VERSION}/clair-scanner_linux_amd64
+ - echo "${CLAIR_EXECUTABLE_SHA} clair-scanner_linux_amd64" | sha256sum -c
- mv clair-scanner_linux_amd64 clair-scanner
- chmod +x clair-scanner
- touch clair-whitelist.yml
diff --git a/lib/gitlab/content_security_policy/config_loader.rb b/lib/gitlab/content_security_policy/config_loader.rb
index b2f3345d33a..ff844645b11 100644
--- a/lib/gitlab/content_security_policy/config_loader.rb
+++ b/lib/gitlab/content_security_policy/config_loader.rb
@@ -5,7 +5,7 @@ module Gitlab
class ConfigLoader
DIRECTIVES = %w(base_uri child_src connect_src default_src font_src
form_action frame_ancestors frame_src img_src manifest_src
- media_src object_src script_src style_src worker_src).freeze
+ media_src object_src report_uri script_src style_src worker_src).freeze
def self.default_settings_hash
{
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index c771984a137..a986bc49f28 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -34,6 +34,41 @@ describe('URL utility', () => {
});
});
+ describe('getParameterValues', () => {
+ beforeEach(() => {
+ setWindowLocation({
+ href: 'https://gitlab.com?test=passing&multiple=1&multiple=2',
+ // make our fake location act like real window.location.toString
+ // URL() (used in getParameterValues) does this if passed an object
+ toString() {
+ return this.href;
+ },
+ });
+ });
+
+ it('returns empty array for no params', () => {
+ expect(urlUtils.getParameterValues()).toEqual([]);
+ });
+
+ it('returns empty array for non-matching params', () => {
+ expect(urlUtils.getParameterValues('notFound')).toEqual([]);
+ });
+
+ it('returns single match', () => {
+ expect(urlUtils.getParameterValues('test')).toEqual(['passing']);
+ });
+
+ it('returns multiple matches', () => {
+ expect(urlUtils.getParameterValues('multiple')).toEqual(['1', '2']);
+ });
+
+ it('accepts url as second arg', () => {
+ const url = 'https://gitlab.com?everything=works';
+ expect(urlUtils.getParameterValues('everything', url)).toEqual(['works']);
+ expect(urlUtils.getParameterValues('test', url)).toEqual([]);
+ });
+ });
+
describe('mergeUrlParams', () => {
it('adds w', () => {
expect(urlUtils.mergeUrlParams({ w: 1 }, '#frag')).toBe('?w=1#frag');
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index d3e10194d92..36f650d5933 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -307,7 +307,7 @@ describe('Dashboard', () => {
});
spyOn(component.$store, 'dispatch').and.stub();
- const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff');
+ const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff').and.callThrough();
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
@@ -319,7 +319,7 @@ describe('Dashboard', () => {
Vue.nextTick()
.then(() => {
expect(component.$store.dispatch).toHaveBeenCalled();
- expect(getTimeDiffSpy).toHaveBeenCalledWith(component.selectedTimeWindow);
+ expect(getTimeDiffSpy).toHaveBeenCalled();
done();
})
@@ -327,7 +327,17 @@ describe('Dashboard', () => {
});
it('shows a specific time window selected from the url params', done => {
- spyOnDependency(Dashboard, 'getParameterValues').and.returnValue(['thirtyMinutes']);
+ const start = 1564439536;
+ const end = 1564441336;
+ spyOnDependency(Dashboard, 'getTimeDiff').and.returnValue({
+ start,
+ end,
+ });
+ spyOnDependency(Dashboard, 'getParameterValues').and.callFake(param => {
+ if (param === 'start') return [start];
+ if (param === 'end') return [end];
+ return [];
+ });
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
diff --git a/spec/javascripts/monitoring/store/actions_spec.js b/spec/javascripts/monitoring/store/actions_spec.js
index 677455275de..955a39e03a5 100644
--- a/spec/javascripts/monitoring/store/actions_spec.js
+++ b/spec/javascripts/monitoring/store/actions_spec.js
@@ -313,8 +313,8 @@ describe('Monitoring store actions', () => {
it('commits prometheus query result', done => {
const commit = jasmine.createSpy();
const params = {
- start: '1557216349.469',
- end: '1557218149.469',
+ start: '2019-08-06T12:40:02.184Z',
+ end: '2019-08-06T20:40:02.184Z',
};
const metric = metricsDashboardResponse.dashboard.panel_groups[0].panels[0].metrics[0];
const state = storeState();
diff --git a/spec/javascripts/monitoring/utils_spec.js b/spec/javascripts/monitoring/utils_spec.js
index 5570d57b8b2..e22e8cdc03d 100644
--- a/spec/javascripts/monitoring/utils_spec.js
+++ b/spec/javascripts/monitoring/utils_spec.js
@@ -3,28 +3,38 @@ import { timeWindows } from '~/monitoring/constants';
import { graphDataPrometheusQuery, graphDataPrometheusQueryRange } from './mock_data';
describe('getTimeDiff', () => {
+ function secondsBetween({ start, end }) {
+ return (new Date(end) - new Date(start)) / 1000;
+ }
+
+ function minutesBetween(timeRange) {
+ return secondsBetween(timeRange) / 60;
+ }
+
+ function hoursBetween(timeRange) {
+ return minutesBetween(timeRange) / 60;
+ }
+
it('defaults to an 8 hour (28800s) difference', () => {
const params = getTimeDiff();
- expect(params.end - params.start).toEqual(28800);
+ expect(hoursBetween(params)).toEqual(8);
});
it('accepts time window as an argument', () => {
- const params = getTimeDiff(timeWindows.thirtyMinutes);
+ const params = getTimeDiff('thirtyMinutes');
- expect(params.end - params.start).not.toEqual(28800);
+ expect(minutesBetween(params)).toEqual(30);
});
it('returns a value for every defined time window', () => {
const nonDefaultWindows = Object.keys(timeWindows).filter(window => window !== 'eightHours');
- nonDefaultWindows.forEach(window => {
- const params = getTimeDiff(timeWindows[window]);
- const diff = params.end - params.start;
+ nonDefaultWindows.forEach(timeWindow => {
+ const params = getTimeDiff(timeWindow);
- // Ensure we're not returning the default, 28800 (the # of seconds in 8 hrs)
- expect(diff).not.toEqual(28800);
- expect(typeof diff).toEqual('number');
+ // Ensure we're not returning the default
+ expect(hoursBetween(params)).not.toEqual(8);
});
});
});
diff --git a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
index e7670c9d523..1d404915617 100644
--- a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
+++ b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
@@ -13,7 +13,8 @@ describe Gitlab::ContentSecurityPolicy::ConfigLoader do
child_src: "'self' https://child.example.com",
default_src: "'self' https://other.example.com",
script_src: "'self' https://script.exammple.com ",
- worker_src: "data: https://worker.example.com"
+ worker_src: "data: https://worker.example.com",
+ report_uri: "http://example.com"
}
}
end
@@ -46,6 +47,7 @@ describe Gitlab::ContentSecurityPolicy::ConfigLoader do
expect(policy.directives['default-src']).to eq(expected_config(:default_src))
expect(policy.directives['child-src']).to eq(expected_config(:child_src))
expect(policy.directives['worker-src']).to eq(expected_config(:worker_src))
+ expect(policy.directives['report-uri']).to eq(expected_config(:report_uri))
end
it 'ignores malformed policy statements' do