Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
86e1f47cd1
commit
aee8d27430
|
@ -1,12 +1,15 @@
|
|||
<script>
|
||||
import eventHub from '../event_hub';
|
||||
import { GlToggle } from '@gitlab/ui';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { GlFormGroup, GlToggle } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
name: 'ActiveToggle',
|
||||
components: {
|
||||
GlFormGroup,
|
||||
GlToggle,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
initialActivated: {
|
||||
type: Boolean,
|
||||
|
@ -33,7 +36,17 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="glFeatures.integrationFormRefactor">
|
||||
<gl-form-group :label="__('Enable integration')" label-for="service[active]">
|
||||
<gl-toggle
|
||||
v-model="activated"
|
||||
name="service[active]"
|
||||
class="gl-display-block gl-line-height-0"
|
||||
@change="onToggle"
|
||||
/>
|
||||
</gl-form-group>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="form-group row" role="group">
|
||||
<label for="service[active]" class="col-form-label col-sm-2">{{ __('Active') }}</label>
|
||||
<div class="col-sm-10 pt-1">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import eventHub from '../event_hub';
|
||||
import { capitalize, lowerCase, isEmpty } from 'lodash';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import { GlFormGroup, GlFormCheckbox, GlFormInput, GlFormSelect, GlFormTextarea } from '@gitlab/ui';
|
||||
|
@ -54,6 +55,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
model: this.value,
|
||||
validated: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -106,17 +108,35 @@ export default {
|
|||
name: this.fieldName,
|
||||
};
|
||||
},
|
||||
valid() {
|
||||
return !this.required || !isEmpty(this.model) || !this.validated;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.isNonEmptyPassword) {
|
||||
this.model = null;
|
||||
}
|
||||
eventHub.$on('validateForm', this.validateForm);
|
||||
},
|
||||
beforeDestroy() {
|
||||
eventHub.$off('validateForm', this.validateForm);
|
||||
},
|
||||
methods: {
|
||||
validateForm() {
|
||||
this.validated = true;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-form-group :label="label" :label-for="fieldId" :description="help">
|
||||
<gl-form-group
|
||||
:label="label"
|
||||
:label-for="fieldId"
|
||||
:invalid-feedback="__('This field is required.')"
|
||||
:state="valid"
|
||||
:description="help"
|
||||
>
|
||||
<template v-if="isCheckbox">
|
||||
<input :name="fieldName" type="hidden" value="false" />
|
||||
<gl-form-checkbox v-model="model" v-bind="sharedProps">
|
||||
|
|
|
@ -1,12 +1,31 @@
|
|||
<script>
|
||||
import { GlFormCheckbox, GlFormRadio } from '@gitlab/ui';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { s__ } from '~/locale';
|
||||
import { GlFormGroup, GlFormCheckbox, GlFormRadio } from '@gitlab/ui';
|
||||
|
||||
const commentDetailOptions = [
|
||||
{
|
||||
value: 'standard',
|
||||
label: s__('Integrations|Standard'),
|
||||
help: s__('Integrations|Includes commit title and branch'),
|
||||
},
|
||||
{
|
||||
value: 'all_details',
|
||||
label: s__('Integrations|All details'),
|
||||
help: s__(
|
||||
'Integrations|Includes Standard plus entire commit message, commit hash, and issue IDs',
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
name: 'JiraTriggerFields',
|
||||
components: {
|
||||
GlFormGroup,
|
||||
GlFormCheckbox,
|
||||
GlFormRadio,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
initialTriggerCommit: {
|
||||
type: Boolean,
|
||||
|
@ -32,13 +51,71 @@ export default {
|
|||
triggerMergeRequest: this.initialTriggerMergeRequest,
|
||||
enableComments: this.initialEnableComments,
|
||||
commentDetail: this.initialCommentDetail,
|
||||
commentDetailOptions,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showEnableComments() {
|
||||
return this.triggerCommit || this.triggerMergeRequest;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="form-group row pt-2" role="group">
|
||||
<div v-if="glFeatures.integrationFormRefactor">
|
||||
<gl-form-group
|
||||
:label="__('Trigger')"
|
||||
label-for="service[trigger]"
|
||||
:description="
|
||||
s__(
|
||||
'Integrations|When a Jira issue is mentioned in a commit or merge request a remote link and comment (if enabled) will be created.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<input name="service[commit_events]" type="hidden" value="false" />
|
||||
<gl-form-checkbox v-model="triggerCommit" name="service[commit_events]">
|
||||
{{ __('Commit') }}
|
||||
</gl-form-checkbox>
|
||||
|
||||
<input name="service[merge_requests_events]" type="hidden" value="false" />
|
||||
<gl-form-checkbox v-model="triggerMergeRequest" name="service[merge_requests_events]">
|
||||
{{ __('Merge request') }}
|
||||
</gl-form-checkbox>
|
||||
</gl-form-group>
|
||||
|
||||
<gl-form-group
|
||||
v-show="showEnableComments"
|
||||
:label="s__('Integrations|Comment settings:')"
|
||||
data-testid="comment-settings"
|
||||
>
|
||||
<input name="service[comment_on_event_enabled]" type="hidden" value="false" />
|
||||
<gl-form-checkbox v-model="enableComments" name="service[comment_on_event_enabled]">
|
||||
{{ s__('Integrations|Enable comments') }}
|
||||
</gl-form-checkbox>
|
||||
</gl-form-group>
|
||||
|
||||
<gl-form-group
|
||||
v-show="showEnableComments && enableComments"
|
||||
:label="s__('Integrations|Comment detail:')"
|
||||
data-testid="comment-detail"
|
||||
>
|
||||
<gl-form-radio
|
||||
v-for="commentDetailOption in commentDetailOptions"
|
||||
:key="commentDetailOption.value"
|
||||
v-model="commentDetail"
|
||||
:value="commentDetailOption.value"
|
||||
name="service[comment_detail]"
|
||||
>
|
||||
{{ commentDetailOption.label }}
|
||||
<template #help>
|
||||
{{ commentDetailOption.help }}
|
||||
</template>
|
||||
</gl-form-radio>
|
||||
</gl-form-group>
|
||||
</div>
|
||||
|
||||
<div v-else class="form-group row pt-2" role="group">
|
||||
<label for="service[trigger]" class="col-form-label col-sm-2 pt-0">{{ __('Trigger') }}</label>
|
||||
<div class="col-sm-10">
|
||||
<label class="weight-normal mb-2">
|
||||
|
@ -76,20 +153,16 @@ export default {
|
|||
<label>
|
||||
{{ s__('Integrations|Comment detail:') }}
|
||||
</label>
|
||||
<gl-form-radio v-model="commentDetail" value="standard" name="service[comment_detail]">
|
||||
{{ s__('Integrations|Standard') }}
|
||||
<gl-form-radio
|
||||
v-for="commentDetailOption in commentDetailOptions"
|
||||
:key="commentDetailOption.value"
|
||||
v-model="commentDetail"
|
||||
:value="commentDetailOption.value"
|
||||
name="service[comment_detail]"
|
||||
>
|
||||
{{ commentDetailOption.label }}
|
||||
<template #help>
|
||||
{{ s__('Integrations|Includes commit title and branch') }}
|
||||
</template>
|
||||
</gl-form-radio>
|
||||
<gl-form-radio v-model="commentDetail" value="all_details" name="service[comment_detail]">
|
||||
{{ s__('Integrations|All details') }}
|
||||
<template #help>
|
||||
{{
|
||||
s__(
|
||||
'Integrations|Includes Standard plus entire commit message, commit hash, and issue IDs',
|
||||
)
|
||||
}}
|
||||
{{ commentDetailOption.help }}
|
||||
</template>
|
||||
</gl-form-radio>
|
||||
</div>
|
||||
|
|
|
@ -45,10 +45,15 @@ export default class IntegrationSettingsForm {
|
|||
// 2) If this service can be tested
|
||||
// If both conditions are true, we override form submission
|
||||
// and test the service using provided configuration.
|
||||
if (this.$form.get(0).checkValidity() && this.canTestService) {
|
||||
if (this.$form.get(0).checkValidity()) {
|
||||
if (this.canTestService) {
|
||||
e.preventDefault();
|
||||
// eslint-disable-next-line no-jquery/no-serialize
|
||||
this.testSettings(this.$form.serialize());
|
||||
}
|
||||
} else {
|
||||
e.preventDefault();
|
||||
// eslint-disable-next-line no-jquery/no-serialize
|
||||
this.testSettings(this.$form.serialize());
|
||||
eventHub.$emit('validateForm');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
<script>
|
||||
import { GlAlert, GlLabel } from '@gitlab/ui';
|
||||
import getIssuesListDetailsQuery from '../queries/get_issues_list_details.query.graphql';
|
||||
import { calculateJiraImportLabel, isFinished, isInProgress } from '~/jira_import/utils';
|
||||
import {
|
||||
calculateJiraImportLabel,
|
||||
isFinished,
|
||||
isInProgress,
|
||||
} from '~/jira_import/utils/jira_import_utils';
|
||||
|
||||
export default {
|
||||
name: 'IssuableListRoot',
|
||||
|
|
|
@ -4,7 +4,8 @@ import { last } from 'lodash';
|
|||
import { __ } from '~/locale';
|
||||
import getJiraImportDetailsQuery from '../queries/get_jira_import_details.query.graphql';
|
||||
import initiateJiraImportMutation from '../queries/initiate_jira_import.mutation.graphql';
|
||||
import { IMPORT_STATE, isInProgress, extractJiraProjectsOptions } from '../utils';
|
||||
import { addInProgressImportToStore } from '../utils/cache_update';
|
||||
import { isInProgress, extractJiraProjectsOptions } from '../utils/jira_import_utils';
|
||||
import JiraImportForm from './jira_import_form.vue';
|
||||
import JiraImportProgress from './jira_import_progress.vue';
|
||||
import JiraImportSetup from './jira_import_setup.vue';
|
||||
|
@ -20,14 +21,14 @@ export default {
|
|||
JiraImportSetup,
|
||||
},
|
||||
props: {
|
||||
isJiraConfigured: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
inProgressIllustration: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isJiraConfigured: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
issuesPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
@ -62,9 +63,10 @@ export default {
|
|||
};
|
||||
},
|
||||
update: ({ project }) => ({
|
||||
projects: extractJiraProjectsOptions(project.services.nodes[0].projects.nodes),
|
||||
status: project.jiraImportStatus,
|
||||
imports: project.jiraImports.nodes,
|
||||
isInProgress: isInProgress(project.jiraImportStatus),
|
||||
mostRecentImport: last(project.jiraImports.nodes),
|
||||
projects: extractJiraProjectsOptions(project.services.nodes[0].projects.nodes),
|
||||
}),
|
||||
skip() {
|
||||
return !this.isJiraConfigured;
|
||||
|
@ -72,32 +74,22 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
isImportInProgress() {
|
||||
return isInProgress(this.jiraImportDetails.status);
|
||||
},
|
||||
mostRecentImport() {
|
||||
// The backend returns JiraImports ordered by created_at asc in app/models/project.rb
|
||||
return last(this.jiraImportDetails.imports);
|
||||
},
|
||||
numberOfPreviousImportsForProject() {
|
||||
numberOfPreviousImports() {
|
||||
return this.jiraImportDetails.imports?.reduce?.(
|
||||
(acc, jiraProject) => (jiraProject.jiraProjectKey === this.selectedProject ? acc + 1 : acc),
|
||||
0,
|
||||
);
|
||||
},
|
||||
hasPreviousImports() {
|
||||
return this.numberOfPreviousImports > 0;
|
||||
},
|
||||
importLabel() {
|
||||
return this.selectedProject
|
||||
? `jira-import::${this.selectedProject}-${this.numberOfPreviousImportsForProject + 1}`
|
||||
? `jira-import::${this.selectedProject}-${this.numberOfPreviousImports + 1}`
|
||||
: 'jira-import::KEY-1';
|
||||
},
|
||||
hasPreviousImports() {
|
||||
return this.numberOfPreviousImportsForProject > 0;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
dismissAlert() {
|
||||
this.showAlert = false;
|
||||
},
|
||||
initiateJiraImport(project) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
|
@ -108,39 +100,8 @@ export default {
|
|||
jiraProjectKey: project,
|
||||
},
|
||||
},
|
||||
update: (store, { data }) => {
|
||||
if (data.jiraImportStart.errors.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cacheData = store.readQuery({
|
||||
query: getJiraImportDetailsQuery,
|
||||
variables: {
|
||||
fullPath: this.projectPath,
|
||||
},
|
||||
});
|
||||
|
||||
store.writeQuery({
|
||||
query: getJiraImportDetailsQuery,
|
||||
variables: {
|
||||
fullPath: this.projectPath,
|
||||
},
|
||||
data: {
|
||||
project: {
|
||||
jiraImportStatus: IMPORT_STATE.SCHEDULED,
|
||||
jiraImports: {
|
||||
nodes: [
|
||||
...cacheData.project.jiraImports.nodes,
|
||||
data.jiraImportStart.jiraImport,
|
||||
],
|
||||
__typename: 'JiraImportConnection',
|
||||
},
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
__typename: 'Project',
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
update: (store, { data }) =>
|
||||
addInProgressImportToStore(store, data.jiraImportStart, this.projectPath),
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.jiraImportStart.errors.length) {
|
||||
|
@ -155,7 +116,13 @@ export default {
|
|||
this.errorMessage = message;
|
||||
this.showAlert = true;
|
||||
},
|
||||
dismissAlert() {
|
||||
this.showAlert = false;
|
||||
},
|
||||
},
|
||||
previousImportsMessage: __(
|
||||
'You have imported from this project %{numberOfPreviousImports} times before. Each new import will create duplicate issues.',
|
||||
),
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -165,16 +132,8 @@ export default {
|
|||
{{ errorMessage }}
|
||||
</gl-alert>
|
||||
<gl-alert v-if="hasPreviousImports" variant="warning" :dismissible="false">
|
||||
<gl-sprintf
|
||||
:message="
|
||||
__(
|
||||
'You have imported from this project %{numberOfPreviousImportsForProject} times before. Each new import will create duplicate issues.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #numberOfPreviousImportsForProject>{{
|
||||
numberOfPreviousImportsForProject
|
||||
}}</template>
|
||||
<gl-sprintf :message="$options.previousImportsMessage">
|
||||
<template #numberOfPreviousImports>{{ numberOfPreviousImports }}</template>
|
||||
</gl-sprintf>
|
||||
</gl-alert>
|
||||
|
||||
|
@ -185,11 +144,11 @@ export default {
|
|||
/>
|
||||
<gl-loading-icon v-else-if="$apollo.loading" size="md" class="mt-3" />
|
||||
<jira-import-progress
|
||||
v-else-if="isImportInProgress"
|
||||
v-else-if="jiraImportDetails.isInProgress"
|
||||
:illustration="inProgressIllustration"
|
||||
:import-initiator="mostRecentImport.scheduledBy.name"
|
||||
:import-project="mostRecentImport.jiraProjectKey"
|
||||
:import-time="mostRecentImport.scheduledAt"
|
||||
:import-initiator="jiraImportDetails.mostRecentImport.scheduledBy.name"
|
||||
:import-project="jiraImportDetails.mostRecentImport.jiraProjectKey"
|
||||
:import-time="jiraImportDetails.mostRecentImport.scheduledAt"
|
||||
:issues-path="issuesPath"
|
||||
/>
|
||||
<jira-import-form
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import getJiraImportDetailsQuery from '../queries/get_jira_import_details.query.graphql';
|
||||
import { IMPORT_STATE } from './jira_import_utils';
|
||||
|
||||
export const addInProgressImportToStore = (store, jiraImportStart, fullPath) => {
|
||||
if (jiraImportStart.errors.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const queryDetails = {
|
||||
query: getJiraImportDetailsQuery,
|
||||
variables: {
|
||||
fullPath,
|
||||
},
|
||||
};
|
||||
|
||||
const cacheData = store.readQuery({
|
||||
...queryDetails,
|
||||
});
|
||||
|
||||
store.writeQuery({
|
||||
...queryDetails,
|
||||
data: {
|
||||
project: {
|
||||
...cacheData.project,
|
||||
jiraImportStatus: IMPORT_STATE.SCHEDULED,
|
||||
jiraImports: {
|
||||
...cacheData.project.jiraImports,
|
||||
nodes: cacheData.project.jiraImports.nodes.concat(jiraImportStart.jiraImport),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
addInProgressImportToStore,
|
||||
};
|
|
@ -20,6 +20,7 @@ class Projects::BlameController < Projects::ApplicationController
|
|||
environment_params[:find_latest] = true
|
||||
@environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last
|
||||
|
||||
@blame_groups = Gitlab::Blame.new(@blob, @commit).groups
|
||||
@blame = Gitlab::Blame.new(@blob, @commit)
|
||||
@blame = Gitlab::View::Presenter::Factory.new(@blame, project: @project, path: @path).fabricate!
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,6 +11,9 @@ class Projects::ServicesController < Projects::ApplicationController
|
|||
before_action :web_hook_logs, only: [:edit, :update]
|
||||
before_action :set_deprecation_notice_for_prometheus_service, only: [:edit, :update]
|
||||
before_action :redirect_deprecated_prometheus_service, only: [:update]
|
||||
before_action only: :edit do
|
||||
push_frontend_feature_flag(:integration_form_refactor)
|
||||
end
|
||||
|
||||
respond_to :html
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ module TodosHelper
|
|||
|
||||
def todo_target_type_name(todo)
|
||||
return _('design') if todo.for_design?
|
||||
return _('alert') if todo.for_alert?
|
||||
|
||||
todo.target_type.titleize.downcase
|
||||
end
|
||||
|
@ -68,6 +69,8 @@ module TodosHelper
|
|||
project_commit_path(todo.project, todo.target, path_options)
|
||||
elsif todo.for_design?
|
||||
todos_design_path(todo, path_options)
|
||||
elsif todo.for_alert?
|
||||
details_project_alert_management_path(todo.project, todo.target)
|
||||
else
|
||||
path = [todo.resource_parent, todo.target]
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ require_dependency 'alert_management'
|
|||
|
||||
module AlertManagement
|
||||
class Alert < ApplicationRecord
|
||||
include IidRoutes
|
||||
include AtomicInternalId
|
||||
include ShaAttribute
|
||||
include Sortable
|
||||
|
@ -143,6 +144,12 @@ module AlertManagement
|
|||
increment!(:events)
|
||||
end
|
||||
|
||||
# required for todos (typically contains an identifier like issue iid)
|
||||
# no-op; we could use iid, but we don't have a reference prefix
|
||||
def to_reference(_from = nil, full: false)
|
||||
''
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hosts_length
|
||||
|
|
|
@ -11,12 +11,7 @@ module PerformanceMonitoring
|
|||
|
||||
class << self
|
||||
def from_json(json_content)
|
||||
dashboard = new(
|
||||
dashboard: json_content['dashboard'],
|
||||
panel_groups: json_content['panel_groups']&.map { |group| PrometheusPanelGroup.from_json(group) }
|
||||
)
|
||||
|
||||
dashboard.tap(&:validate!)
|
||||
build_from_hash(json_content).tap(&:validate!)
|
||||
end
|
||||
|
||||
def find_for(project:, user:, path:, options: {})
|
||||
|
@ -30,6 +25,17 @@ module PerformanceMonitoring
|
|||
}.merge(dashboard_response[:dashboard])
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_from_hash(attributes)
|
||||
return new unless attributes.is_a?(Hash)
|
||||
|
||||
new(
|
||||
dashboard: attributes['dashboard'],
|
||||
panel_groups: attributes['panel_groups']&.map { |group| PrometheusPanelGroup.from_json(group) }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def to_yaml
|
||||
|
|
|
@ -10,16 +10,24 @@ module PerformanceMonitoring
|
|||
validates :query, presence: true, unless: :query_range
|
||||
validates :query_range, presence: true, unless: :query
|
||||
|
||||
def self.from_json(json_content)
|
||||
metric = PrometheusMetric.new(
|
||||
id: json_content['id'],
|
||||
unit: json_content['unit'],
|
||||
label: json_content['label'],
|
||||
query: json_content['query'],
|
||||
query_range: json_content['query_range']
|
||||
)
|
||||
class << self
|
||||
def from_json(json_content)
|
||||
build_from_hash(json_content).tap(&:validate!)
|
||||
end
|
||||
|
||||
metric.tap(&:validate!)
|
||||
private
|
||||
|
||||
def build_from_hash(attributes)
|
||||
return new unless attributes.is_a?(Hash)
|
||||
|
||||
new(
|
||||
id: attributes['id'],
|
||||
unit: attributes['unit'],
|
||||
label: attributes['label'],
|
||||
query: attributes['query'],
|
||||
query_range: attributes['query_range']
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,17 +8,24 @@ module PerformanceMonitoring
|
|||
|
||||
validates :title, presence: true
|
||||
validates :metrics, presence: true
|
||||
class << self
|
||||
def from_json(json_content)
|
||||
build_from_hash(json_content).tap(&:validate!)
|
||||
end
|
||||
|
||||
def self.from_json(json_content)
|
||||
panel = new(
|
||||
type: json_content['type'],
|
||||
title: json_content['title'],
|
||||
y_label: json_content['y_label'],
|
||||
weight: json_content['weight'],
|
||||
metrics: json_content['metrics']&.map { |metric| PrometheusMetric.from_json(metric) }
|
||||
)
|
||||
private
|
||||
|
||||
panel.tap(&:validate!)
|
||||
def build_from_hash(attributes)
|
||||
return new unless attributes.is_a?(Hash)
|
||||
|
||||
new(
|
||||
type: attributes['type'],
|
||||
title: attributes['title'],
|
||||
y_label: attributes['y_label'],
|
||||
weight: attributes['weight'],
|
||||
metrics: attributes['metrics']&.map { |metric| PrometheusMetric.from_json(metric) }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def id(group_title)
|
||||
|
|
|
@ -8,15 +8,22 @@ module PerformanceMonitoring
|
|||
|
||||
validates :group, presence: true
|
||||
validates :panels, presence: true
|
||||
class << self
|
||||
def from_json(json_content)
|
||||
build_from_hash(json_content).tap(&:validate!)
|
||||
end
|
||||
|
||||
def self.from_json(json_content)
|
||||
panel_group = new(
|
||||
group: json_content['group'],
|
||||
priority: json_content['priority'],
|
||||
panels: json_content['panels']&.map { |panel| PrometheusPanel.from_json(panel) }
|
||||
)
|
||||
private
|
||||
|
||||
panel_group.tap(&:validate!)
|
||||
def build_from_hash(attributes)
|
||||
return new unless attributes.is_a?(Hash)
|
||||
|
||||
new(
|
||||
group: attributes['group'],
|
||||
priority: attributes['priority'],
|
||||
panels: attributes['panels']&.map { |panel| PrometheusPanel.from_json(panel) }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -189,6 +189,10 @@ class Todo < ApplicationRecord
|
|||
target_type == DesignManagement::Design.name
|
||||
end
|
||||
|
||||
def for_alert?
|
||||
target_type == AlertManagement::Alert.name
|
||||
end
|
||||
|
||||
# override to return commits, which are not active record
|
||||
def target
|
||||
if for_commit?
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
class BlamePresenter < Gitlab::View::Presenter::Simple
|
||||
include ActionView::Helpers::UrlHelper
|
||||
include ActionView::Helpers::TranslationHelper
|
||||
include ActionView::Context
|
||||
include AvatarsHelper
|
||||
include BlameHelper
|
||||
include CommitsHelper
|
||||
include ApplicationHelper
|
||||
include TreeHelper
|
||||
include IconsHelper
|
||||
|
||||
presents :blame
|
||||
|
||||
CommitData = Struct.new(
|
||||
:author_avatar,
|
||||
:age_map_class,
|
||||
:commit_link,
|
||||
:commit_author_link,
|
||||
:project_blame_link,
|
||||
:time_ago_tooltip)
|
||||
|
||||
def initialize(subject, **attributes)
|
||||
super
|
||||
|
||||
@commits = {}
|
||||
precalculate_data_by_commit!
|
||||
end
|
||||
|
||||
def groups
|
||||
@groups ||= blame.groups
|
||||
end
|
||||
|
||||
def commit_data(commit)
|
||||
@commits[commit.id] ||= get_commit_data(commit)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Huge source files with a high churn rate (e.g. 'locale/gitlab.pot') could have
|
||||
# 10x times more blame groups than unique commits across all the groups.
|
||||
# That means we could cache per-commit data we need
|
||||
# to avoid recalculating it multiple times.
|
||||
# For such files, it could significantly improve the performance of the Blame.
|
||||
def precalculate_data_by_commit!
|
||||
groups.each { |group| commit_data(group[:commit]) }
|
||||
end
|
||||
|
||||
def get_commit_data(commit)
|
||||
CommitData.new.tap do |data|
|
||||
data.author_avatar = author_avatar(commit, size: 36, has_tooltip: false)
|
||||
data.age_map_class = age_map_class(commit.committed_date, project_duration)
|
||||
data.commit_link = link_to commit.title, project_commit_path(project, commit.id), class: "cdark", title: commit.title
|
||||
data.commit_author_link = commit_author_link(commit, avatar: false)
|
||||
data.project_blame_link = project_blame_link(commit)
|
||||
data.time_ago_tooltip = time_ago_with_tooltip(commit.committed_date)
|
||||
end
|
||||
end
|
||||
|
||||
def project_blame_link(commit)
|
||||
previous_commit_id = commit.parent_id
|
||||
return unless previous_commit_id
|
||||
|
||||
link_to project_blame_path(project, tree_join(previous_commit_id, path)),
|
||||
title: _('View blame prior to this change'),
|
||||
aria: { label: _('View blame prior to this change') },
|
||||
data: { toggle: 'tooltip', placement: 'right', container: 'body' } do
|
||||
versions_sprite_icon
|
||||
end
|
||||
end
|
||||
|
||||
def project_duration
|
||||
@project_duration ||= age_map_duration(groups, project)
|
||||
end
|
||||
|
||||
def versions_sprite_icon
|
||||
@versions_sprite_icon ||= sprite_icon('doc-versions', size: 16, css_class: 'doc-versions align-text-bottom')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
%tr
|
||||
%td.blame-commit{ class: commit_data.age_map_class }
|
||||
.commit
|
||||
= commit_data.author_avatar
|
||||
.commit-row-title
|
||||
%span.item-title.str-truncated-100
|
||||
= commit_data.commit_link
|
||||
%span
|
||||
= commit_data.project_blame_link
|
||||
|
||||
.light
|
||||
= commit_data.commit_author_link
|
||||
= _('committed')
|
||||
#{commit_data.time_ago_tooltip}
|
||||
%td.line-numbers
|
||||
- line_count = blame_group[:lines].count
|
||||
- (current_line...(current_line + line_count)).each do |i|
|
||||
%a.diff-line-num{ href: "#L#{i}", id: "L#{i}", 'data-line-number' => i }
|
||||
= link_icon
|
||||
= i
|
||||
\
|
||||
%td.lines
|
||||
%pre.code.highlight
|
||||
%code
|
||||
- blame_group[:lines].each do |line|
|
||||
#{line}
|
|
@ -1,5 +1,5 @@
|
|||
- project_duration = age_map_duration(@blame_groups, @project)
|
||||
- page_title "Blame", @blob.path, @ref
|
||||
- link_icon = icon("link")
|
||||
|
||||
#blob-content-holder.tree-holder
|
||||
= render "projects/blob/breadcrumb", blob: @blob, blame: true
|
||||
|
@ -11,38 +11,13 @@
|
|||
.table-responsive.file-content.blame.code.js-syntax-highlight
|
||||
%table
|
||||
- current_line = 1
|
||||
- @blame_groups.each do |blame_group|
|
||||
%tr
|
||||
- commit = blame_group[:commit]
|
||||
%td.blame-commit{ class: age_map_class(commit.committed_date, project_duration) }
|
||||
.commit
|
||||
= author_avatar(commit, size: 36, has_tooltip: false)
|
||||
.commit-row-title
|
||||
%span.item-title.str-truncated-100
|
||||
= link_to commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title
|
||||
%span
|
||||
- previous_commit_id = commit.parent_id
|
||||
- if previous_commit_id
|
||||
= link_to project_blame_path(@project, tree_join(previous_commit_id, @path)),
|
||||
title: _('View blame prior to this change'),
|
||||
aria: { label: _('View blame prior to this change') },
|
||||
data: { toggle: 'tooltip', placement: 'right', container: 'body' } do
|
||||
= sprite_icon('doc-versions', size: 16, css_class: 'doc-versions align-text-bottom')
|
||||
|
||||
.light
|
||||
= commit_author_link(commit, avatar: false)
|
||||
committed
|
||||
#{time_ago_with_tooltip(commit.committed_date)}
|
||||
%td.line-numbers
|
||||
- line_count = blame_group[:lines].count
|
||||
- (current_line...(current_line + line_count)).each do |i|
|
||||
%a.diff-line-num{ href: "#L#{i}", id: "L#{i}", 'data-line-number' => i }
|
||||
= icon("link")
|
||||
= i
|
||||
\
|
||||
- current_line += line_count
|
||||
%td.lines
|
||||
%pre.code.highlight
|
||||
%code
|
||||
- blame_group[:lines].each do |line|
|
||||
#{line}
|
||||
- @blame.groups.each do |blame_group|
|
||||
- commit_data = @blame.commit_data(blame_group[:commit])
|
||||
|
||||
= render 'blame_group',
|
||||
blame_group: blame_group,
|
||||
current_line: current_line,
|
||||
link_icon: link_icon,
|
||||
commit_data: commit_data
|
||||
|
||||
- current_line += blame_group[:lines].count
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove FF hide_token_from_runners_api
|
||||
merge_request: 33947
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Fixed dashboard YAML file validaiton for files which do not contain object
|
||||
as root element
|
||||
merge_request: 33935
|
||||
author:
|
||||
type: fixed
|
|
@ -19,6 +19,7 @@ exceptions:
|
|||
- CSS
|
||||
- CSV
|
||||
- DNS
|
||||
- EKS
|
||||
- GET
|
||||
- GNU
|
||||
- GPG
|
||||
|
|
|
@ -47,15 +47,15 @@ GET /projects/:id/vulnerability_findings?pipeline_id=42
|
|||
```
|
||||
|
||||
CAUTION: **Deprecation:**
|
||||
Beginning with GitLab 12.9, the `undefined` severity level is deprecated and the `undefined` confidence level isn't reported for new vulnerabilities.
|
||||
Beginning with GitLab 12.9, the `undefined` severity and confidence level is no longer reported.
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| ------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) which the authenticated user is a member of. |
|
||||
| `report_type` | string array | no | Returns vulnerability findings belonging to specified report type. Valid values: `sast`, `dast`, `dependency_scanning`, or `container_scanning`. Defaults to all. |
|
||||
| `scope` | string | no | Returns vulnerability findings for the given scope: `all` or `dismissed`. Defaults to `dismissed`. |
|
||||
| `severity` | string array | no | Returns vulnerability findings belonging to specified severity level: `undefined`, `info`, `unknown`, `low`, `medium`, `high`, or `critical`. Defaults to all. |
|
||||
| `confidence` | string array | no | Returns vulnerability findings belonging to specified confidence level: `undefined`, `ignore`, `unknown`, `experimental`, `low`, `medium`, `high`, or `confirmed`. Defaults to all. |
|
||||
| `severity` | string array | no | Returns vulnerability findings belonging to specified severity level: `info`, `unknown`, `low`, `medium`, `high`, or `critical`. Defaults to all. |
|
||||
| `confidence` | string array | no | Returns vulnerability findings belonging to specified confidence level: `ignore`, `unknown`, `experimental`, `low`, `medium`, `high`, or `confirmed`. Defaults to all. |
|
||||
| `pipeline_id` | integer/string | no | Returns vulnerability findings belonging to specified pipeline. |
|
||||
|
||||
```shell
|
||||
|
|
|
@ -456,7 +456,8 @@ On GitLab.com, we have DangerBot setup to monitor Telemetry related files and Da
|
|||
| `dependency_list_usages_total` | `counts` | | |
|
||||
| `epics` | `counts` | | |
|
||||
| `feature_flags` | `counts` | | |
|
||||
| `geo_nodes` | `counts` | | |
|
||||
| `geo_nodes` | `counts` | `geo` | Number of sites in a Geo deployment |
|
||||
| `geo_event_log_max_id` | `counts` | `geo` | Number of replication events on a Geo primary |
|
||||
| `incident_issues` | `counts` | `monitor` | Issues created by the alert bot |
|
||||
| `alert_bot_incident_issues` | `counts` | `monitor` | Issues created by the alert bot |
|
||||
| `incident_labeled_issues` | `counts` | `monitor` | Issues with the incident label |
|
||||
|
|
|
@ -803,7 +803,7 @@ restore:
|
|||
|
||||
```shell
|
||||
# This command will overwrite the contents of your GitLab database!
|
||||
sudo gitlab-backup restore BACKUP=1493107454_2018_04_25_10.6.4-ce
|
||||
sudo gitlab-backup restore BACKUP=11493107454_2018_04_25_10.6.4-ce
|
||||
```
|
||||
|
||||
NOTE: **Note**
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
Before Width: | Height: | Size: 10 KiB |
|
@ -52,29 +52,29 @@ NOTE: **Note:**
|
|||
set up by administrators. However, you can turn this off by disabling the
|
||||
**Allow requests to the local network from system hooks** option.
|
||||
|
||||
## Whitelist for local requests
|
||||
## Allowlist for local requests
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/44496) in GitLab 12.2
|
||||
|
||||
You can allow certain domains and IP addresses to be accessible to both *system hooks*
|
||||
and *webhooks* even when local requests are not allowed by adding them to the
|
||||
whitelist. Navigate to **Admin Area > Settings > Network** (`/admin/application_settings/network`)
|
||||
allowlist. Navigate to **Admin Area > Settings > Network** (`/admin/application_settings/network`)
|
||||
and expand **Outbound requests**:
|
||||
|
||||
![Outbound local requests whitelist](img/whitelist.png)
|
||||
![Outbound local requests allowlist](img/allowlist_v13_0.png)
|
||||
|
||||
The whitelist entries can be separated by semicolons, commas or whitespaces
|
||||
The allowed entries can be separated by semicolons, commas or whitespaces
|
||||
(including newlines) and be in different formats like hostnames, IP addresses and/or
|
||||
IP ranges. IPv6 is supported. Hostnames that contain unicode characters should
|
||||
use IDNA encoding.
|
||||
|
||||
The whitelist can hold a maximum of 1000 entries. Each entry can be a maximum of
|
||||
The allowlist can hold a maximum of 1000 entries. Each entry can be a maximum of
|
||||
255 characters.
|
||||
|
||||
You can whitelist a particular port by specifying it in the whitelist entry.
|
||||
You can allow a particular port by specifying it in the allowlist entry.
|
||||
For example `127.0.0.1:8080` will only allow connections to port 8080 on `127.0.0.1`.
|
||||
If no port is mentioned, all ports on that IP/domain are whitelisted. An IP range
|
||||
will whitelist all ports on all IPs in that range.
|
||||
If no port is mentioned, all ports on that IP/domain are allowed. An IP range
|
||||
will allow all ports on all IPs in that range.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 122 KiB |
|
@ -35,6 +35,17 @@ shows a total of 15 months for the chart in the GitLab.org group.
|
|||
|
||||
![Issues created per month](img/issues_created_per_month_v12_8.png)
|
||||
|
||||
## Drill into the information
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196547) in GitLab 13.1.
|
||||
|
||||
You can examine details of individual issues by browsing the table
|
||||
located below the chart.
|
||||
|
||||
The chart displays the top 100 issues based on the global page filters.
|
||||
|
||||
![Issues table](img/issues_table_v13_1.png)
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
|
||||
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
|
||||
|
|
|
@ -164,6 +164,41 @@ You will need to add your AWS external ID to the
|
|||
[IAM Role in the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html#cli-configure-role-xaccount)
|
||||
to manage your cluster using `kubectl`.
|
||||
|
||||
### Troubleshooting creating a new cluster
|
||||
|
||||
The following errors are commonly encountered when creating a new cluster.
|
||||
|
||||
#### Error: Request failed with status code 422
|
||||
|
||||
When submitting the initial authentication form, GitLab returns a status code 422
|
||||
error when it can't determine the role you've provided. Make sure you've
|
||||
correctly configured your role with the **Account ID** and **External ID**
|
||||
provided by GitLab. In GitLab, make sure to enter the correct **Role ARN**.
|
||||
|
||||
#### Could not load Security Groups for this VPC
|
||||
|
||||
When populating options in the configuration form, GitLab returns this error
|
||||
because GitLab has successfully assumed your provided role, but the role has
|
||||
insufficient permissions to retrieve the resources needed for the form. Make sure
|
||||
you've assigned the role the correct permissions.
|
||||
|
||||
#### `ROLLBACK_FAILED` during cluster creation
|
||||
|
||||
The creation process halted because GitLab encountered an error when creating
|
||||
one or more resources. You can inspect the associated
|
||||
[CloudFormation stack](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-console-view-stack-data-resources.html)
|
||||
to find the specific resources that failed to create.
|
||||
|
||||
If the `Cluster` resource failed with the error
|
||||
`The provided role doesn't have the Amazon EKS Managed Policies associated with it.`,
|
||||
the role specified in **Role name** is not configured correctly.
|
||||
|
||||
NOTE: **Note:**
|
||||
This role should not be the same as the one created above. If you don't have an
|
||||
existing
|
||||
[EKS cluster IAM role](https://docs.aws.amazon.com/eks/latest/userguide/service_IAM_role.html),
|
||||
you must create one.
|
||||
|
||||
## Existing EKS cluster
|
||||
|
||||
To add an existing EKS cluster to your project, group, or instance:
|
||||
|
|
|
@ -100,7 +100,7 @@ When you create a project in GitLab, you'll have access to a large number of
|
|||
- [Maven packages](../packages/maven_repository/index.md): your private Maven repository in GitLab. **(PREMIUM)**
|
||||
- [NPM packages](../packages/npm_registry/index.md): your private NPM package registry in GitLab. **(PREMIUM)**
|
||||
- [Code owners](code_owners.md): specify code owners for certain files **(STARTER)**
|
||||
- [License Compliance](../compliance/license_compliance/index.md): approve and blacklist licenses for projects. **(ULTIMATE)**
|
||||
- [License Compliance](../compliance/license_compliance/index.md): approve and deny licenses for projects. **(ULTIMATE)**
|
||||
- [Dependency List](../application_security/dependency_list/index.md): view project dependencies. **(ULTIMATE)**
|
||||
- [Requirements](requirements/index.md): Requirements allow you to create criteria to check your products against. **(ULTIMATE)**
|
||||
- [Static Site Editor](static_site_editor/index.md): quickly edit content on static websites without prior knowledge of the codebase or Git commands.
|
||||
|
|
|
@ -385,7 +385,7 @@ The following tables outline the details of expected properties.
|
|||
|
||||
| Property | Type | Required | Description |
|
||||
| -------- | ---- | -------- | ----------- |
|
||||
| `variables` | hash | no | Variables can be defined here. |
|
||||
| `variables` | hash | yes | Variables can be defined here. |
|
||||
|
||||
Read the documentation on [templating](#templating-variables-for-metrics-dashboards).
|
||||
|
||||
|
|
|
@ -11,13 +11,6 @@ module API
|
|||
expose :version, :revision, :platform, :architecture
|
||||
expose :contacted_at
|
||||
|
||||
# Will be removed: https://gitlab.com/gitlab-org/gitlab/-/issues/217105
|
||||
expose(:token, if: ->(runner, options) do
|
||||
return false if ::Feature.enabled?(:hide_token_from_runners_api, default_enabled: true)
|
||||
|
||||
options[:current_user].admin? || !runner.instance_type?
|
||||
end)
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
expose :projects, with: Entities::BasicProjectDetails do |runner, options|
|
||||
if options[:current_user].admin?
|
||||
|
|
|
@ -45,7 +45,7 @@ module Gitlab
|
|||
|
||||
def license_usage_data
|
||||
{
|
||||
recorded_at: Time.now, # should be calculated very first
|
||||
recorded_at: recorded_at,
|
||||
uuid: alt_usage_data { Gitlab::CurrentSettings.uuid },
|
||||
hostname: alt_usage_data { Gitlab.config.gitlab.host },
|
||||
version: alt_usage_data { Gitlab::VERSION },
|
||||
|
@ -55,6 +55,10 @@ module Gitlab
|
|||
}
|
||||
end
|
||||
|
||||
def recorded_at
|
||||
Time.now
|
||||
end
|
||||
|
||||
def recording_ce_finish_data
|
||||
{
|
||||
recording_ce_finished_at: Time.now
|
||||
|
|
|
@ -8248,6 +8248,9 @@ msgstr ""
|
|||
msgid "Enable header and footer in emails"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable integration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable maintenance mode"
|
||||
msgstr ""
|
||||
|
||||
|
@ -15118,6 +15121,15 @@ msgstr ""
|
|||
msgid "On track"
|
||||
msgstr ""
|
||||
|
||||
msgid "OnDemandScans|Create new DAST scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "OnDemandScans|On-demand Scans"
|
||||
msgstr ""
|
||||
|
||||
msgid "OnDemandScans|Schedule or run scans immediately against target sites. Currently available on-demand scan type: DAST. %{helpLinkStart}More information%{helpLinkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Onboarding"
|
||||
msgstr ""
|
||||
|
||||
|
@ -25495,7 +25507,7 @@ msgstr ""
|
|||
msgid "You have declined the invitation to join %{label}."
|
||||
msgstr ""
|
||||
|
||||
msgid "You have imported from this project %{numberOfPreviousImportsForProject} times before. Each new import will create duplicate issues."
|
||||
msgid "You have imported from this project %{numberOfPreviousImports} times before. Each new import will create duplicate issues."
|
||||
msgstr ""
|
||||
|
||||
msgid "You have no permissions"
|
||||
|
@ -25923,6 +25935,9 @@ msgstr ""
|
|||
msgid "ago"
|
||||
msgstr ""
|
||||
|
||||
msgid "alert"
|
||||
msgstr ""
|
||||
|
||||
msgid "allowed to fail"
|
||||
msgstr ""
|
||||
|
||||
|
@ -26235,6 +26250,9 @@ msgstr ""
|
|||
msgid "commit %{commit_id}"
|
||||
msgstr ""
|
||||
|
||||
msgid "committed"
|
||||
msgstr ""
|
||||
|
||||
msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -2,12 +2,10 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'Admin activates Prometheus' do
|
||||
describe 'Admin activates Prometheus', :js do
|
||||
let(:admin) { create(:user, :admin) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(integration_form_refactor: false)
|
||||
|
||||
sign_in(admin)
|
||||
|
||||
visit(admin_application_settings_services_path)
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Disable individual triggers' do
|
||||
RSpec.describe 'Disable individual triggers', :js do
|
||||
include_context 'project service activation'
|
||||
|
||||
let(:checkbox_selector) { 'input[type=checkbox][id$=_events]' }
|
||||
let(:checkbox_selector) { 'input[type=checkbox][name$="_events]"]' }
|
||||
|
||||
before do
|
||||
visit_project_integration(service_name)
|
||||
|
|
|
@ -23,6 +23,6 @@ RSpec.describe 'User activates Atlassian Bamboo CI' do
|
|||
# Password field should not be filled in.
|
||||
click_link('Atlassian Bamboo CI')
|
||||
|
||||
expect(find_field('Enter new password').value).to be_blank
|
||||
expect(find_field('Enter new Password').value).to be_blank
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ RSpec.describe 'User activates JetBrains TeamCity CI' do
|
|||
it 'activates service', :js do
|
||||
visit_project_integration('JetBrains TeamCity CI')
|
||||
check('Push')
|
||||
check('Merge request')
|
||||
check('Merge Request')
|
||||
fill_in('Teamcity url', with: 'http://teamcity.example.com')
|
||||
fill_in('Build type', with: 'GitlabTest_Build')
|
||||
fill_in('Username', with: 'user')
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'User activates Slack notifications' do
|
||||
RSpec.describe 'User activates Slack notifications', :js do
|
||||
include_context 'project service activation'
|
||||
|
||||
context 'when service is not configured yet' do
|
||||
|
@ -10,7 +10,7 @@ RSpec.describe 'User activates Slack notifications' do
|
|||
visit_project_integration('Slack notifications')
|
||||
end
|
||||
|
||||
it 'activates service', :js do
|
||||
it 'activates service' do
|
||||
fill_in('Webhook', with: 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685')
|
||||
|
||||
click_test_then_save_integration
|
||||
|
@ -38,13 +38,13 @@ RSpec.describe 'User activates Slack notifications' do
|
|||
end
|
||||
|
||||
it 'filters events by channel' do
|
||||
expect(page.find_field('service_push_channel').value).to have_content('1')
|
||||
expect(page.find_field('service_issue_channel').value).to have_content('2')
|
||||
expect(page.find_field('service_merge_request_channel').value).to have_content('3')
|
||||
expect(page.find_field('service_note_channel').value).to have_content('4')
|
||||
expect(page.find_field('service_tag_push_channel').value).to have_content('5')
|
||||
expect(page.find_field('service_pipeline_channel').value).to have_content('6')
|
||||
expect(page.find_field('service_wiki_page_channel').value).to have_content('7')
|
||||
expect(page.find_field(name: 'service[push_channel]').value).to have_content('1')
|
||||
expect(page.find_field(name: 'service[issue_channel]').value).to have_content('2')
|
||||
expect(page.find_field(name: 'service[merge_request_channel]').value).to have_content('3')
|
||||
expect(page.find_field(name: 'service[note_channel]').value).to have_content('4')
|
||||
expect(page.find_field(name: 'service[tag_push_channel]').value).to have_content('5')
|
||||
expect(page.find_field(name: 'service[pipeline_channel]').value).to have_content('6')
|
||||
expect(page.find_field(name: 'service[wiki_page_channel]').value).to have_content('7')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Slack slash commands' do
|
||||
RSpec.describe 'Slack slash commands', :js do
|
||||
include_context 'project service activation'
|
||||
|
||||
before do
|
||||
|
@ -10,7 +10,7 @@ RSpec.describe 'Slack slash commands' do
|
|||
end
|
||||
|
||||
it 'shows a token placeholder' do
|
||||
token_placeholder = find_field('service_token')['placeholder']
|
||||
token_placeholder = find_field('Token')['placeholder']
|
||||
|
||||
expect(token_placeholder).to eq('XXxxXXxxXXxxXXxxXXxxXXxx')
|
||||
end
|
||||
|
@ -19,8 +19,8 @@ RSpec.describe 'Slack slash commands' do
|
|||
expect(page).to have_content('This service allows users to perform common')
|
||||
end
|
||||
|
||||
it 'redirects to the integrations page after saving but not activating', :js do
|
||||
fill_in 'service_token', with: 'token'
|
||||
it 'redirects to the integrations page after saving but not activating' do
|
||||
fill_in 'Token', with: 'token'
|
||||
click_active_toggle
|
||||
click_on 'Save'
|
||||
|
||||
|
@ -28,8 +28,8 @@ RSpec.describe 'Slack slash commands' do
|
|||
expect(page).to have_content('Slack slash commands settings saved, but not activated.')
|
||||
end
|
||||
|
||||
it 'redirects to the integrations page after activating', :js do
|
||||
fill_in 'service_token', with: 'token'
|
||||
it 'redirects to the integrations page after activating' do
|
||||
fill_in 'Token', with: 'token'
|
||||
click_on 'Save'
|
||||
|
||||
expect(current_path).to eq(project_settings_integrations_path(project))
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
dashboard: 'Test Dashboard'
|
||||
priority: 1
|
||||
links:
|
||||
- title: Link 1
|
||||
url: https://gitlab.com
|
||||
- title: Link 2
|
||||
url: https://docs.gitlab.com
|
||||
templating:
|
||||
variables:
|
||||
text_variable_full_syntax:
|
||||
label: 'Variable 1'
|
||||
type: text
|
||||
options:
|
||||
default_value: 'default'
|
||||
text_variable_simple_syntax: 'default value'
|
||||
panel_groups:
|
||||
- group: Group A
|
||||
priority: 1
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
"panel_groups": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panel_groups.json" }
|
||||
}
|
||||
},
|
||||
"templating": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/templating.json" },
|
||||
"links": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/links.json" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"type": "array",
|
||||
"required": ["url"],
|
||||
"properties": {
|
||||
"url": { "type": "string" },
|
||||
"title": { "type": "string" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"type": "object",
|
||||
"required": ["variables"],
|
||||
"properties": {
|
||||
"variables": { "$ref": "variables.json" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
12
spec/fixtures/lib/gitlab/metrics/dashboard/schemas/text_variable_full_syntax.json
vendored
Normal file
12
spec/fixtures/lib/gitlab/metrics/dashboard/schemas/text_variable_full_syntax.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type", "options"
|
||||
],
|
||||
"properties": {
|
||||
"type": { "type": "string" },
|
||||
"label": { "type": "string" },
|
||||
"options": { "$ref": "text_variable_options.json" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
7
spec/fixtures/lib/gitlab/metrics/dashboard/schemas/text_variable_options.json
vendored
Normal file
7
spec/fixtures/lib/gitlab/metrics/dashboard/schemas/text_variable_options.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default_value": { "type": "string" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9_]*$": {
|
||||
"anyOf": [
|
||||
{ "$ref": "text_variable_full_syntax.json" },
|
||||
{ "type": "string" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
|
@ -6,14 +6,13 @@ import JiraImportForm from '~/jira_import/components/jira_import_form.vue';
|
|||
import JiraImportProgress from '~/jira_import/components/jira_import_progress.vue';
|
||||
import JiraImportSetup from '~/jira_import/components/jira_import_setup.vue';
|
||||
import initiateJiraImportMutation from '~/jira_import/queries/initiate_jira_import.mutation.graphql';
|
||||
import { IMPORT_STATE } from '~/jira_import/utils';
|
||||
|
||||
const mountComponent = ({
|
||||
isJiraConfigured = true,
|
||||
errorMessage = '',
|
||||
selectedProject = 'MTG',
|
||||
showAlert = false,
|
||||
status = IMPORT_STATE.NONE,
|
||||
isInProgress = false,
|
||||
loading = false,
|
||||
mutate = jest.fn(() => Promise.resolve()),
|
||||
mountType,
|
||||
|
@ -22,8 +21,8 @@ const mountComponent = ({
|
|||
|
||||
return mountFunction(JiraImportApp, {
|
||||
propsData: {
|
||||
isJiraConfigured,
|
||||
inProgressIllustration: 'in-progress-illustration.svg',
|
||||
isJiraConfigured,
|
||||
issuesPath: 'gitlab-org/gitlab-test/-/issues',
|
||||
jiraIntegrationPath: 'gitlab-org/gitlab-test/-/services/jira/edit',
|
||||
projectPath: 'gitlab-org/gitlab-test',
|
||||
|
@ -35,12 +34,7 @@ const mountComponent = ({
|
|||
showAlert,
|
||||
selectedProject,
|
||||
jiraImportDetails: {
|
||||
projects: [
|
||||
{ text: 'My Jira Project (MJP)', value: 'MJP' },
|
||||
{ text: 'My Second Jira Project (MSJP)', value: 'MSJP' },
|
||||
{ text: 'Migrate to GitLab (MTG)', value: 'MTG' },
|
||||
],
|
||||
status,
|
||||
isInProgress,
|
||||
imports: [
|
||||
{
|
||||
jiraProjectKey: 'MTG',
|
||||
|
@ -64,6 +58,18 @@ const mountComponent = ({
|
|||
},
|
||||
},
|
||||
],
|
||||
mostRecentImport: {
|
||||
jiraProjectKey: 'MTG',
|
||||
scheduledAt: '2020-04-09T16:17:18+00:00',
|
||||
scheduledBy: {
|
||||
name: 'Jane Doe',
|
||||
},
|
||||
},
|
||||
projects: [
|
||||
{ text: 'My Jira Project (MJP)', value: 'MJP' },
|
||||
{ text: 'My Second Jira Project (MSJP)', value: 'MSJP' },
|
||||
{ text: 'Migrate to GitLab (MTG)', value: 'MTG' },
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -140,7 +146,7 @@ describe('JiraImportApp', () => {
|
|||
|
||||
describe('when Jira integration is configured but import is in progress', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mountComponent({ status: IMPORT_STATE.SCHEDULED });
|
||||
wrapper = mountComponent({ isInProgress: true });
|
||||
});
|
||||
|
||||
it('does not show the "Set up Jira integration" screen', () => {
|
||||
|
@ -184,7 +190,7 @@ describe('JiraImportApp', () => {
|
|||
|
||||
describe('import in progress screen', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mountComponent({ status: IMPORT_STATE.SCHEDULED });
|
||||
wrapper = mountComponent({ isInProgress: true });
|
||||
});
|
||||
|
||||
it('shows the illustration', () => {
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import getJiraImportDetailsQuery from '~/jira_import/queries/get_jira_import_details.query.graphql';
|
||||
import { IMPORT_STATE } from '~/jira_import/utils/jira_import_utils';
|
||||
|
||||
export const fullPath = 'gitlab-org/gitlab-test';
|
||||
|
||||
export const queryDetails = {
|
||||
query: getJiraImportDetailsQuery,
|
||||
variables: {
|
||||
fullPath,
|
||||
},
|
||||
};
|
||||
|
||||
export const jiraImportDetailsQueryResponse = {
|
||||
project: {
|
||||
jiraImportStatus: IMPORT_STATE.NONE,
|
||||
jiraImports: {
|
||||
nodes: [
|
||||
{
|
||||
jiraProjectKey: 'MJP',
|
||||
scheduledAt: '2020-01-01T12:34:56Z',
|
||||
scheduledBy: {
|
||||
name: 'Jane Doe',
|
||||
__typename: 'User',
|
||||
},
|
||||
__typename: 'JiraImport',
|
||||
},
|
||||
],
|
||||
__typename: 'JiraImportConnection',
|
||||
},
|
||||
services: {
|
||||
nodes: [
|
||||
{
|
||||
projects: {
|
||||
nodes: [
|
||||
{
|
||||
key: 'MJP',
|
||||
name: 'My Jira Project',
|
||||
__typename: 'JiraProject',
|
||||
},
|
||||
{
|
||||
key: 'MTG',
|
||||
name: 'Migrate To GitLab',
|
||||
__typename: 'JiraProject',
|
||||
},
|
||||
],
|
||||
__typename: 'JiraProjectConnection',
|
||||
},
|
||||
__typename: 'JiraService',
|
||||
},
|
||||
],
|
||||
__typename: 'ServiceConnection',
|
||||
},
|
||||
__typename: 'Project',
|
||||
},
|
||||
};
|
||||
|
||||
export const jiraImportMutationResponse = {
|
||||
jiraImportStart: {
|
||||
clientMutationId: null,
|
||||
jiraImport: {
|
||||
jiraProjectKey: 'MTG',
|
||||
scheduledAt: '2020-02-02T20:20:20Z',
|
||||
scheduledBy: {
|
||||
name: 'John Doe',
|
||||
__typename: 'User',
|
||||
},
|
||||
__typename: 'JiraImport',
|
||||
},
|
||||
errors: [],
|
||||
__typename: 'JiraImportStartPayload',
|
||||
},
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
import { addInProgressImportToStore } from '~/jira_import/utils/cache_update';
|
||||
import { IMPORT_STATE } from '~/jira_import/utils/jira_import_utils';
|
||||
import {
|
||||
fullPath,
|
||||
queryDetails,
|
||||
jiraImportDetailsQueryResponse,
|
||||
jiraImportMutationResponse,
|
||||
} from '../mock_data';
|
||||
|
||||
describe('addInProgressImportToStore', () => {
|
||||
const store = {
|
||||
readQuery: jest.fn(() => jiraImportDetailsQueryResponse),
|
||||
writeQuery: jest.fn(),
|
||||
};
|
||||
|
||||
describe('when updating the cache', () => {
|
||||
beforeEach(() => {
|
||||
addInProgressImportToStore(store, jiraImportMutationResponse.jiraImportStart, fullPath);
|
||||
});
|
||||
|
||||
it('reads the cache with the correct query', () => {
|
||||
expect(store.readQuery).toHaveBeenCalledWith(queryDetails);
|
||||
});
|
||||
|
||||
it('writes to the cache with the expected arguments', () => {
|
||||
const expected = {
|
||||
...queryDetails,
|
||||
data: {
|
||||
project: {
|
||||
...jiraImportDetailsQueryResponse.project,
|
||||
jiraImportStatus: IMPORT_STATE.SCHEDULED,
|
||||
jiraImports: {
|
||||
...jiraImportDetailsQueryResponse.project.jiraImports,
|
||||
nodes: jiraImportDetailsQueryResponse.project.jiraImports.nodes.concat(
|
||||
jiraImportMutationResponse.jiraImportStart.jiraImport,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(store.writeQuery).toHaveBeenCalledWith(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are errors', () => {
|
||||
beforeEach(() => {
|
||||
const jiraImportStart = {
|
||||
...jiraImportMutationResponse.jiraImportStart,
|
||||
errors: ['There was an error'],
|
||||
};
|
||||
|
||||
addInProgressImportToStore(store, jiraImportStart, fullPath);
|
||||
});
|
||||
|
||||
it('does not read from the store', () => {
|
||||
expect(store.readQuery).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not write to the store', () => {
|
||||
expect(store.writeQuery).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -4,7 +4,7 @@ import {
|
|||
IMPORT_STATE,
|
||||
isFinished,
|
||||
isInProgress,
|
||||
} from '~/jira_import/utils';
|
||||
} from '~/jira_import/utils/jira_import_utils';
|
||||
|
||||
describe('isInProgress', () => {
|
||||
it.each`
|
|
@ -20,6 +20,10 @@ describe TodosHelper do
|
|||
author: author,
|
||||
note: note)
|
||||
end
|
||||
let_it_be(:alert_todo) do
|
||||
alert = create(:alert_management_alert, iid: 1001)
|
||||
create(:todo, target: alert)
|
||||
end
|
||||
|
||||
describe '#todos_count_format' do
|
||||
it 'shows fuzzy count for 100 or more items' do
|
||||
|
@ -115,6 +119,18 @@ describe TodosHelper do
|
|||
expect(path).to eq("#{issue_path}/designs/#{design.filename}##{dom_id(design_todo.note)}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given an alert' do
|
||||
let(:todo) { alert_todo }
|
||||
|
||||
it 'responds with an appropriate path' do
|
||||
path = helper.todo_target_path(todo)
|
||||
|
||||
expect(path).to eq(
|
||||
"/#{todo.project.full_path}/-/alert_management/#{todo.target.iid}/details"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#todo_target_type_name' do
|
||||
|
@ -127,6 +143,16 @@ describe TodosHelper do
|
|||
expect(name).to eq('design')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given an alert todo' do
|
||||
let(:todo) { alert_todo }
|
||||
|
||||
it 'responds with an appropriate target type name' do
|
||||
name = helper.todo_target_type_name(todo)
|
||||
|
||||
expect(name).to eq('alert')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#todo_types_options' do
|
||||
|
|
|
@ -10,6 +10,17 @@ describe Gitlab::UsageData, :aggregate_failures do
|
|||
stub_object_store_settings
|
||||
end
|
||||
|
||||
describe '#uncached_data' do
|
||||
it 'ensures recorded_at is set before any other usage data calculation' do
|
||||
%i(alt_usage_data redis_usage_data distinct_count count).each do |method|
|
||||
expect(described_class).not_to receive(method)
|
||||
end
|
||||
expect(described_class).to receive(:recorded_at).and_raise(Exception.new('Stopped calculating recorded_at'))
|
||||
|
||||
expect { described_class.uncached_data }.to raise_error('Stopped calculating recorded_at')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#data' do
|
||||
let!(:ud) { build(:usage_data) }
|
||||
|
||||
|
|
|
@ -242,6 +242,12 @@ describe AlertManagement::Alert do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#to_reference' do
|
||||
let(:alert) { build(:alert_management_alert) }
|
||||
|
||||
it { expect(alert.to_reference).to eq('') }
|
||||
end
|
||||
|
||||
describe '#trigger' do
|
||||
subject { alert.trigger }
|
||||
|
||||
|
|
|
@ -47,6 +47,24 @@ describe PerformanceMonitoring::PrometheusDashboard do
|
|||
end
|
||||
end
|
||||
|
||||
context 'dashboard content is missing' do
|
||||
let(:json_content) { nil }
|
||||
|
||||
it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"]
|
||||
end
|
||||
|
||||
context 'dashboard content is NOT a hash' do
|
||||
let(:json_content) { YAML.safe_load("'test'") }
|
||||
|
||||
it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"]
|
||||
end
|
||||
|
||||
context 'content is an array' do
|
||||
let(:json_content) { [{ "dashboard" => "Dashboard Title" }] }
|
||||
|
||||
it_behaves_like 'validation failed', panel_groups: ["can't be blank"], dashboard: ["can't be blank"]
|
||||
end
|
||||
|
||||
context 'dashboard definition is missing panels_groups and dashboard keys' do
|
||||
let(:json_content) do
|
||||
{
|
||||
|
|
|
@ -24,6 +24,14 @@ describe PerformanceMonitoring::PrometheusMetric do
|
|||
end
|
||||
|
||||
describe 'validations' do
|
||||
context 'json_content is not a hash' do
|
||||
let(:json_content) { nil }
|
||||
|
||||
subject { described_class.from_json(json_content) }
|
||||
|
||||
it { expect { subject }.to raise_error(ActiveModel::ValidationError) }
|
||||
end
|
||||
|
||||
context 'when unit is missing' do
|
||||
before do
|
||||
json_content['unit'] = nil
|
||||
|
|
|
@ -30,6 +30,14 @@ describe PerformanceMonitoring::PrometheusPanelGroup do
|
|||
end
|
||||
|
||||
describe 'validations' do
|
||||
context 'json_content is not a hash' do
|
||||
let(:json_content) { nil }
|
||||
|
||||
subject { described_class.from_json(json_content) }
|
||||
|
||||
it { expect { subject }.to raise_error(ActiveModel::ValidationError) }
|
||||
end
|
||||
|
||||
context 'when group is missing' do
|
||||
before do
|
||||
json_content.delete('group')
|
||||
|
|
|
@ -42,6 +42,14 @@ describe PerformanceMonitoring::PrometheusPanel do
|
|||
end
|
||||
|
||||
describe 'validations' do
|
||||
context 'json_content is not a hash' do
|
||||
let(:json_content) { nil }
|
||||
|
||||
subject { described_class.from_json(json_content) }
|
||||
|
||||
it { expect { subject }.to raise_error(ActiveModel::ValidationError) }
|
||||
end
|
||||
|
||||
context 'when title is missing' do
|
||||
before do
|
||||
json_content['title'] = nil
|
||||
|
|
|
@ -100,6 +100,20 @@ describe Todo do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#for_alert?' do
|
||||
it 'returns true when target is a Alert' do
|
||||
subject.target_type = 'AlertManagement::Alert'
|
||||
|
||||
expect(subject.for_alert?).to eq(true)
|
||||
end
|
||||
|
||||
it 'returns false when target is not a Alert' do
|
||||
subject.target_type = 'Issue'
|
||||
|
||||
expect(subject.for_alert?).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#target' do
|
||||
context 'for commits' do
|
||||
let(:project) { create(:project, :repository) }
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::BlamePresenter do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:path) { 'files/ruby/popen.rb' }
|
||||
let(:commit) { project.commit('master') }
|
||||
let(:blob) { project.repository.blob_at(commit.id, path) }
|
||||
let(:blame) { Gitlab::Blame.new(blob, commit) }
|
||||
|
||||
subject { described_class.new(blame, project: project, path: path) }
|
||||
|
||||
it 'precalculates necessary data on init' do
|
||||
expect_any_instance_of(described_class)
|
||||
.to receive(:precalculate_data_by_commit!)
|
||||
.and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
describe '#groups' do
|
||||
it 'delegates #groups call to the blame' do
|
||||
expect(blame).to receive(:groups).and_call_original
|
||||
|
||||
subject.groups
|
||||
end
|
||||
end
|
||||
|
||||
describe '#commit_data' do
|
||||
it 'has the data necessary to render the view' do
|
||||
commit = blame.groups.first[:commit]
|
||||
data = subject.commit_data(commit)
|
||||
|
||||
aggregate_failures do
|
||||
expect(data.author_avatar.to_s).to include('src="https://www.gravatar.com/')
|
||||
expect(data.age_map_class).to include('blame-commit-age-')
|
||||
expect(data.commit_link.to_s).to include '913c66a37b4a45b9769037c55c2d238bd0942d2e">Files, encoding and much more</a>'
|
||||
expect(data.commit_author_link.to_s).to include('<a class="commit-author-link" href=')
|
||||
expect(data.project_blame_link.to_s).to include('<a title="View blame prior to this change"')
|
||||
expect(data.time_ago_tooltip.to_s).to include('data-container="body">Feb 27, 2014</time>')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -326,32 +326,6 @@ describe API::Runners do
|
|||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'FF hide_token_from_runners_api is enabled' do
|
||||
before do
|
||||
stub_feature_flags(hide_token_from_runners_api: true)
|
||||
end
|
||||
|
||||
it "does not return runner's token" do
|
||||
get api("/runners/#{shared_runner.id}", admin)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response).not_to have_key('token')
|
||||
end
|
||||
end
|
||||
|
||||
context 'FF hide_token_from_runners_api is disabled' do
|
||||
before do
|
||||
stub_feature_flags(hide_token_from_runners_api: false)
|
||||
end
|
||||
|
||||
it "returns runner's token" do
|
||||
get api("/runners/#{shared_runner.id}", admin)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response).to have_key('token')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /runners/:id' do
|
||||
|
|
|
@ -5,7 +5,6 @@ shared_context 'project service activation' do
|
|||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(integration_form_refactor: false)
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue