@@ -18,7 +18,10 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { Link } from 'react-router'; | |||
import { ButtonLink } from '../../../../components/controls/buttons'; | |||
import { Alert } from '../../../../components/ui/Alert'; | |||
import MandatoryFieldMarker from '../../../../components/ui/MandatoryFieldMarker'; | |||
import { translate } from '../../../../helpers/l10n'; | |||
import { AlmBindingDefinitionBase } from '../../../../types/alm-settings'; | |||
@@ -35,6 +38,7 @@ export interface AlmBindingDefinitionFormFieldProps<B extends AlmBindingDefiniti | |||
overwriteOnly?: boolean; | |||
propKey: keyof B; | |||
value: string; | |||
isSecret?: boolean; | |||
} | |||
export function AlmBindingDefinitionFormField<B extends AlmBindingDefinitionBase>( | |||
@@ -49,7 +53,8 @@ export function AlmBindingDefinitionFormField<B extends AlmBindingDefinitionBase | |||
optional, | |||
overwriteOnly = false, | |||
propKey, | |||
value | |||
value, | |||
isSecret | |||
} = props; | |||
const [showField, setShowField] = React.useState(!overwriteOnly); | |||
@@ -62,23 +67,23 @@ export function AlmBindingDefinitionFormField<B extends AlmBindingDefinitionBase | |||
{!optional && <MandatoryFieldMarker />} | |||
{help && <div className="markdown small spacer-top">{help}</div>} | |||
</div> | |||
<div className="settings-definition-right big-padded-top"> | |||
<div className="settings-definition-right big-padded-top display-flex-column"> | |||
{!showField && overwriteOnly && ( | |||
<div> | |||
<p>{translate('settings.almintegration.form.secret_field')}</p> | |||
<p>{translate('settings.almintegration.form.secret.field')}</p> | |||
<ButtonLink | |||
onClick={() => { | |||
props.onFieldChange(propKey, ''); | |||
setShowField(true); | |||
}}> | |||
{translate('settings.almintegration.form.update_secret_field')} | |||
{translate('settings.almintegration.form.secret.update_field')} | |||
</ButtonLink> | |||
</div> | |||
)} | |||
{showField && isTextArea && ( | |||
<textarea | |||
className="settings-large-input" | |||
className="width-100" | |||
id={id} | |||
maxLength={maxLength || 2000} | |||
onChange={e => props.onFieldChange(propKey, e.currentTarget.value)} | |||
@@ -90,8 +95,8 @@ export function AlmBindingDefinitionFormField<B extends AlmBindingDefinitionBase | |||
{showField && !isTextArea && ( | |||
<input | |||
className="width-100" | |||
autoFocus={autoFocus} | |||
className="settings-large-input" | |||
id={id} | |||
maxLength={maxLength || 100} | |||
name={id} | |||
@@ -101,6 +106,27 @@ export function AlmBindingDefinitionFormField<B extends AlmBindingDefinitionBase | |||
value={value} | |||
/> | |||
)} | |||
{showField && isSecret && ( | |||
<Alert variant="info" className="spacer-top"> | |||
<FormattedMessage | |||
id="settings.almintegration.form.secret.can_encrypt" | |||
defaultMessage={translate('settings.almintegration.form.secret.can_encrypt')} | |||
values={{ | |||
learn_more: ( | |||
<Link | |||
target="_blank" | |||
to={{ | |||
pathname: | |||
'/documentation/instance-administration/security/#settings-encryption' | |||
}}> | |||
{translate('learn_more')} | |||
</Link> | |||
) | |||
}} | |||
/> | |||
</Alert> | |||
)} | |||
</div> | |||
</div> | |||
); |
@@ -93,6 +93,7 @@ export default function AzureForm(props: AzureFormProps) { | |||
overwriteOnly={Boolean(formData.key)} | |||
propKey="personalAccessToken" | |||
value={formData.personalAccessToken} | |||
isSecret={true} | |||
/> | |||
</> | |||
); |
@@ -68,7 +68,7 @@ export default function BitbucketCloudForm(props: BitbucketCloudFormProps) { | |||
propKey="workspace" | |||
value={formData.workspace || ''} | |||
/> | |||
<Alert className="big-spacer-top big-spacer-right" variant="info"> | |||
<Alert className="big-spacer-top" variant="info"> | |||
<FormattedMessage | |||
defaultMessage={translate(`settings.almintegration.bitbucketcloud.info`)} | |||
id="settings.almintegration.bitbucketcloud.info" | |||
@@ -104,6 +104,7 @@ export default function BitbucketCloudForm(props: BitbucketCloudFormProps) { | |||
overwriteOnly={Boolean(formData.key)} | |||
propKey="clientSecret" | |||
value={formData.clientSecret || ''} | |||
isSecret={true} | |||
/> | |||
</> | |||
); |
@@ -92,6 +92,7 @@ export default function BitbucketServerForm(props: BitbucketServerFormProps) { | |||
overwriteOnly={Boolean(formData.key)} | |||
propKey="personalAccessToken" | |||
value={formData.personalAccessToken || ''} | |||
isSecret={true} | |||
/> | |||
</> | |||
); |
@@ -63,7 +63,7 @@ export default function GithubForm(props: GithubFormProps) { | |||
propKey="url" | |||
value={formData.url} | |||
/> | |||
<Alert className="big-spacer-top big-spacer-right" variant="info"> | |||
<Alert className="big-spacer-top" variant="info"> | |||
<FormattedMessage | |||
defaultMessage={translate(`settings.almintegration.github.info`)} | |||
id="settings.almintegration.github.info" | |||
@@ -100,6 +100,7 @@ export default function GithubForm(props: GithubFormProps) { | |||
overwriteOnly={Boolean(formData.key)} | |||
propKey="clientSecret" | |||
value={formData.clientSecret} | |||
isSecret={true} | |||
/> | |||
<AlmBindingDefinitionFormField | |||
id="private_key" | |||
@@ -109,6 +110,7 @@ export default function GithubForm(props: GithubFormProps) { | |||
overwriteOnly={Boolean(formData.key)} | |||
propKey="privateKey" | |||
value={formData.privateKey} | |||
isSecret={true} | |||
/> | |||
</> | |||
); |
@@ -89,6 +89,7 @@ export default function GitlabForm(props: GitlabFormProps) { | |||
overwriteOnly={Boolean(formData.key)} | |||
propKey="personalAccessToken" | |||
value={formData.personalAccessToken} | |||
isSecret={true} | |||
/> | |||
</> | |||
); |
@@ -33,6 +33,7 @@ it('should render correctly', () => { | |||
expect(shallowRender({ isTextArea: true })).toMatchSnapshot('textarea'); | |||
expect(shallowRender({ optional: true })).toMatchSnapshot('optional'); | |||
expect(shallowRender({ overwriteOnly: true })).toMatchSnapshot('secret'); | |||
expect(shallowRender({ isSecret: true })).toMatchSnapshot('encryptable'); | |||
}); | |||
it('should call onFieldChange', () => { |
@@ -16,10 +16,10 @@ exports[`should render correctly: default 1`] = ` | |||
<MandatoryFieldMarker /> | |||
</div> | |||
<div | |||
className="settings-definition-right big-padded-top" | |||
className="settings-definition-right big-padded-top display-flex-column" | |||
> | |||
<input | |||
className="settings-large-input" | |||
className="width-100" | |||
id="key" | |||
maxLength={40} | |||
name="key" | |||
@@ -32,6 +32,63 @@ exports[`should render correctly: default 1`] = ` | |||
</div> | |||
`; | |||
exports[`should render correctly: encryptable 1`] = ` | |||
<div | |||
className="settings-definition" | |||
> | |||
<div | |||
className="settings-definition-left" | |||
> | |||
<label | |||
className="h3" | |||
htmlFor="key" | |||
> | |||
settings.almintegration.form.key | |||
</label> | |||
<MandatoryFieldMarker /> | |||
</div> | |||
<div | |||
className="settings-definition-right big-padded-top display-flex-column" | |||
> | |||
<input | |||
className="width-100" | |||
id="key" | |||
maxLength={40} | |||
name="key" | |||
onChange={[Function]} | |||
size={50} | |||
type="text" | |||
value="key" | |||
/> | |||
<Alert | |||
className="spacer-top" | |||
variant="info" | |||
> | |||
<FormattedMessage | |||
defaultMessage="settings.almintegration.form.secret.can_encrypt" | |||
id="settings.almintegration.form.secret.can_encrypt" | |||
values={ | |||
Object { | |||
"learn_more": <Link | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
target="_blank" | |||
to={ | |||
Object { | |||
"pathname": "/documentation/instance-administration/security/#settings-encryption", | |||
} | |||
} | |||
> | |||
learn_more | |||
</Link>, | |||
} | |||
} | |||
/> | |||
</Alert> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: optional 1`] = ` | |||
<div | |||
className="settings-definition" | |||
@@ -47,10 +104,10 @@ exports[`should render correctly: optional 1`] = ` | |||
</label> | |||
</div> | |||
<div | |||
className="settings-definition-right big-padded-top" | |||
className="settings-definition-right big-padded-top display-flex-column" | |||
> | |||
<input | |||
className="settings-large-input" | |||
className="width-100" | |||
id="key" | |||
maxLength={40} | |||
name="key" | |||
@@ -79,16 +136,16 @@ exports[`should render correctly: secret 1`] = ` | |||
<MandatoryFieldMarker /> | |||
</div> | |||
<div | |||
className="settings-definition-right big-padded-top" | |||
className="settings-definition-right big-padded-top display-flex-column" | |||
> | |||
<div> | |||
<p> | |||
settings.almintegration.form.secret_field | |||
settings.almintegration.form.secret.field | |||
</p> | |||
<ButtonLink | |||
onClick={[Function]} | |||
> | |||
settings.almintegration.form.update_secret_field | |||
settings.almintegration.form.secret.update_field | |||
</ButtonLink> | |||
</div> | |||
</div> | |||
@@ -111,10 +168,10 @@ exports[`should render correctly: textarea 1`] = ` | |||
<MandatoryFieldMarker /> | |||
</div> | |||
<div | |||
className="settings-definition-right big-padded-top" | |||
className="settings-definition-right big-padded-top display-flex-column" | |||
> | |||
<textarea | |||
className="settings-large-input" | |||
className="width-100" | |||
id="key" | |||
maxLength={40} | |||
onChange={[Function]} | |||
@@ -147,10 +204,10 @@ exports[`should render correctly: with help 1`] = ` | |||
</div> | |||
</div> | |||
<div | |||
className="settings-definition-right big-padded-top" | |||
className="settings-definition-right big-padded-top display-flex-column" | |||
> | |||
<input | |||
className="settings-large-input" | |||
className="width-100" | |||
id="key" | |||
maxLength={40} | |||
name="key" |
@@ -63,6 +63,7 @@ exports[`should render correctly: create 1`] = ` | |||
/> | |||
} | |||
id="personal_access_token" | |||
isSecret={true} | |||
isTextArea={true} | |||
onFieldChange={[MockFunction]} | |||
overwriteOnly={false} | |||
@@ -135,6 +136,7 @@ exports[`should render correctly: edit 1`] = ` | |||
/> | |||
} | |||
id="personal_access_token" | |||
isSecret={true} | |||
isTextArea={true} | |||
onFieldChange={[MockFunction]} | |||
overwriteOnly={true} |
@@ -36,7 +36,7 @@ exports[`should render correctly 1`] = ` | |||
value="workspace" | |||
/> | |||
<Alert | |||
className="big-spacer-top big-spacer-right" | |||
className="big-spacer-top" | |||
variant="info" | |||
> | |||
<FormattedMessage | |||
@@ -76,6 +76,7 @@ exports[`should render correctly 1`] = ` | |||
<AlmBindingDefinitionFormField | |||
help="settings.almintegration.form.oauth_secret.bitbucketcloud.help" | |||
id="client_secret.bitbucketcloud" | |||
isSecret={true} | |||
onFieldChange={[MockFunction]} | |||
overwriteOnly={true} | |||
propKey="clientSecret" |
@@ -59,6 +59,7 @@ exports[`should render correctly 1`] = ` | |||
/> | |||
} | |||
id="personal_access_token" | |||
isSecret={true} | |||
isTextArea={true} | |||
onFieldChange={[MockFunction]} | |||
overwriteOnly={true} |
@@ -34,7 +34,7 @@ exports[`should render correctly 1`] = ` | |||
value="" | |||
/> | |||
<Alert | |||
className="big-spacer-top big-spacer-right" | |||
className="big-spacer-top" | |||
variant="info" | |||
> | |||
<FormattedMessage | |||
@@ -73,6 +73,7 @@ exports[`should render correctly 1`] = ` | |||
<AlmBindingDefinitionFormField | |||
help="settings.almintegration.form.client_secret.github.help" | |||
id="client_secret.github" | |||
isSecret={true} | |||
maxLength={80} | |||
onFieldChange={[MockFunction]} | |||
overwriteOnly={false} | |||
@@ -82,6 +83,7 @@ exports[`should render correctly 1`] = ` | |||
<AlmBindingDefinitionFormField | |||
help="settings.almintegration.form.private_key.github.help" | |||
id="private_key" | |||
isSecret={true} | |||
isTextArea={true} | |||
onFieldChange={[MockFunction]} | |||
overwriteOnly={false} | |||
@@ -125,7 +127,7 @@ exports[`should render correctly 2`] = ` | |||
value="http://github.enterprise.com" | |||
/> | |||
<Alert | |||
className="big-spacer-top big-spacer-right" | |||
className="big-spacer-top" | |||
variant="info" | |||
> | |||
<FormattedMessage | |||
@@ -164,6 +166,7 @@ exports[`should render correctly 2`] = ` | |||
<AlmBindingDefinitionFormField | |||
help="settings.almintegration.form.client_secret.github.help" | |||
id="client_secret.github" | |||
isSecret={true} | |||
maxLength={80} | |||
onFieldChange={[MockFunction]} | |||
overwriteOnly={true} | |||
@@ -173,6 +176,7 @@ exports[`should render correctly 2`] = ` | |||
<AlmBindingDefinitionFormField | |||
help="settings.almintegration.form.private_key.github.help" | |||
id="private_key" | |||
isSecret={true} | |||
isTextArea={true} | |||
onFieldChange={[MockFunction]} | |||
overwriteOnly={true} |
@@ -59,6 +59,7 @@ exports[`should render correctly 1`] = ` | |||
/> | |||
} | |||
id="personal_access_token" | |||
isSecret={true} | |||
isTextArea={true} | |||
onFieldChange={[MockFunction]} | |||
overwriteOnly={false} | |||
@@ -127,6 +128,7 @@ exports[`should render correctly 2`] = ` | |||
/> | |||
} | |||
id="personal_access_token" | |||
isSecret={true} | |||
isTextArea={true} | |||
onFieldChange={[MockFunction]} | |||
overwriteOnly={true} |
@@ -1180,7 +1180,7 @@ settings.almintegration.form.client_id.bitbucketcloud=OAuth Key | |||
settings.almintegration.form.client_secret.bitbucketcloud=OAuth Secret | |||
settings.almintegration.form.private_key=Private Key | |||
settings.almintegration.form.private_key.github.help=Your GitHub App's private key. You can generate a .pem file from your GitHub App's page under Private keys. Copy and paste the whole contents of the file here. | |||
settings.almintegration.form.personal_access_token=Personal Access token | |||
settings.almintegration.form.personal_access_token=Personal Access Token | |||
settings.almintegration.form.personal_access_token.azure.help=SonarQube needs a {pat} to report the Quality Gate status on Pull Requests in Azure DevOps. To create this token, we recommend using a dedicated Azure DevOps account with administration permissions. The token itself needs {permission} permission. {doc_link} | |||
settings.almintegration.form.personal_access_token.azure.help.url=Personal Access Token | |||
settings.almintegration.form.personal_access_token.gitlab.help=SonarQube needs a {pat} to report the Quality Gate status on Merge Requests in GitLab. To create this token, we recommend using a dedicated GitLab account with {permission} permission to all target projects. The token itself needs the {scope} scope. {doc_link} | |||
@@ -1189,8 +1189,9 @@ settings.almintegration.form.personal_access_token.bitbucket.help=SonarQube need | |||
settings.almintegration.form.personal_access_token.bitbucket.help.url=Personal Access Token | |||
settings.almintegration.form.save=Save configuration | |||
settings.almintegration.form.cancel=Cancel | |||
settings.almintegration.form.secret_field=This field is hidden for security reasons. | |||
settings.almintegration.form.update_secret_field=Update field value | |||
settings.almintegration.form.secret.field=This field is hidden for security reasons. | |||
settings.almintegration.form.secret.update_field=Update field value | |||
settings.almintegration.form.secret.can_encrypt=You can encrypt this value. {learn_more} | |||
settings.almintegration.feature.status_reporting.title=Quality Gate status reporting | |||
settings.almintegration.feature.status_reporting.description_pr=Add analysis and a Quality Gate to your Pull Requests directly in your ALM provider's interface. | |||
settings.almintegration.feature.status_reporting.description_pr_and_commits=Add the Quality Gate status to your Pull Requests and on analyzed commits directly in your ALM provider's interface. |