瀏覽代碼

SONAR-12000 Update frontend for webhooks secret

tags/7.8
Siegfried Ehret 5 年之前
父節點
當前提交
310ce579dd

+ 2
- 0
server/sonar-web/src/main/js/api/webhooks.ts 查看文件

@@ -24,6 +24,7 @@ export function createWebhook(data: {
name: string;
organization: string | undefined;
project?: string;
secret?: string;
url: string;
}): Promise<{ webhook: T.Webhook }> {
return postJSON('/api/webhooks/create', data).catch(throwGlobalError);
@@ -43,6 +44,7 @@ export function searchWebhooks(data: {
export function updateWebhook(data: {
webhook: string;
name: string;
secret?: string;
url: string;
}): Promise<void | Response> {
return post('/api/webhooks/update', data).catch(throwGlobalError);

+ 1
- 0
server/sonar-web/src/main/js/app/types.d.ts 查看文件

@@ -923,6 +923,7 @@ declare namespace T {
key: string;
latestDelivery?: WebhookDelivery;
name: string;
secret?: string;
url: string;
}


+ 20
- 7
server/sonar-web/src/main/js/apps/webhooks/components/App.tsx 查看文件

@@ -72,11 +72,15 @@ export default class App extends React.PureComponent<Props, State> {
};
};

handleCreate = (data: { name: string; url: string }) => {
return createWebhook({
...data,
handleCreate = (data: { name: string; secret?: string; url: string }) => {
const createData = {
name: data.name,
url: data.url,
...(data.secret && { secret: data.secret }),
...this.getScopeParams()
}).then(({ webhook }) => {
};

return createWebhook(createData).then(({ webhook }) => {
if (this.mounted) {
this.setState(({ webhooks }) => ({ webhooks: [...webhooks, webhook] }));
}
@@ -93,12 +97,21 @@ export default class App extends React.PureComponent<Props, State> {
});
};

handleUpdate = (data: { webhook: string; name: string; url: string }) => {
return updateWebhook(data).then(() => {
handleUpdate = (data: { webhook: string; name: string; secret?: string; url: string }) => {
const udpateData = {
webhook: data.webhook,
name: data.name,
url: data.url,
...(data.secret && { secret: data.secret })
};

return updateWebhook(udpateData).then(() => {
if (this.mounted) {
this.setState(({ webhooks }) => ({
webhooks: webhooks.map(webhook =>
webhook.key === data.webhook ? { ...webhook, name: data.name, url: data.url } : webhook
webhook.key === data.webhook
? { ...webhook, name: data.name, secret: data.secret, url: data.url }
: webhook
)
}));
}

+ 21
- 2
server/sonar-web/src/main/js/apps/webhooks/components/CreateWebhookForm.tsx 查看文件

@@ -32,6 +32,7 @@ interface Props {

interface Values {
name: string;
secret: string;
url: string;
}

@@ -43,8 +44,8 @@ export default class CreateWebhookForm extends React.PureComponent<Props> {
};

handleValidate = (data: Values) => {
const { name, url } = data;
const errors: { name?: string; url?: string } = {};
const { name, secret, url } = data;
const errors: { name?: string; secret?: string; url?: string } = {};
if (!name.trim()) {
errors.name = translate('webhooks.name.required');
}
@@ -55,6 +56,9 @@ export default class CreateWebhookForm extends React.PureComponent<Props> {
} else if (!isWebUri(url)) {
errors.url = translate('webhooks.url.bad_format');
}
if (secret && secret.length > 200) {
errors.secret = translate('webhooks.secret.bad_format');
}
return errors;
};

@@ -69,6 +73,7 @@ export default class CreateWebhookForm extends React.PureComponent<Props> {
header={modalHeader}
initialValues={{
name: webhook ? webhook.name : '',
secret: webhook ? webhook.secret : '',
url: webhook ? webhook.url : ''
}}
isInitialValid={isUpdate}
@@ -124,6 +129,20 @@ export default class CreateWebhookForm extends React.PureComponent<Props> {
type="text"
value={values.url}
/>
<InputValidationField
description={translate('webhooks.secret.description')}
dirty={dirty}
disabled={isSubmitting}
error={errors.secret}
id="webhook-secret"
label={<label htmlFor="webhook-secret">{translate('webhooks.secret')}</label>}
name="secret"
onBlur={handleBlur}
onChange={handleChange}
touched={touched.secret}
type="password"
value={values.secret}
/>
</>
)}
</ValidationModal>

+ 1
- 1
server/sonar-web/src/main/js/apps/webhooks/components/PageActions.tsx 查看文件

@@ -25,7 +25,7 @@ import { translate, translateWithParameters } from '../../../helpers/l10n';

interface Props {
loading: boolean;
onCreate: (data: { name: string; url: string }) => Promise<void>;
onCreate: (data: { name: string; secret?: string; url: string }) => Promise<void>;
webhooksCount: number;
}


+ 2
- 0
server/sonar-web/src/main/js/apps/webhooks/components/WebhookItem.tsx 查看文件

@@ -20,6 +20,7 @@
import * as React from 'react';
import WebhookItemLatestDelivery from './WebhookItemLatestDelivery';
import WebhookActions from './WebhookActions';
import { translate } from '../../../helpers/l10n';

interface Props {
onDelete: (webhook: string) => Promise<void>;
@@ -32,6 +33,7 @@ export default function WebhookItem({ onDelete, onUpdate, webhook }: Props) {
<tr>
<td>{webhook.name}</td>
<td>{webhook.url}</td>
<td>{webhook.secret ? translate('yes') : translate('no')}</td>
<td>
<WebhookItemLatestDelivery webhook={webhook} />
</td>

+ 1
- 0
server/sonar-web/src/main/js/apps/webhooks/components/WebhooksList.tsx 查看文件

@@ -34,6 +34,7 @@ export default class WebhooksList extends React.PureComponent<Props> {
<tr>
<th>{translate('name')}</th>
<th>{translate('webhooks.url')}</th>
<th>{translate('webhooks.secret_header')}</th>
<th>{translate('webhooks.last_execution')}</th>
<th />
</tr>

+ 8
- 3
server/sonar-web/src/main/js/apps/webhooks/components/__tests__/CreateWebhookForm-test.tsx 查看文件

@@ -21,14 +21,19 @@ import * as React from 'react';
import { shallow } from 'enzyme';
import CreateWebhookForm from '../CreateWebhookForm';

const webhook = { key: '1', name: 'foo', url: 'http://foo.bar' };
const webhookWithoutSecret = { key: '1', name: 'foo', url: 'http://foo.bar' };
const webhookWithSecret = { key: '2', name: 'bar', secret: 'sonar', url: 'http://foo.bar' };

it('should render correctly when creating a new webhook', () => {
expect(getWrapper()).toMatchSnapshot();
});

it('should render correctly when updating a webhook', () => {
expect(getWrapper({ webhook })).toMatchSnapshot();
it('should render correctly when updating a webhook without secret', () => {
expect(getWrapper({ webhook: webhookWithoutSecret })).toMatchSnapshot();
});

it('should render correctly when updating a webhook with a secret', () => {
expect(getWrapper({ webhook: webhookWithSecret })).toMatchSnapshot();
});

function getWrapper(props = {}) {

+ 24
- 1
server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/CreateWebhookForm-test.tsx.snap 查看文件

@@ -7,6 +7,7 @@ exports[`should render correctly when creating a new webhook 1`] = `
initialValues={
Object {
"name": "",
"secret": "",
"url": "",
}
}
@@ -20,13 +21,35 @@ exports[`should render correctly when creating a new webhook 1`] = `
</ValidationModal>
`;

exports[`should render correctly when updating a webhook 1`] = `
exports[`should render correctly when updating a webhook with a secret 1`] = `
<ValidationModal
confirmButtonText="update_verb"
header="webhooks.update"
initialValues={
Object {
"name": "bar",
"secret": "sonar",
"url": "http://foo.bar",
}
}
isInitialValid={true}
onClose={[MockFunction]}
onSubmit={[MockFunction]}
size="small"
validate={[Function]}
>
<Component />
</ValidationModal>
`;

exports[`should render correctly when updating a webhook without secret 1`] = `
<ValidationModal
confirmButtonText="update_verb"
header="webhooks.update"
initialValues={
Object {
"name": "foo",
"secret": undefined,
"url": "http://foo.bar",
}
}

+ 3
- 0
server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhookItem-test.tsx.snap 查看文件

@@ -8,6 +8,9 @@ exports[`should render correctly 1`] = `
<td>
http://webhook.target
</td>
<td>
no
</td>
<td>
<WebhookItemLatestDelivery
webhook={

+ 3
- 0
server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/WebhooksList-test.tsx.snap 查看文件

@@ -18,6 +18,9 @@ exports[`should correctly render the webhooks 1`] = `
<th>
webhooks.url
</th>
<th>
webhooks.secret_header
</th>
<th>
webhooks.last_execution
</th>

+ 4
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties 查看文件

@@ -3187,6 +3187,10 @@ webhooks.name=Name
webhooks.name.required=Name is required.
webhooks.no_result=No webhook defined.
webhooks.update=Update Webhook
webhooks.secret=Secret
webhooks.secret_header=Secret?
webhooks.secret.bad_format=Secret must have a maximum length of 200 characters
webhooks.secret.description=If provided, secret will be used as the key to generate the HMAC hex (lowercase) digest value in the 'X-Sonar-Webhook-HMAC-SHA256' header
webhooks.url=URL
webhooks.url.bad_format=Bad format of URL.
webhooks.url.bad_protocol=URL must start with "http://" or "https://".

Loading…
取消
儲存