Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-04-14 18:09:18 +00:00
parent a3dfd311f4
commit 844e3ef899
35 changed files with 628 additions and 145 deletions

View File

@ -0,0 +1,59 @@
<script>
import { s__ } from '~/locale';
import IntegrationsTable from './integrations_table.vue';
export default {
name: 'IntegrationsList',
components: {
IntegrationsTable,
},
props: {
integrations: {
type: Array,
required: true,
},
},
computed: {
integrationsGrouped() {
return this.integrations.reduce(
(integrations, integration) => {
if (integration.active) {
integrations.active.push(integration);
} else {
integrations.inactive.push(integration);
}
return integrations;
},
{ active: [], inactive: [] },
);
},
},
i18n: {
activeTableEmptyText: s__("Integrations|You haven't activated any integrations yet."),
inactiveTableEmptyText: s__("Integrations|You've activated every integration 🎉"),
activeIntegrationsHeading: s__('Integrations|Active integrations'),
inactiveIntegrationsHeading: s__('Integrations|Add an integration'),
},
};
</script>
<template>
<div>
<h4>{{ $options.i18n.activeIntegrationsHeading }}</h4>
<integrations-table
class="gl-mb-7!"
:integrations="integrationsGrouped.active"
:empty-text="$options.i18n.activeTableEmptyText"
show-updated-at
data-testid="active-integrations-table"
/>
<h4>{{ $options.i18n.inactiveIntegrationsHeading }}</h4>
<integrations-table
:integrations="integrationsGrouped.inactive"
:empty-text="$options.i18n.inactiveTableEmptyText"
data-testid="inactive-integrations-table"
/>
</div>
</template>

View File

@ -0,0 +1,95 @@
<script>
import { GlIcon, GlLink, GlTable, GlTooltipDirective } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
components: {
GlIcon,
GlLink,
GlTable,
TimeAgoTooltip,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
integrations: {
type: Array,
required: true,
},
showUpdatedAt: {
type: Boolean,
required: false,
default: false,
},
emptyText: {
type: String,
required: false,
default: undefined,
},
},
computed: {
fields() {
return [
{
key: 'active',
label: '',
thClass: 'gl-w-10',
},
{
key: 'title',
label: __('Integration'),
thClass: 'gl-w-quarter',
},
{
key: 'description',
label: __('Description'),
thClass: 'gl-display-none d-sm-table-cell',
tdClass: 'gl-display-none d-sm-table-cell',
},
{
key: 'updated_at',
label: this.showUpdatedAt ? __('Last updated') : '',
thClass: 'gl-w-20p',
},
];
},
},
methods: {
getStatusTooltipTitle(integration) {
return sprintf(s__('Integrations|%{integrationTitle}: active'), {
integrationTitle: integration.title,
});
},
},
};
</script>
<template>
<gl-table :items="integrations" :fields="fields" :empty-text="emptyText" show-empty fixed>
<template #cell(active)="{ item }">
<gl-icon
v-if="item.active"
v-gl-tooltip
name="check"
class="gl-text-green-500"
:title="getStatusTooltipTitle(item)"
/>
</template>
<template #cell(title)="{ item }">
<gl-link
:href="item.edit_path"
class="gl-font-weight-bold"
:data-qa-selector="`${item.name}_link`"
>
{{ item.title }}
</gl-link>
</template>
<template #cell(updated_at)="{ item }">
<time-ago-tooltip v-if="showUpdatedAt && item.updated_at" :time="item.updated_at" />
</template>
</gl-table>
</template>

View File

@ -0,0 +1,23 @@
import Vue from 'vue';
import IntegrationList from './components/integrations_list.vue';
export default () => {
const el = document.querySelector('.js-integrations-list');
if (!el) {
return null;
}
const { integrations } = el.dataset;
return new Vue({
el,
render(createElement) {
return createElement(IntegrationList, {
props: {
integrations: JSON.parse(integrations),
},
});
},
});
};

View File

@ -0,0 +1,3 @@
import initIntegrationsList from '~/integrations/index';
initIntegrationsList();

View File

