Commit 8e61e9a1 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 49551dbc 0b0eddca
...@@ -14,7 +14,7 @@ import { ...@@ -14,7 +14,7 @@ import {
} from '~/behaviors/shortcuts/keybindings'; } from '~/behaviors/shortcuts/keybindings';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { isSingleViewStyle } from '~/helpers/diffs_helper'; import { isSingleViewStyle } from '~/helpers/diffs_helper';
import { getParameterByName, parseBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
import { updateHistory } from '~/lib/utils/url_utility'; import { updateHistory } from '~/lib/utils/url_utility';
import { __ } from '~/locale'; import { __ } from '~/locale';
import PanelResizer from '~/vue_shared/components/panel_resizer.vue'; import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
...@@ -192,6 +192,7 @@ export default { ...@@ -192,6 +192,7 @@ export default {
'showTreeList', 'showTreeList',
'isLoading', 'isLoading',
'startVersion', 'startVersion',
'latestDiff',
'currentDiffFileId', 'currentDiffFileId',
'isTreeLoaded', 'isTreeLoaded',
'conflictResolutionPath', 'conflictResolutionPath',
...@@ -234,8 +235,8 @@ export default { ...@@ -234,8 +235,8 @@ export default {
isLimitedContainer() { isLimitedContainer() {
return !this.renderFileTree && !this.isParallelView && !this.isFluidLayout; return !this.renderFileTree && !this.isParallelView && !this.isFluidLayout;
}, },
isDiffHead() { isFullChangeset() {
return parseBoolean(getParameterByName('diff_head')); return this.startVersion === null && this.latestDiff;
}, },
showFileByFileNavigation() { showFileByFileNavigation() {
return this.diffFiles.length > 1 && this.viewDiffsFileByFile; return this.diffFiles.length > 1 && this.viewDiffsFileByFile;
...@@ -258,7 +259,7 @@ export default { ...@@ -258,7 +259,7 @@ export default {
if (this.renderOverflowWarning) { if (this.renderOverflowWarning) {
visible = this.$options.alerts.ALERT_OVERFLOW_HIDDEN; visible = this.$options.alerts.ALERT_OVERFLOW_HIDDEN;
} else if (this.isDiffHead && this.hasConflicts) { } else if (this.isFullChangeset && this.hasConflicts) {
visible = this.$options.alerts.ALERT_MERGE_CONFLICT; visible = this.$options.alerts.ALERT_MERGE_CONFLICT;
} else if (this.whichCollapsedTypes.automatic && !this.viewDiffsFileByFile) { } else if (this.whichCollapsedTypes.automatic && !this.viewDiffsFileByFile) {
visible = this.$options.alerts.ALERT_COLLAPSED_FILES; visible = this.$options.alerts.ALERT_COLLAPSED_FILES;
......
...@@ -9,7 +9,11 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController ...@@ -9,7 +9,11 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
def index def index
set_index_vars set_index_vars
@personal_access_token = finder.build scopes = params[:scopes].split(',').map(&:squish).select(&:present?).map(&:to_sym) unless params[:scopes].nil?
@personal_access_token = finder.build(
name: params[:name],
scopes: scopes
)
end end
def create def create
......
...@@ -11,6 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w ...@@ -11,6 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Notifications for expiring tokens](https://gitlab.com/gitlab-org/gitlab/-/issues/3649) added in GitLab 12.6. > - [Notifications for expiring tokens](https://gitlab.com/gitlab-org/gitlab/-/issues/3649) added in GitLab 12.6.
> - [Token lifetime limits](https://gitlab.com/gitlab-org/gitlab/-/issues/3649) added in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.6. > - [Token lifetime limits](https://gitlab.com/gitlab-org/gitlab/-/issues/3649) added in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.6.
> - [Additional notifications for expiring tokens](https://gitlab.com/gitlab-org/gitlab/-/issues/214721) added in GitLab 13.3. > - [Additional notifications for expiring tokens](https://gitlab.com/gitlab-org/gitlab/-/issues/214721) added in GitLab 13.3.
> - [Prefill token name and scopes](https://gitlab.com/gitlab-org/gitlab/-/issues/334664) added in GitLab 14.1.
If you're unable to use [OAuth2](../../api/oauth2.md), you can use a personal access token to authenticate with the [GitLab API](../../api/index.md#personalproject-access-tokens). You can also use a personal access token with Git to authenticate over HTTP. If you're unable to use [OAuth2](../../api/oauth2.md), you can use a personal access token to authenticate with the [GitLab API](../../api/index.md#personalproject-access-tokens). You can also use a personal access token with Git to authenticate over HTTP.
...@@ -37,6 +38,16 @@ You can create as many personal access tokens as you like. ...@@ -37,6 +38,16 @@ You can create as many personal access tokens as you like.
Save the personal access token somewhere safe. After you leave the page, Save the personal access token somewhere safe. After you leave the page,
you no longer have access to the token. you no longer have access to the token.
### Prefill personal access token name and scopes
You can link directly to the Personal Access Token page and have the form prefilled with a name and
list of scopes. To do this, you can append a `name` parameter and a list of comma-separated scopes
to the URL. For example:
```plaintext
https://gitlab.example.com/-/profile/personal_access_tokens?name=Example+Access+token&scopes=api,read_user,read_registry
```
## Revoke a personal access token ## Revoke a personal access token
At any time, you can revoke a personal access token. At any time, you can revoke a personal access token.
......
...@@ -65,8 +65,10 @@ export default { ...@@ -65,8 +65,10 @@ export default {
return this.projectFullPath && this.pipelineIid && this.securityReportSummary.coverageFuzzing; return this.projectFullPath && this.pipelineIid && this.securityReportSummary.coverageFuzzing;
}, },
canCreateIssue() { canCreateIssue() {
const path = this.vulnerability.create_vulnerability_feedback_issue_path; const gitLabIssuePath = this.vulnerability.create_vulnerability_feedback_issue_path;
return Boolean(path); const jiraIssueUrl = this.vulnerability.create_jira_issue_url;
return Boolean(gitLabIssuePath || jiraIssueUrl);
}, },
canCreateMergeRequest() { canCreateMergeRequest() {
const path = this.vulnerability.create_vulnerability_feedback_merge_request_path; const path = this.vulnerability.create_vulnerability_feedback_merge_request_path;
......
...@@ -154,6 +154,7 @@ describe('Security Dashboard component', () => { ...@@ -154,6 +154,7 @@ describe('Security Dashboard component', () => {
givenState | expectedProps givenState | expectedProps
${{ modal: { vulnerability: 'foo' } }} | ${{ modal: { vulnerability: 'foo' }, canCreateIssue: false, canCreateMergeRequest: false, canDismissVulnerability: false, isCreatingIssue: false, isDismissingVulnerability: false, isCreatingMergeRequest: false }} ${{ modal: { vulnerability: 'foo' } }} | ${{ modal: { vulnerability: 'foo' }, canCreateIssue: false, canCreateMergeRequest: false, canDismissVulnerability: false, isCreatingIssue: false, isDismissingVulnerability: false, isCreatingMergeRequest: false }}
${{ modal: { vulnerability: { create_vulnerability_feedback_issue_path: 'foo' } } }} | ${expect.objectContaining({ canCreateIssue: true })} ${{ modal: { vulnerability: { create_vulnerability_feedback_issue_path: 'foo' } } }} | ${expect.objectContaining({ canCreateIssue: true })}
${{ modal: { vulnerability: { create_jira_issue_url: 'foo' } } }} | ${expect.objectContaining({ canCreateIssue: true })}
${{ modal: { vulnerability: { create_vulnerability_feedback_merge_request_path: 'foo' } } }} | ${expect.objectContaining({ canCreateMergeRequest: true })} ${{ modal: { vulnerability: { create_vulnerability_feedback_merge_request_path: 'foo' } } }} | ${expect.objectContaining({ canCreateMergeRequest: true })}
${{ modal: { vulnerability: { create_vulnerability_feedback_dismissal_path: 'foo' } } }} | ${expect.objectContaining({ canDismissVulnerability: true })} ${{ modal: { vulnerability: { create_vulnerability_feedback_dismissal_path: 'foo' } } }} | ${expect.objectContaining({ canDismissVulnerability: true })}
${{ isCreatingIssue: true }} | ${expect.objectContaining({ isCreatingIssue: true })} ${{ isCreatingIssue: true }} | ${expect.objectContaining({ isCreatingIssue: true })}
......
...@@ -6,7 +6,7 @@ RSpec.describe Integrations::Github::StatusMessage do ...@@ -6,7 +6,7 @@ RSpec.describe Integrations::Github::StatusMessage do
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
let(:project) { double(:project, namespace: "me", to_s: 'example_project') } let(:project) { double(:project, namespace: "me", to_s: 'example_project') }
let(:service) { double(:service, static_context?: false) } let(:integration) { double(:integration, static_context?: false) }
before do before do
stub_config_setting(host: 'instance-host') stub_config_setting(host: 'instance-host')
...@@ -14,14 +14,14 @@ RSpec.describe Integrations::Github::StatusMessage do ...@@ -14,14 +14,14 @@ RSpec.describe Integrations::Github::StatusMessage do
describe '#description' do describe '#description' do
it 'includes human readable gitlab status' do it 'includes human readable gitlab status' do
subject = described_class.new(project, service, detailed_status: 'passed') subject = described_class.new(project, integration, detailed_status: 'passed')
expect(subject.description).to eq "Pipeline passed on GitLab" expect(subject.description).to eq "Pipeline passed on GitLab"
end end
it 'gets truncated to 140 chars' do it 'gets truncated to 140 chars' do
dummy_text = 'a' * 500 dummy_text = 'a' * 500
subject = described_class.new(project, service, detailed_status: dummy_text) subject = described_class.new(project, integration, detailed_status: dummy_text)
expect(subject.description.length).to eq 140 expect(subject.description.length).to eq 140
end end
...@@ -43,7 +43,7 @@ RSpec.describe Integrations::Github::StatusMessage do ...@@ -43,7 +43,7 @@ RSpec.describe Integrations::Github::StatusMessage do
with_them do with_them do
it 'transforms status' do it 'transforms status' do
subject = described_class.new(project, service, status: gitlab_status) subject = described_class.new(project, integration, status: gitlab_status)
expect(subject.status).to eq github_status expect(subject.status).to eq github_status
end end
...@@ -51,7 +51,7 @@ RSpec.describe Integrations::Github::StatusMessage do ...@@ -51,7 +51,7 @@ RSpec.describe Integrations::Github::StatusMessage do
end end
describe '#status_options' do describe '#status_options' do
let(:subject) { described_class.new(project, service, id: 1) } let(:subject) { described_class.new(project, integration, id: 1) }
it 'includes context' do it 'includes context' do
expect(subject.status_options[:context]).to be_a String expect(subject.status_options[:context]).to be_a String
...@@ -68,12 +68,12 @@ RSpec.describe Integrations::Github::StatusMessage do ...@@ -68,12 +68,12 @@ RSpec.describe Integrations::Github::StatusMessage do
describe '#context' do describe '#context' do
subject do subject do
described_class.new(project, service, ref: 'some-ref') described_class.new(project, integration, ref: 'some-ref')
end end
context 'when status context is supposed to be dynamic' do context 'when status context is supposed to be dynamic' do
before do before do
allow(service).to receive(:static_context?).and_return(false) allow(integration).to receive(:static_context?).and_return(false)
end end
it 'appends pipeline reference to the status context' do it 'appends pipeline reference to the status context' do
...@@ -83,7 +83,7 @@ RSpec.describe Integrations::Github::StatusMessage do ...@@ -83,7 +83,7 @@ RSpec.describe Integrations::Github::StatusMessage do
context 'when status context is supposed to be static' do context 'when status context is supposed to be static' do
before do before do
allow(service).to receive(:static_context?).and_return(true) allow(integration).to receive(:static_context?).and_return(true)
end end
it 'appends instance hostname to the status context' do it 'appends instance hostname to the status context' do
...@@ -98,7 +98,7 @@ RSpec.describe Integrations::Github::StatusMessage do ...@@ -98,7 +98,7 @@ RSpec.describe Integrations::Github::StatusMessage do
let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) } let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
subject do subject do
described_class.from_pipeline_data(project, service, sample_data) described_class.from_pipeline_data(project, integration, sample_data)
end end
it 'builds an instance of Integrations::Github::StatusMessage' do it 'builds an instance of Integrations::Github::StatusMessage' do
...@@ -136,11 +136,11 @@ RSpec.describe Integrations::Github::StatusMessage do ...@@ -136,11 +136,11 @@ RSpec.describe Integrations::Github::StatusMessage do
context 'when static context has been configured' do context 'when static context has been configured' do
before do before do
allow(service).to receive(:static_context?).and_return(true) allow(integration).to receive(:static_context?).and_return(true)
end end
subject do subject do
described_class.from_pipeline_data(project, service, sample_data) described_class.from_pipeline_data(project, integration, sample_data)
end end
it 'appends instance name to the context name' do it 'appends instance name to the context name' do
......
...@@ -15,6 +15,7 @@ module Gitlab ...@@ -15,6 +15,7 @@ module Gitlab
# worker_class can either be the string or class of the worker being enqueued. # worker_class can either be the string or class of the worker being enqueued.
worker_class = worker_class.safe_constantize if worker_class.respond_to?(:safe_constantize) worker_class = worker_class.safe_constantize if worker_class.respond_to?(:safe_constantize)
labels = create_labels(worker_class, queue, job) labels = create_labels(worker_class, queue, job)
labels[:scheduling] = job.key?('at') ? 'delayed' : 'immediate'
@metrics.fetch(ENQUEUED).increment(labels, 1) @metrics.fetch(ENQUEUED).increment(labels, 1)
......
...@@ -64,5 +64,17 @@ RSpec.describe Profiles::PersonalAccessTokensController do ...@@ -64,5 +64,17 @@ RSpec.describe Profiles::PersonalAccessTokensController do
it "retrieves newly created personal access token value" do it "retrieves newly created personal access token value" do
expect(assigns(:new_personal_access_token)).to eql(token_value) expect(assigns(:new_personal_access_token)).to eql(token_value)
end end
it "sets PAT name and scopes" do
name = 'My PAT'
scopes = 'api,read_user'
get :index, params: { name: name, scopes: scopes }
expect(assigns(:personal_access_token)).to have_attributes(
name: eq(name),
scopes: contain_exactly(:api, :read_user)
)
end
end end
end end
...@@ -149,4 +149,15 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do ...@@ -149,4 +149,15 @@ RSpec.describe 'Profile > Personal Access Tokens', :js do
expect(page).to have_pushed_frontend_feature_flags(personalAccessTokensScopedToProjects: true) expect(page).to have_pushed_frontend_feature_flags(personalAccessTokensScopedToProjects: true)
end end
it "prefills token details" do
name = 'My PAT'
scopes = 'api,read_user'
visit profile_personal_access_tokens_path({ name: name, scopes: scopes })
expect(page).to have_field("Token name", with: name)
expect(find("#personal_access_token_scopes_api")).to be_checked
expect(find("#personal_access_token_scopes_read_user")).to be_checked
end
end end
...@@ -6,14 +6,19 @@ import Vue, { nextTick } from 'vue'; ...@@ -6,14 +6,19 @@ import Vue, { nextTick } from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { TEST_HOST } from 'spec/test_constants'; import { TEST_HOST } from 'spec/test_constants';
import App from '~/diffs/components/app.vue'; import App from '~/diffs/components/app.vue';
import CollapsedFilesWarning from '~/diffs/components/collapsed_files_warning.vue';
import CommitWidget from '~/diffs/components/commit_widget.vue'; import CommitWidget from '~/diffs/components/commit_widget.vue';
import CompareVersions from '~/diffs/components/compare_versions.vue'; import CompareVersions from '~/diffs/components/compare_versions.vue';
import DiffFile from '~/diffs/components/diff_file.vue'; import DiffFile from '~/diffs/components/diff_file.vue';
import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue';
import NoChanges from '~/diffs/components/no_changes.vue'; import NoChanges from '~/diffs/components/no_changes.vue';
import TreeList from '~/diffs/components/tree_list.vue'; import TreeList from '~/diffs/components/tree_list.vue';
/* eslint-disable import/order */
/* You know what: sometimes alphabetical isn't the best order */
import CollapsedFilesWarning from '~/diffs/components/collapsed_files_warning.vue';
import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue';
import MergeConflictWarning from '~/diffs/components/merge_conflict_warning.vue';
/* eslint-enable import/order */
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import * as urlUtils from '~/lib/utils/url_utility'; import * as urlUtils from '~/lib/utils/url_utility';
import createDiffsStore from '../create_diffs_store'; import createDiffsStore from '../create_diffs_store';
...@@ -541,6 +546,43 @@ describe('diffs/components/app', () => { ...@@ -541,6 +546,43 @@ describe('diffs/components/app', () => {
expect(getCollapsedFilesWarning(wrapper).exists()).toBe(false); expect(getCollapsedFilesWarning(wrapper).exists()).toBe(false);
}); });
}); });
describe('merge conflicts', () => {
it('should render the merge conflicts banner if viewing the whole changeset and there are conflicts', () => {
createComponent({}, ({ state }) => {
Object.assign(state.diffs, {
latestDiff: true,
startVersion: null,
hasConflicts: true,
canMerge: false,
conflictResolutionPath: 'path',
});
});
expect(wrapper.find(MergeConflictWarning).exists()).toBe(true);
});
it.each`
prop | value
${'latestDiff'} | ${false}
${'startVersion'} | ${'notnull'}
${'hasConflicts'} | ${false}
`(
"should not render if any of the MR properties aren't correct - like $prop: $value",
({ prop, value }) => {
createComponent({}, ({ state }) => {
Object.assign(state.diffs, {
latestDiff: true,
startVersion: null,
hasConflicts: true,
[prop]: value,
});
});
expect(wrapper.find(MergeConflictWarning).exists()).toBe(false);
},
);
});
}); });
it('should display commit widget if store has a commit', () => { it('should display commit widget if store has a commit', () => {
......
...@@ -3,11 +3,12 @@ ...@@ -3,11 +3,12 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::SidekiqMiddleware::ClientMetrics do RSpec.describe Gitlab::SidekiqMiddleware::ClientMetrics do
let(:enqueued_jobs_metric) { double('enqueued jobs metric', increment: true) }
shared_examples "a metrics middleware" do shared_examples "a metrics middleware" do
context "with mocked prometheus" do context "with mocked prometheus" do
let(:enqueued_jobs_metric) { double('enqueued jobs metric', increment: true) }
before do before do
labels[:scheduling] = 'immediate'
allow(Gitlab::Metrics).to receive(:counter).with(described_class::ENQUEUED, anything).and_return(enqueued_jobs_metric) allow(Gitlab::Metrics).to receive(:counter).with(described_class::ENQUEUED, anything).and_return(enqueued_jobs_metric)
end end
...@@ -32,4 +33,35 @@ RSpec.describe Gitlab::SidekiqMiddleware::ClientMetrics do ...@@ -32,4 +33,35 @@ RSpec.describe Gitlab::SidekiqMiddleware::ClientMetrics do
end end
it_behaves_like 'metrics middleware with worker attribution' it_behaves_like 'metrics middleware with worker attribution'
context 'when mounted' do
before do
stub_const('TestWorker', Class.new)
TestWorker.class_eval do
include Sidekiq::Worker
def perform(*args)
end
end
allow(Gitlab::Metrics).to receive(:counter).and_return(Gitlab::Metrics::NullMetric.instance)
allow(Gitlab::Metrics).to receive(:counter).with(described_class::ENQUEUED, anything).and_return(enqueued_jobs_metric)
end
context 'when scheduling jobs for immediate execution' do
it 'increments enqueued jobs metric with scheduling label set to immediate' do
expect(enqueued_jobs_metric).to receive(:increment).with(a_hash_including(scheduling: 'immediate'), 1)
Sidekiq::Testing.inline! { TestWorker.perform_async }
end
end
context 'when scheduling jobs for future execution' do
it 'increments enqueued jobs metric with scheduling label set to delayed' do
expect(enqueued_jobs_metric).to receive(:increment).with(a_hash_including(scheduling: 'delayed'), 1)
Sidekiq::Testing.inline! { TestWorker.perform_in(1.second) }
end
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment