Commit 3f0aff08 authored by Simon Knox's avatar Simon Knox

Merge branch '321201-api-fuzzing-form-ux-fine-tuning-hints' into 'master'

Insert tips as comments in the YAML snippet

See merge request gitlab-org/gitlab!58656
parents fe6531c9 9e3e81b1
......@@ -18,6 +18,7 @@ import DynamicFields from '../../components/dynamic_fields.vue';
import FormInput from '../../components/form_input.vue';
import { SCAN_MODES, CONFIGURATION_SNIPPET_MODAL_ID } from '../constants';
import apiFuzzingCiConfigurationCreate from '../graphql/api_fuzzing_ci_configuration_create.mutation.graphql';
import { insertTips } from '../utils';
import ConfigurationSnippetModal from './configuration_snippet_modal.vue';
export default {
......@@ -155,6 +156,30 @@ export default {
}
return fields.some(({ value }) => isEmptyValue(value));
},
configurationYamlWithTips() {
if (!this.configurationYaml) {
return '';
}
return insertTips(this.configurationYaml, [
{
tip: s__('APIFuzzing|Tip: Insert this part below all stages'),
// eslint-disable-next-line @gitlab/require-i18n-strings
token: 'stages:',
},
{
tip: s__('APIFuzzing|Tip: Insert this part below all include'),
// eslint-disable-next-line @gitlab/require-i18n-strings
token: 'include:',
},
{
tip: s__(
'APIFuzzing|Tip: Insert the following variables anywhere below stages and include',
),
// eslint-disable-next-line @gitlab/require-i18n-strings
token: 'variables:',
},
]);
},
},
methods: {
async onSubmit() {
......@@ -303,7 +328,7 @@ export default {
<configuration-snippet-modal
:ref="$options.CONFIGURATION_SNIPPET_MODAL_ID"
:ci-yaml-edit-url="ciYamlEditPath"
:yaml="configurationYaml"
:yaml="configurationYamlWithTips"
/>
</form>
</template>
/* eslint-disable @gitlab/require-i18n-strings */
import { isString } from 'lodash';
export const insertTip = ({ snippet, tip, token }) => {
if (!isString(snippet)) {
throw new Error('snippet must be a string');
}
if (!isString(tip)) {
throw new Error('tip must be a string');
}
if (!isString(token)) {
throw new Error('token must be a string');
}
const lines = snippet.split('\n');
for (let i = 0; i < lines.length; i += 1) {
if (lines[i].includes(token)) {
const indent = lines[i].match(/^[ \t]+/)?.[0] ?? '';
lines[i] = lines[i].replace(token, `# ${tip}\n${indent}${token}`);
break;
}
}
return lines.join('\n');
};
export const insertTips = (snippet, tips = []) =>
tips.reduce(
(snippetWithTips, { tip, token }) => insertTip({ snippet: snippetWithTips, tip, token }),
snippet,
);
---
title: Insert tips as comments in the YAML snippet
merge_request: 58656
author:
type: changed
......@@ -241,9 +241,16 @@ describe('EE - ApiFuzzingConfigurationForm', () => {
ciYamlEditUrl:
createApiFuzzingConfigurationMutationResponse.data.apiFuzzingCiConfigurationCreate
.gitlabCiYamlEditPath,
yaml:
createApiFuzzingConfigurationMutationResponse.data.apiFuzzingCiConfigurationCreate
.configurationYaml,
yaml: `---
# Tip: Insert this part below all stages
stages:
- fuzz
# Tip: Insert this part below all include
include:
- template: template.gitlab-ci.yml
# Tip: Insert the following variables anywhere below stages and include
variables:
- FOO: bar`,
});
});
......
......@@ -43,7 +43,13 @@ export const apiFuzzingConfigurationQueryResponse = {
export const createApiFuzzingConfigurationMutationResponse = {
data: {
apiFuzzingCiConfigurationCreate: {
configurationYaml: 'yaml snippet',
configurationYaml: `---
stages:
- fuzz
include:
- template: template.gitlab-ci.yml
variables:
- FOO: bar`,
gitlabCiYamlEditPath: '/ci/editor',
errors: [],
__typename: 'ApiFuzzingCiConfiguration',
......
import { insertTip, insertTips } from 'ee/security_configuration/api_fuzzing/utils';
const nonStringValues = [1, {}, null];
describe('insertTip', () => {
describe.each(['snippet', 'tip', 'token'])('throws when %s is', (arg) => {
const validValues = {
snippet: 'snippet',
tip: 'tip',
token: 'token',
};
it.each(nonStringValues)('%s', (value) => {
expect(() => {
insertTip({ ...validValues, [arg]: value });
}).toThrowError(`${arg} must be a string`);
});
});
it('returns snippet as is if token can not be found', () => {
const snippet = 'some code snippet';
expect(
insertTip({
snippet,
token: 'ghost',
tip: 'a very helpful tip',
}),
).toBe(snippet);
});
const tip = 'a very helpful tip';
it.each`
snippet | token | expected
${'some code snippet'} | ${'code'} | ${`some # ${tip}\ncode snippet`}
${'some code snippet'} | ${'some'} | ${`# ${tip}\nsome code snippet`}
${'some code snippet'} | ${'e'} | ${`som# ${tip}\ne code snippet`}
`('inserts the tip on the line before the first found token', ({ snippet, token, expected }) => {
expect(
insertTip({
snippet,
token,
tip,
}),
).toBe(expected);
});
it('preserves indentation', () => {
const snippet = `---
default:
artifacts:
expire_in: 30 days`;
const expected = `---
default:
artifacts:
# a very helpful tip
expire_in: 30 days`;
expect(
insertTip({
snippet,
token: 'expire_in:',
tip,
}),
).toBe(expected);
});
});
describe('insertTips', () => {
const validTips = [
{ tip: 'Tip 1', token: 'default:' },
{ tip: 'Tip 2', token: 'artifacts:' },
{ tip: 'Tip 3', token: 'expire_in:' },
{ tip: 'Tip 4', token: 'tags:' },
];
it.each(nonStringValues)('throws if snippet is not a string', (snippet) => {
expect(() => {
insertTips(snippet, validTips);
}).toThrowError('snippet must be a string');
});
describe.each(['tip', 'token'])('throws if %s', (prop) => {
it.each(nonStringValues)('is %s', (value) => {
expect(() => {
insertTips('some code snippet', [
{
...validTips[0],
[prop]: value,
},
]);
}).toThrowError(`${prop} must be a string`);
});
});
it('returns snippet as is if token can not be found', () => {
const snippet = 'some code snippet';
expect(insertTips(snippet, validTips)).toBe(snippet);
});
it('returns the snippet with properly inserted tips', () => {
const snippet = `default:
artifacts:
expire_in: 30 days
tags:
- gitlab-org`;
const expected = `# Tip 1
default:
# Tip 2
artifacts:
# Tip 3
expire_in: 30 days
# Tip 4
tags:
- gitlab-org`;
expect(insertTips(snippet, validTips)).toBe(expected);
});
});
......@@ -1563,6 +1563,15 @@ msgstr ""
msgid "APIFuzzing|There are two ways to perform scans."
msgstr ""
msgid "APIFuzzing|Tip: Insert the following variables anywhere below stages and include"
msgstr ""
msgid "APIFuzzing|Tip: Insert this part below all include"
msgstr ""
msgid "APIFuzzing|Tip: Insert this part below all stages"
msgstr ""
msgid "APIFuzzing|To prevent a security leak, authentication info must be added as a %{ciVariablesLinkStart}CI variable%{ciVariablesLinkEnd}. A user with maintainer access rights can manage CI variables in the %{ciSettingsLinkStart}Settings%{ciSettingsLinkEnd} area. We detected that you are not a maintainer. Commit your changes and assign them to a maintainer to update the credentials before merging."
msgstr ""
......
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