@ -1,4 +1,7 @@
import initIntegrationsList from '~/integrations/index';
import PersistentUserCallout from '~/persistent_user_callout';
const callout = document.querySelector('.js-webhooks-moved-alert');
PersistentUserCallout.factory(callout);
initIntegrationsList();

View File

@ -115,6 +115,12 @@ module ServicesHelper
form_data
end
def integration_list_data(integrations)
{
integrations: integrations.map { |i| serialize_integration(i) }.to_json
}
end
def trigger_events_for_service(integration)
ServiceEventSerializer.new(service: integration).represent(integration.configurable_events).to_json
end
@ -155,6 +161,17 @@ module ServicesHelper
'project'
end
end
def serialize_integration(integration)
{
active: integration.operating?,
title: integration.title,
description: integration.description,
updated_at: integration.updated_at,
edit_path: scoped_edit_integration_path(integration),
name: integration.to_param
}
end
end
ServicesHelper.prepend_if_ee('EE::ServicesHelper')

View File

@ -72,7 +72,7 @@ module Pages
path: File.join(project.full_path, 'public/')
}
rescue LegacyStorageDisabledError => e
Gitlab::ErrorTracking.track_exception(e)
Gitlab::ErrorTracking.track_exception(e, project_id: project.id)
nil
end

View File

@ -79,21 +79,25 @@ class DroneCiService < CiService
end
def title
'Drone CI'
'Drone'
end
def description
'Drone is a Continuous Integration platform built on Docker, written in Go'
s_('ProjectService|Run CI/CD pipelines with Drone.')
end
def self.to_param
'drone_ci'
end
def help
s_('ProjectService|Run CI/CD pipelines with Drone.')
end
def fields
[
{ type: 'text', name: 'token', placeholder: 'Drone CI project specific token', required: true },
{ type: 'text', name: 'drone_url', title: s_('ProjectService|Drone URL'), placeholder: 'http://drone.example.com', required: true },
{ type: 'text', name: 'token', help: s_('ProjectService|Token for the Drone project.'), required: true },
{ type: 'text', name: 'drone_url', title: s_('ProjectService|Drone server URL'), placeholder: 'http://drone.example.com', required: true },
{ type: 'checkbox', name: 'enable_ssl_verification', title: "Enable SSL verification" }
]
end

View File

@ -13,8 +13,8 @@
.gl-alert-actions
= link_to s_('AdminSettings|Go to General Settings'), general_admin_application_settings_path, class: 'btn gl-alert-action btn-info gl-button'
%h4= s_('AdminSettings|Apply integration settings to all Projects')
%p
= s_('AdminSettings|Integrations configured here will automatically apply to all projects on this instance.')
= link_to _('Learn more'), integrations_help_page_path, target: '_blank', rel: 'noopener noreferrer'
%h3= s_('Integrations|Project integration management')
- integrations_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: integrations_help_page_path }
%p= s_('Integrations|As a GitLab administrator, you can set default configuration parameters for a given integration that all projects can inherit and use. When you set these parameters, your changes update the integration for all projects that are not already using custom settings. Learn more about %{integrations_link_start}Project integration management%{link_end}.').html_safe % { integrations_link_start: integrations_link_start, link_end: '</a>'.html_safe }
= render 'shared/integrations/index', integrations: @integrations

View File

@ -2,8 +2,8 @@
- page_title _('Integrations')
- @content_class = 'limit-container-width' unless fluid_layout
%h4= s_('GroupSettings|Apply integration settings to all Projects')
%p
= s_('GroupSettings|Integrations configured here will automatically apply to all projects in this group.')
= link_to _('Learn more'), integrations_help_page_path, target: '_blank', rel: 'noopener noreferrer'
%h3= s_('Integrations|Project integration management')
- integrations_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: integrations_help_page_path }
%p= s_('Integrations|As a GitLab administrator, you can set default configuration parameters for a given integration that all projects can inherit and use. When you set these parameters, your changes update the integration for all projects that are not already using custom settings. Learn more about %{integrations_link_start}Project integration management%{link_end}.').html_safe % { integrations_link_start: integrations_link_start, link_end: '</a>'.html_safe }
= render 'shared/integrations/index', integrations: @integrations

