Commit abeeb24c authored by Robert Speicher's avatar Robert Speicher

Merge branch '20422-hide-ui-variables-by-default' into 'master'

Resolve "Hide variables in UI by default"

Closes #20422

See merge request gitlab-org/gitlab-ce!23518
parents 8fd72c63 6de31cdd
<script> <script>
import { __ } from '~/locale';
import { GlButton } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
const HIDDEN_VALUE = '••••••';
export default { export default {
components: { components: {
GlButton, GlButton,
...@@ -13,17 +16,26 @@ export default { ...@@ -13,17 +16,26 @@ export default {
}, },
data() { data() {
return { return {
areVariablesVisible: false, showVariableValues: false,
}; };
}, },
computed: { computed: {
hasVariables() { hasVariables() {
return this.trigger.variables && this.trigger.variables.length > 0; return this.trigger.variables && this.trigger.variables.length > 0;
}, },
getToggleButtonText() {
return this.showVariableValues ? __('Hide values') : __('Reveal values');
},
hasValues() {
return this.trigger.variables.some(v => v.value);
},
}, },
methods: { methods: {
revealVariables() { toggleValues() {
this.areVariablesVisible = true; this.showVariableValues = !this.showVariableValues;
},
getDisplayValue(value) {
return this.showVariableValues ? value : HIDDEN_VALUE;
}, },
}, },
}; };
...@@ -33,31 +45,33 @@ export default { ...@@ -33,31 +45,33 @@ export default {
<div class="build-widget block"> <div class="build-widget block">
<h4 class="title">{{ __('Trigger') }}</h4> <h4 class="title">{{ __('Trigger') }}</h4>
<p v-if="trigger.short_token" class="js-short-token"> <p
v-if="trigger.short_token"
class="js-short-token"
:class="{ 'append-bottom-0': !hasVariables }"
>
<span class="build-light-text"> {{ __('Token') }} </span> {{ trigger.short_token }} <span class="build-light-text"> {{ __('Token') }} </span> {{ trigger.short_token }}
</p> </p>
<p v-if="hasVariables"> <template v-if="hasVariables">
<gl-button <p class="trigger-variables-btn-container">
v-if="!areVariablesVisible" <span class="build-light-text"> {{ __('Variables:') }} </span>
type="button"
class="btn btn-default group js-reveal-variables" <gl-button v-if="hasValues" class="group js-reveal-variables" @click="toggleValues">
@click="revealVariables" {{ getToggleButtonText }}
>
{{ __('Reveal Variables') }}
</gl-button> </gl-button>
</p> </p>
<dl v-if="areVariablesVisible" class="js-build-variables trigger-build-variables"> <table class="js-build-variables trigger-build-variables">
<template v-for="variable in trigger.variables"> <tr v-for="(variable, index) in trigger.variables" :key="`${variable.key}-${index}`">
<dt :key="`${variable.key}-variable`" class="js-build-variable trigger-build-variable"> <td class="js-build-variable trigger-build-variable trigger-variables-table-cell">
{{ variable.key }} {{ variable.key }}
</dt> </td>
<td class="js-build-value trigger-build-value trigger-variables-table-cell">
<dd :key="`${variable.key}-value`" class="js-build-value trigger-build-value"> {{ getDisplayValue(variable.value) }}
{{ variable.value }} </td>
</dd> </tr>
</table>
</template> </template>
</dl>
</div> </div>
</template> </template>
...@@ -228,9 +228,16 @@ ...@@ -228,9 +228,16 @@
padding: 16px 0; padding: 16px 0;
} }
.trigger-variables-btn-container {
@extend .d-flex;
justify-content: space-between;
align-items: center;
}
.trigger-build-variables { .trigger-build-variables {
margin: 0; margin: 0;
overflow-x: auto; overflow-x: auto;
width: 100%;
-ms-overflow-style: scrollbar; -ms-overflow-style: scrollbar;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
...@@ -243,7 +250,15 @@ ...@@ -243,7 +250,15 @@
.trigger-build-value { .trigger-build-value {
padding: 2px 4px; padding: 2px 4px;
color: $black; color: $black;
background-color: $white-light; }
.trigger-variables-table-cell {
font-size: $gl-font-size-small;
line-height: $gl-line-height;
border: 1px solid $theme-gray-200;
padding: $gl-padding-4 6px;
width: 50%;
vertical-align: top;
} }
.badge.badge-pill { .badge.badge-pill {
......
...@@ -3,5 +3,6 @@ ...@@ -3,5 +3,6 @@
class TriggerVariableEntity < Grape::Entity class TriggerVariableEntity < Grape::Entity
include RequestAwareEntity include RequestAwareEntity
expose :key, :value, :public expose :key, :public
expose :value, if: ->(_, _) { can?(request.current_user, :admin_build, request.project) }
end end
---
title: Pipeline trigger variable values are hidden in the UI by default. Maintainers
have the option to reveal them.
merge_request: 23518
author: jhampton
type: added
...@@ -148,7 +148,7 @@ file. The parameter is of the form: ...@@ -148,7 +148,7 @@ file. The parameter is of the form:
variables[key]=value variables[key]=value
``` ```
This information is also exposed in the UI. This information is also exposed in the UI. Please note that _values_ are only viewable by Owners and Maintainers.
![Job variables in UI](img/trigger_variables.png) ![Job variables in UI](img/trigger_variables.png)
......
...@@ -3411,6 +3411,9 @@ msgid_plural "Hide values" ...@@ -3411,6 +3411,9 @@ msgid_plural "Hide values"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "Hide values"
msgstr ""
msgid "Hide whitespace changes" msgid "Hide whitespace changes"
msgstr "" msgstr ""
...@@ -5617,14 +5620,14 @@ msgstr "" ...@@ -5617,14 +5620,14 @@ msgstr ""
msgid "Retry verification" msgid "Retry verification"
msgstr "" msgstr ""
msgid "Reveal Variables"
msgstr ""
msgid "Reveal value" msgid "Reveal value"
msgid_plural "Reveal values" msgid_plural "Reveal values"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "Reveal values"
msgstr ""
msgid "Revert this commit" msgid "Revert this commit"
msgstr "" msgstr ""
...@@ -7254,6 +7257,9 @@ msgstr "" ...@@ -7254,6 +7257,9 @@ msgstr ""
msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want." msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
msgstr "" msgstr ""
msgid "Variables:"
msgstr ""
msgid "Various container registry settings." msgid "Various container registry settings."
msgstr "" msgstr ""
......
...@@ -401,18 +401,56 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do ...@@ -401,18 +401,56 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
context 'with variables' do context 'with variables' do
before do before do
create(:ci_pipeline_variable, pipeline: pipeline, key: :TRIGGER_KEY_1, value: 'TRIGGER_VALUE_1') create(:ci_pipeline_variable, pipeline: pipeline, key: :TRIGGER_KEY_1, value: 'TRIGGER_VALUE_1')
end
context 'user is a maintainer' do
before do
project.add_maintainer(user)
get_show(id: job.id, format: :json) get_show(id: job.id, format: :json)
end end
it 'returns a job_detail' do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('job/job_details')
end
it 'exposes trigger information and variables' do it 'exposes trigger information and variables' do
expect(json_response['trigger']['short_token']).to eq 'toke'
expect(json_response['trigger']['variables'].length).to eq 1
end
it 'exposes correct variable properties' do
first_variable = json_response['trigger']['variables'].first
expect(first_variable['key']).to eq "TRIGGER_KEY_1"
expect(first_variable['value']).to eq "TRIGGER_VALUE_1"
expect(first_variable['public']).to eq false
end
end
context 'user is not a mantainer' do
before do
get_show(id: job.id, format: :json)
end
it 'returns a job_detail' do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('job/job_details') expect(response).to match_response_schema('job/job_details')
end
it 'exposes trigger information and variables' do
expect(json_response['trigger']['short_token']).to eq 'toke' expect(json_response['trigger']['short_token']).to eq 'toke'
expect(json_response['trigger']['variables'].length).to eq 1 expect(json_response['trigger']['variables'].length).to eq 1
expect(json_response['trigger']['variables'].first['key']).to eq "TRIGGER_KEY_1" end
expect(json_response['trigger']['variables'].first['value']).to eq "TRIGGER_VALUE_1"
expect(json_response['trigger']['variables'].first['public']).to eq false it 'exposes correct variable properties' do
first_variable = json_response['trigger']['variables'].first
expect(first_variable['key']).to eq "TRIGGER_KEY_1"
expect(first_variable['value']).to be_nil
expect(first_variable['public']).to eq false
end
end end
end end
end end
......
...@@ -346,21 +346,61 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do ...@@ -346,21 +346,61 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
describe 'Variables' do describe 'Variables' do
let(:trigger_request) { create(:ci_trigger_request) } let(:trigger_request) { create(:ci_trigger_request) }
let(:job) { create(:ci_build, pipeline: pipeline, trigger_request: trigger_request) }
let(:job) do context 'when user is a maintainer' do
create :ci_build, pipeline: pipeline, trigger_request: trigger_request shared_examples 'no reveal button variables behavior' do
it 'renders a hidden value with no reveal values button', :js do
expect(page).to have_content('Token')
expect(page).to have_content('Variables')
expect(page).not_to have_css('.js-reveal-variables')
expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
expect(page).to have_selector('.js-build-value', text: '••••••')
end
end
context 'when variables are stored in trigger_request' do
before do
trigger_request.update_attribute(:variables, { 'TRIGGER_KEY_1' => 'TRIGGER_VALUE_1' } )
visit project_job_path(project, job)
end
it_behaves_like 'no reveal button variables behavior'
end
context 'when variables are stored in pipeline_variables' do
before do
create(:ci_pipeline_variable, pipeline: pipeline, key: 'TRIGGER_KEY_1', value: 'TRIGGER_VALUE_1')
visit project_job_path(project, job)
end end
shared_examples 'expected variables behavior' do it_behaves_like 'no reveal button variables behavior'
it 'shows variable key and value after click', :js do end
end
context 'when user is a maintainer' do
before do
project.add_maintainer(user)
end
shared_examples 'reveal button variables behavior' do
it 'renders a hidden value with a reveal values button', :js do
expect(page).to have_content('Token') expect(page).to have_content('Token')
expect(page).to have_content('Variables')
expect(page).to have_css('.js-reveal-variables') expect(page).to have_css('.js-reveal-variables')
expect(page).not_to have_css('.js-build-variable')
expect(page).not_to have_css('.js-build-value')
click_button 'Reveal Variables' expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
expect(page).to have_selector('.js-build-value', text: '••••••')
end
it 'reveals values on button click', :js do
click_button 'Reveal values'
expect(page).not_to have_css('.js-reveal-variables')
expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1') expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1') expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1')
end end
...@@ -373,7 +413,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do ...@@ -373,7 +413,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
visit project_job_path(project, job) visit project_job_path(project, job)
end end
it_behaves_like 'expected variables behavior' it_behaves_like 'reveal button variables behavior'
end end
context 'when variables are stored in pipeline_variables' do context 'when variables are stored in pipeline_variables' do
...@@ -383,7 +423,8 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do ...@@ -383,7 +423,8 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
visit project_job_path(project, job) visit project_job_path(project, job)
end end
it_behaves_like 'expected variables behavior' it_behaves_like 'reveal button variables behavior'
end
end end
end end
......
...@@ -12,12 +12,11 @@ ...@@ -12,12 +12,11 @@
"type": "object", "type": "object",
"required": [ "required": [
"key", "key",
"value",
"public" "public"
], ],
"properties": { "properties": {
"key": { "type": "string" }, "key": { "type": "string" },
"value": { "type": "string" }, "value": { "type": "string", "optional": true },
"public": { "type": "boolean" } "public": { "type": "boolean" }
}, },
"additionalProperties": false "additionalProperties": false
......
...@@ -31,8 +31,8 @@ describe('Trigger block', () => { ...@@ -31,8 +31,8 @@ describe('Trigger block', () => {
}); });
describe('with variables', () => { describe('with variables', () => {
describe('reveal variables', () => { describe('hide/reveal variables', () => {
it('reveals variables on click', done => { it('should toggle variables on click', done => {
vm = mountComponent(Component, { vm = mountComponent(Component, {
trigger: { trigger: {
short_token: 'bd7e', short_token: 'bd7e',
...@@ -48,6 +48,10 @@ describe('Trigger block', () => { ...@@ -48,6 +48,10 @@ describe('Trigger block', () => {
vm.$nextTick() vm.$nextTick()
.then(() => { .then(() => {
expect(vm.$el.querySelector('.js-build-variables')).not.toBeNull(); expect(vm.$el.querySelector('.js-build-variables')).not.toBeNull();
expect(vm.$el.querySelector('.js-reveal-variables').textContent.trim()).toEqual(
'Hide values',
);
expect(vm.$el.querySelector('.js-build-variables').textContent).toContain( expect(vm.$el.querySelector('.js-build-variables').textContent).toContain(
'UPLOAD_TO_GCS', 'UPLOAD_TO_GCS',
); );
...@@ -58,6 +62,26 @@ describe('Trigger block', () => { ...@@ -58,6 +62,26 @@ describe('Trigger block', () => {
); );
expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('true'); expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('true');
vm.$el.querySelector('.js-reveal-variables').click();
})
.then(vm.$nextTick)
.then(() => {
expect(vm.$el.querySelector('.js-reveal-variables').textContent.trim()).toEqual(
'Reveal values',
);
expect(vm.$el.querySelector('.js-build-variables').textContent).toContain(
'UPLOAD_TO_GCS',
);
expect(vm.$el.querySelector('.js-build-value').textContent).toContain('••••••');
expect(vm.$el.querySelector('.js-build-variables').textContent).toContain(
'UPLOAD_TO_S3',
);
expect(vm.$el.querySelector('.js-build-value').textContent).toContain('••••••');
}) })
.then(done) .then(done)
.catch(done.fail); .catch(done.fail);
......
require 'spec_helper'
describe TriggerVariableEntity do
let(:project) { create(:project) }
let(:request) { double('request') }
let(:user) { create(:user) }
let(:variable) { { key: 'TEST_KEY', value: 'TEST_VALUE' } }
subject { described_class.new(variable, request: request).as_json }
before do
allow(request).to receive(:current_user).and_return(user)
allow(request).to receive(:project).and_return(project)
end
it 'exposes the variable key' do
expect(subject).to include(:key)
end
context 'when user has access to the value' do
context 'when user is maintainer' do
before do
project.team.add_maintainer(user)
end
it 'exposes the variable value' do
expect(subject).to include(:value)
end
end
context 'when user is owner' do
let(:user) { project.owner }
it 'exposes the variable value' do
expect(subject).to include(:value)
end
end
end
context 'when user does not have access to the value' do
before do
project.team.add_developer(user)
end
it 'does not expose the variable value' do
expect(subject).not_to include(:value)
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