View File

@ -12,7 +12,7 @@
.gl-alert-actions
= link_to _('Go to Webhooks'), project_hooks_path(@project), class: 'gl-button btn gl-alert-action btn-info'
%h4= _('Integrations')
%h3= _('Integrations')
- integrations_link_start = '<a href="%{url}">'.html_safe % { url: help_page_url('user/project/integrations/overview') }
- webhooks_link_start = '<a href="%{url}">'.html_safe % { url: project_hooks_path(@project) }
%p= _("%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, webhooks_link_start: webhooks_link_start, link_end: '</a>'.html_safe }

View File

@ -1,27 +1 @@
%table.table.b-table.gl-table{ role: 'table', 'aria-busy': false, 'aria-colcount': 4 }
%colgroup
%col
%col
%col.d-none.d-sm-table-column
%col{ width: 135 }
%thead{ role: 'rowgroup' }
%tr{ role: 'row' }
%th{ role: 'columnheader', scope: 'col', 'aria-colindex': 1 }
%th{ role: 'columnheader', scope: 'col', 'aria-colindex': 2 }= _('Integration')
%th.d-none.d-sm-block{ role: 'columnheader', scope: 'col', 'aria-colindex': 3 }= _('Description')
%th{ role: 'columnheader', scope: 'col', 'aria-colindex': 4 }= _('Last updated')
%tbody{ role: 'rowgroup' }
- integrations.each do |integration|
- activated_label = (integration.activated? ? s_("ProjectService|%{service_title}: status on") : s_("ProjectService|%{service_title}: status off")) % { service_title: integration.title }
%tr{ role: 'row' }
%td{ role: 'cell', 'aria-colindex': 1, 'aria-label': activated_label, title: activated_label }
- if integration.operating?
= sprite_icon('check', css_class: 'gl-text-green-500')
%td{ role: 'cell', 'aria-colindex': 2 }
= link_to integration.title, scoped_edit_integration_path(integration), class: 'gl-font-weight-bold', data: { qa_selector: "#{integration.to_param}_link" }
%td.d-none.d-sm-table-cell{ role: 'cell', 'aria-colindex': 3 }
= integration.description
%td{ role: 'cell', 'aria-colindex': 4 }
- if integration.updated_at.present?
= time_ago_with_tooltip integration.updated_at
.js-integrations-list{ data: integration_list_data(integrations) }

View File

@ -0,0 +1,5 @@
---
title: Return 403 status code to the Runner when CI Job is deleted
merge_request: 59382
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Add index on (created_at, web_hook_id) to the partitioned web_hook_logs
merge_request: 59261
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Update drone integration UI text
merge_request: 59231
author:
type: other

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292897
milestone: '13.7'
type: development
group: group::dynamic analysis
default_enabled: false
default_enabled: true

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class AddCreatedAtWebHookIdIndexToPartitionedWebHookLog < ActiveRecord::Migration[6.0]
include Gitlab::Database::PartitioningMigrationHelpers
DOWNTIME = false
CREATED_AT_WEB_HOOK_ID_INDEX_NAME = 'index_web_hook_logs_part_on_created_at_and_web_hook_id'
disable_ddl_transaction!
def up
add_concurrent_partitioned_index :web_hook_logs_part_0c5294f417,
[:created_at, :web_hook_id],
name: CREATED_AT_WEB_HOOK_ID_INDEX_NAME
end
def down
remove_concurrent_partitioned_index_by_name :web_hook_logs_part_0c5294f417, CREATED_AT_WEB_HOOK_ID_INDEX_NAME
end
end

View File

@ -0,0 +1 @@
fe98a617ac8bacf270425c1e9b9b60aee1c3c0e47d5c915fe122cb99c1c1c822

View File

@ -24260,6 +24260,8 @@ CREATE INDEX index_web_hook_logs_on_created_at_and_web_hook_id ON web_hook_logs
CREATE INDEX index_web_hook_logs_on_web_hook_id ON web_hook_logs USING btree (web_hook_id);
CREATE INDEX index_web_hook_logs_part_on_created_at_and_web_hook_id ON ONLY web_hook_logs_part_0c5294f417 USING btree (created_at, web_hook_id);
CREATE INDEX index_web_hook_logs_part_on_web_hook_id ON ONLY web_hook_logs_part_0c5294f417 USING btree (web_hook_id);
CREATE INDEX index_web_hooks_on_group_id ON web_hooks USING btree (group_id) WHERE ((type)::text = 'GroupHook'::text);

View File

@ -312,6 +312,67 @@ Example response:
]
```
## List memberships for a billable member of a group
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/321560) in GitLab 13.11.
Gets a list of memberships for a billable member of a group.
Lists all projects and groups a user is a member of. Only projects and groups within the group hierarchy are included.
For instance, if the requested group is `Root Group`, and the requested user is a direct member of both `Root Group / Sub Group One` and `Other Group / Sub Group Two`, then only `Root Group / Sub Group One` will be returned, because `Other Group / Sub Group Two` is not within the `Root Group` hierarchy.
The response represents only direct memberships. Inherited memberships are not included.
This API endpoint works on top-level groups only. It does not work on subgroups.
This API endpoint requires permission to admin memberships for the group.
This API endpoint takes [pagination](README.md#pagination) parameters `page` and `per_page` to restrict the list of memberships.
```plaintext
GET /groups/:id/billable_members/:user_id/memberships
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `user_id` | integer | yes | The user ID of the billable member |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/billable_members/:user_id/memberships"
```
Example response:
```json
[
{
"id": 168,
"source_id": 131,
"source_full_name": "Root Group / Sub Group One",
"source_members_url": "https://gitlab.example.com/groups/root-group/sub-group-one/-/group_members",
"created_at": "2021-03-31T17:28:44.812Z",
"expires_at": "2022-03-21",
"access_level": {
"string_value": "Developer",
"integer_value": 30
}
},
{
"id": 169,
"source_id": 63,
"source_full_name": "Root Group / Sub Group One / My Project",
"source_members_url": "https://gitlab.example.com/root-group/sub-group-one/my-project/-/project_members",
"created_at": "2021-03-31T17:29:14.934Z",
"expires_at": null,
"access_level": {
"string_value": "Maintainer",
"integer_value": 40
}
}
]
```
## Remove a billable member from a group
Removes a billable member from a group and its subgroups and projects.

View File

@ -3169,6 +3169,76 @@ artifacts are restored after [caches](#cache).
[Read more about artifacts](../pipelines/job_artifacts.md).
#### `dependencies`
By default, all `artifacts` from previous stages
are passed to each job. However, you can use the `dependencies` keyword to
define a limited list of jobs to fetch artifacts from. You can also set a job to download no artifacts at all.
To use this feature, define `dependencies` in context of the job and pass
a list of all previous jobs the artifacts should be downloaded from.
You can define jobs from stages that were executed before the current one.
An error occurs if you define jobs from the current or an upcoming stage.
To prevent a job from downloading artifacts, define an empty array.
When you use `dependencies`, the status of the previous job is not considered.
If a job fails or it's a manual job that isn't triggered, no error occurs.
The following example defines two jobs with artifacts: `build:osx` and
`build:linux`. When the `test:osx` is executed, the artifacts from `build:osx`
are downloaded and extracted in the context of the build. The same happens
for `test:linux` and artifacts from `build:linux`.
The job `deploy` downloads artifacts from all previous jobs because of
the [stage](#stages) precedence:
```yaml
build:osx:
stage: build
script: make build:osx
artifacts:
paths:
- binaries/
build:linux:
stage: build
script: make build:linux
artifacts:
paths:
- binaries/
test:osx:
stage: test
script: make test:osx
dependencies:
- build:osx
test:linux:
stage: test
script: make test:linux
dependencies:
- build:linux
deploy:
stage: deploy
script: make deploy
```
##### When a dependent job fails
> Introduced in GitLab 10.3.
If the artifacts of the job that is set as a dependency are
[expired](#artifactsexpire_in) or
[erased](../pipelines/job_artifacts.md#erase-job-artifacts), then
the dependent job fails.
You can ask your administrator to
[flip this switch](../../administration/job_artifacts.md#validation-for-dependencies)
and bring back the old behavior.
#### `artifacts:exclude`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15122) in GitLab 13.1
@ -3717,76 +3787,6 @@ plan report uploads to GitLab as an artifact and displays
in merge requests. For more information, see
[Output `terraform plan` information into a merge request](../../user/infrastructure/mr_integration.md).
##### `dependencies`
By default, all `artifacts` from previous stages
are passed to each job. However, you can use the `dependencies` keyword to
define a limited list of jobs to fetch artifacts from. You can also set a job to download no artifacts at all.
To use this feature, define `dependencies` in context of the job and pass
a list of all previous jobs the artifacts should be downloaded from.
You can define jobs from stages that were executed before the current one.
An error occurs if you define jobs from the current or an upcoming stage.
To prevent a job from downloading artifacts, define an empty array.
When you use `dependencies`, the status of the previous job is not considered.
If a job fails or it's a manual job that isn't triggered, no error occurs.
The following example defines two jobs with artifacts: `build:osx` and
`build:linux`. When the `test:osx` is executed, the artifacts from `build:osx`
are downloaded and extracted in the context of the build. The same happens
for `test:linux` and artifacts from `build:linux`.
The job `deploy` downloads artifacts from all previous jobs because of
the [stage](#stages) precedence:
```yaml
build:osx:
stage: build
script: make build:osx
artifacts:
paths:
- binaries/
build:linux:
stage: build
script: make build:linux
artifacts:
paths:
- binaries/
test:osx:
stage: test
script: make test:osx
dependencies:
- build:osx
test:linux:
stage: test
script: make test:linux
dependencies:
- build:linux
deploy:
stage: deploy
script: make deploy
```
###### When a dependent job fails
> Introduced in GitLab 10.3.
If the artifacts of the job that is set as a dependency are
[expired](#artifactsexpire_in) or
[erased](../pipelines/job_artifacts.md#erase-job-artifacts), then
the dependent job fails.
You can ask your administrator to
[flip this switch](../../administration/job_artifacts.md#validation-for-dependencies)
and bring back the old behavior.
#### `artifacts:untracked`
Use `artifacts:untracked` to add all Git untracked files as artifacts (along

View File

@ -940,6 +940,14 @@ A site profile contains the following:
- **Profile name**: A name you assign to the site to be scanned.
- **Target URL**: The URL that DAST runs against.
- **Excluded URLs**: A comma-separated list of URLs to exclude from the scan.
- **Request headers**: A comma-separated list of HTTP request headers, including names and values. These headers are added to every request made by DAST.
- **Authentication**:
- **Authenticated URL**: The URL of the page containing the sign-in HTML form on the target website. The username and password are submitted with the login form to create an authenticated scan.
- **Username**: The username used to authenticate to the website.
- **Password**: The password used to authenticate to the website.
- **Username form field**: The name of username field at the sign-in HTML form.
- **Password form field**: The name of password field at the sign-in HTML form.
#### Site profile validation

View File

@ -38,10 +38,17 @@ module API
end
end
# HTTP status codes to terminate the job on GitLab Runner:
# - 403
def authenticate_job!(require_running: true)
job = current_job
not_found! unless job
# 404 is not returned here because we want to terminate the job if it's
# running. A 404 can be returned from anywhere in the networking stack which is why
# we are explicit about a 403, we should improve this in
# https://gitlab.com/gitlab-org/gitlab/-/issues/327703
forbidden! unless job
forbidden! unless job_token_valid?(job)
forbidden!('Project has been deleted!') if job.project.nil? || job.project.pending_delete?

View File

@ -2285,9 +2285,6 @@ msgstr ""
msgid "AdminProjects|Delete Project %{projectName}?"
msgstr ""
msgid "AdminSettings|Apply integration settings to all Projects"
msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
@ -2306,9 +2303,6 @@ msgstr ""
msgid "AdminSettings|Go to General Settings"
msgstr ""
msgid "AdminSettings|Integrations configured here will automatically apply to all projects on this instance."
msgstr ""
msgid "AdminSettings|Keep the latest artifacts for all jobs in the latest successful pipelines"
msgstr ""
@ -10142,7 +10136,7 @@ msgstr ""
msgid "DastProfiles|URL"
msgstr ""
msgid "DastProfiles|URLs to skip during the authenticated scan. Use regular expression syntax to match multiple URLs."
msgid "DastProfiles|URLs to skip during the authenticated scan."
msgstr ""
msgid "DastProfiles|Username"
@ -15457,9 +15451,6 @@ msgstr ""
msgid "GroupSettings|Allow project access token creation"
msgstr ""
msgid "GroupSettings|Apply integration settings to all Projects"
msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
@ -15508,9 +15499,6 @@ msgstr ""
msgid "GroupSettings|If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility."
msgstr ""
msgid "GroupSettings|Integrations configured here will automatically apply to all projects in this group."
msgstr ""
msgid "GroupSettings|Learn more about group-level project templates."
msgstr ""
@ -17068,12 +17056,21 @@ msgstr ""
msgid "Integrations"
msgstr ""
msgid "Integrations|%{integrationTitle}: active"
msgstr ""
msgid "Integrations|%{integration} settings saved and active."
msgstr ""
msgid "Integrations|%{integration} settings saved, but not active."
msgstr ""
msgid "Integrations|Active integrations"
msgstr ""
msgid "Integrations|Add an integration"
msgstr ""
msgid "Integrations|Add namespace"
msgstr ""
@ -17086,6 +17083,9 @@ msgstr ""
msgid "Integrations|All projects inheriting these settings will also be reset."
msgstr ""
msgid "Integrations|As a GitLab administrator, you can set default configuration parameters for a given integration that all projects can inherit and use. When you set these parameters, your changes update the integration for all projects that are not already using custom settings. Learn more about %{integrations_link_start}Project integration management%{link_end}."
msgstr ""
msgid "Integrations|Browser limitations"
msgstr ""
@ -17149,6 +17149,9 @@ msgstr ""
msgid "Integrations|Note: this integration only works with accounts on GitLab.com (SaaS)."
msgstr ""
msgid "Integrations|Project integration management"
msgstr ""
msgid "Integrations|Projects using custom settings will not be affected."
msgstr ""
@ -17203,12 +17206,18 @@ msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr ""
msgid "Integrations|You haven't activated any integrations yet."
msgstr ""
msgid "Integrations|You must have owner or maintainer permissions to link namespaces."
msgstr ""
msgid "Integrations|You should now see GitLab.com activity inside your Jira Cloud issues. %{linkStart}Learn more%{linkEnd}"
msgstr ""
msgid "Integrations|You've activated every integration 🎉"
msgstr ""
msgid "Interactive mode"
msgstr ""
@ -24590,13 +24599,7 @@ msgstr ""
msgid "ProjectSelect|Search for project"
msgstr ""
msgid "ProjectService|%{service_title}: status off"
msgstr ""
msgid "ProjectService|%{service_title}: status on"
msgstr ""
msgid "ProjectService|Drone URL"
msgid "ProjectService|Drone server URL"
msgstr ""
msgid "ProjectService|Enter new API key"
@ -24632,12 +24635,18 @@ msgstr ""
msgid "ProjectService|Perform common operations on GitLab project: %{project_name}"
msgstr ""
msgid "ProjectService|Run CI/CD pipelines with Drone."
msgstr ""
msgid "ProjectService|TeamCity URL"
msgstr ""
msgid "ProjectService|To configure this integration, you should:"
msgstr ""
msgid "ProjectService|Token for the Drone project."
msgstr ""
msgid "ProjectService|Trigger event for new comments on confidential issues."
msgstr ""

View File

@ -5,9 +5,9 @@ module QA
module Project
module Settings
class Integrations < QA::Page::Base
view 'app/views/shared/integrations/_index.html.haml' do
element :prometheus_link, 'data: { qa_selector: "#{integration.to_param' # rubocop:disable QA/ElementWithPattern
element :jira_link, 'data: { qa_selector: "#{integration.to_param' # rubocop:disable QA/ElementWithPattern
view 'app/assets/javascripts/integrations/index/components/integrations_table.vue' do
element :prometheus_link, %q(:data-qa-selector="`${item.name}_link`") # rubocop:disable QA/ElementWithPattern
element :jira_link, %q(:data-qa-selector="`${item.name}_link`") # rubocop:disable QA/ElementWithPattern
end
def click_on_prometheus_integration

View File

@ -24,7 +24,7 @@ RSpec.describe 'User searches group settings', :js do
visit group_settings_integrations_path(group)
end
it_behaves_like 'can highlight results', 'integration settings'
it_behaves_like 'can highlight results', 'set default configuration'
end
context 'in Repository page' do

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'User views services' do
RSpec.describe 'User views services', :js do
include_context 'project service activation'
it 'shows the list of available services' do

View File

@ -0,0 +1,26 @@
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import IntegrationsList from '~/integrations/index/components/integrations_list.vue';
import { mockActiveIntegrations, mockInactiveIntegrations } from '../mock_data';
describe('IntegrationsList', () => {
let wrapper;
const findActiveIntegrationsTable = () => wrapper.findByTestId('active-integrations-table');
const findInactiveIntegrationsTable = () => wrapper.findByTestId('inactive-integrations-table');
const createComponent = (propsData = {}) => {
wrapper = extendedWrapper(shallowMount(IntegrationsList, { propsData }));
};
afterEach(() => {
wrapper.destroy();
});
it('provides correct `integrations` prop to the IntegrationsTable instance', () => {
createComponent({ integrations: [...mockInactiveIntegrations, ...mockActiveIntegrations] });
expect(findActiveIntegrationsTable().props('integrations')).toEqual(mockActiveIntegrations);
expect(findInactiveIntegrationsTable().props('integrations')).toEqual(mockInactiveIntegrations);
});
});

View File

@ -0,0 +1,53 @@
import { GlTable, GlIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import IntegrationsTable from '~/integrations/index/components/integrations_table.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { mockActiveIntegrations, mockInactiveIntegrations } from '../mock_data';
describe('IntegrationsTable', () => {
let wrapper;
const findTable = () => wrapper.findComponent(GlTable);
const createComponent = (propsData = {}) => {
wrapper = mount(IntegrationsTable, {
propsData: {
integrations: mockActiveIntegrations,
...propsData,
},
});
};
afterEach(() => {
wrapper.destroy();
});
describe.each([true, false])('when `showUpdatedAt` is %p', (showUpdatedAt) => {
beforeEach(() => {
createComponent({ showUpdatedAt });
});
it(`${showUpdatedAt ? 'renders' : 'does not render'} content in "Last updated" column`, () => {
const headers = findTable().findAll('th');
expect(headers.wrappers.some((header) => header.text() === 'Last updated')).toBe(
showUpdatedAt,
);
expect(wrapper.findComponent(TimeAgoTooltip).exists()).toBe(showUpdatedAt);
});
});
describe.each`
scenario | integrations | shouldRenderActiveIcon
${'when integration is active'} | ${[mockActiveIntegrations[0]]} | ${true}
${'when integration is inactive'} | ${[mockInactiveIntegrations[0]]} | ${false}
`('$scenario', ({ shouldRenderActiveIcon, integrations }) => {
beforeEach(() => {
createComponent({ integrations });
});
it(`${shouldRenderActiveIcon ? 'renders' : 'does not render'} icon in first column`, () => {
expect(findTable().findComponent(GlIcon).exists()).toBe(shouldRenderActiveIcon);
});
});
});

View File

@ -0,0 +1,50 @@
export const mockActiveIntegrations = [
{
active: true,
title: 'Asana',
description: 'Asana - Teamwork without email',
updated_at: '2021-03-18T00:27:09.634Z',
edit_path:
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/asana/edit',
name: 'asana',
},
{
active: true,
title: 'Jira',
description: 'Jira issue tracker',
updated_at: '2021-01-29T06:41:25.806Z',
edit_path:
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/jira/edit',
name: 'jira',
},
];
export const mockInactiveIntegrations = [
{
active: false,
title: 'Webex Teams',
description: 'Receive event notifications in Webex Teams',
updated_at: null,
edit_path:
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/webex_teams/edit',
name: 'webex_teams',
},
{
active: false,
title: 'YouTrack',
description: 'YouTrack issue tracker',
updated_at: null,
edit_path:
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/youtrack/edit',
name: 'youtrack',
},
{
active: false,
title: 'Atlassian Bamboo CI',
description: 'A continuous integration and build server',
updated_at: null,
edit_path:
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/bamboo/edit',
name: 'bamboo',
},
];

View File

@ -113,7 +113,7 @@ RSpec.describe Gitlab::Sanitizers::Exif do
it 'cleans only jpg/tiff images with the correct mime types' do
expect(sanitizer).not_to receive(:extra_tags)
expect { subject }.to raise_error(RuntimeError, /File type text\/plain not supported/)
expect { subject }.to raise_error(RuntimeError, %r{File type text/plain not supported})
end
end
end

View File

@ -59,7 +59,7 @@ RSpec.describe Pages::LookupPath do
it 'return nil when legacy storage is disabled and there is no deployment' do
stub_feature_flags(pages_serve_from_legacy_storage: false)
expect(Gitlab::ErrorTracking).to receive(:track_exception)
.with(described_class::LegacyStorageDisabledError)
.with(described_class::LegacyStorageDisabledError, project_id: project.id)
.and_call_original
expect(source).to eq(nil)

View File

@ -180,6 +180,18 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
it_behaves_like 'authorizes local file'
end
end
context 'when job does not exist anymore' do
before do
allow(job).to receive(:id).and_return(non_existing_record_id)
end
it 'returns 403 Forbidden' do
subject
expect(response).to have_gitlab_http_status(:forbidden)
end
end
end
end
@ -321,6 +333,18 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
end
context 'when job does not exist anymore' do
before do
allow(job).to receive(:id).and_return(non_existing_record_id)
end
it 'returns 403 Forbidden' do
upload_artifacts(file_upload, headers_with_token)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when job is running' do
shared_examples 'successful artifacts upload' do
it 'updates successfully' do
@ -867,6 +891,18 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
end
context 'when job does not exist anymore' do
before do
allow(job).to receive(:id).and_return(non_existing_record_id)
end
it 'responds with 403 Forbidden' do
get api("/jobs/#{job.id}/artifacts"), params: { token: token }, headers: headers
expect(response).to have_gitlab_http_status(:forbidden)
end
end
def download_artifact(params = {}, request_headers = headers)
params = params.merge(token: token)
job.reload

View File

@ -278,14 +278,22 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
end
def update_job(token = job.token, **params)
context 'when job does not exist anymore' do
it 'returns 403 Forbidden' do
update_job(non_existing_record_id, state: 'success', trace: 'BUILD TRACE UPDATED')
expect(response).to have_gitlab_http_status(:forbidden)
end
end
def update_job(job_id = job.id, token = job.token, **params)
new_params = params.merge(token: token)
put api("/jobs/#{job.id}"), params: new_params
put api("/jobs/#{job_id}"), params: new_params
end
def update_job_after_time(update_interval = 20.minutes, state = 'running')
travel_to(job.updated_at + update_interval) do
update_job(job.token, state: state)
update_job(job.id, job.token, state: state)
end
end
end

View File

@ -219,6 +219,14 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
end
context 'when job does not exist anymore' do
it 'returns 403 Forbidden' do
patch_the_trace(job_id: non_existing_record_id)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when Runner makes a force-patch' do
before do
force_patch_the_trace
@ -264,7 +272,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
it { expect(response).to have_gitlab_http_status(:forbidden) }
end
def patch_the_trace(content = ' appended', request_headers = nil)
def patch_the_trace(content = ' appended', request_headers = nil, job_id: job.id)
unless request_headers
job.trace.read do |stream|
offset = stream.size
@ -274,7 +282,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
Timecop.travel(job.updated_at + update_interval) do
patch api("/jobs/#{job.id}/trace"), params: content, headers: request_headers
patch api("/jobs/#{job_id}/trace"), params: content, headers: request_headers
job.reload
end
end