Skip to content

Commit

Permalink
Merge branch 'master' into add-limit-req
Browse files Browse the repository at this point in the history
  • Loading branch information
juzhiyuan authored Apr 14, 2021
2 parents d9ed9ad + b6a175f commit 69b9e7b
Show file tree
Hide file tree
Showing 6 changed files with 412 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-undef */

context('Create and delete consumer with api-breaker plugin form', () => {
beforeEach(() => {
cy.login();

cy.fixture('selector.json').as('domSelector');
cy.fixture('data.json').as('data');
});

const selector = {
break_response_code: "#break_response_code"
}

const data = {
break_response_code: 200,
}

it('creates consumer with api-breaker form', function () {
cy.visit('/');
cy.contains('Consumer').click();
cy.get(this.domSelector.empty).should('be.visible');
cy.contains('Create').click();
// basic information
cy.get(this.domSelector.username).type(this.data.consumerName);
cy.get(this.domSelector.description).type(this.data.description);
cy.contains('Next').click();

// config auth plugin
cy.contains(this.domSelector.pluginCard, 'key-auth').within(() => {
cy.contains('Enable').click({
force: true,
});
});
cy.focused(this.domSelector.drawer).should('exist');
cy.get(this.domSelector.disabledSwitcher).click();
// edit codemirror
cy.get(this.domSelector.codeMirror)
.first()
.then((editor) => {
editor[0].CodeMirror.setValue(
JSON.stringify({
key: 'test',
}),
);
cy.contains('button', 'Submit').click();
});

cy.contains(this.domSelector.pluginCard, 'api-breaker').within(() => {
cy.contains('Enable').click({
force: true,
});
});

cy.focused(this.domSelector.drawer).should('exist');

// config api-breaker form
cy.get(this.domSelector.drawer).within(() => {
cy.contains('Submit').click({
force: true,
});
});
cy.get(this.domSelector.notification).should('contain', 'Invalid plugin data');

cy.get(selector.break_response_code).type(data.break_response_code);
cy.get(this.domSelector.disabledSwitcher).click();
cy.get(this.domSelector.drawer).within(() => {
cy.contains('Submit').click({
force: true,
});
});
cy.get(this.domSelector.drawer).should('not.exist');

cy.contains('button', 'Next').click();
cy.contains('button', 'Submit').click();
cy.get(this.domSelector.notification).should('contain', this.data.createConsumerSuccess);
});

it('delete the consumer', function () {
cy.visit('/consumer/list');
cy.contains(this.data.consumerName).should('be.visible').siblings().contains('Delete').click();
cy.contains('button', 'Confirm').click();
cy.get(this.domSelector.notification).should('contain', this.data.deleteConsumerSuccess);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-undef */

context('Create and delete route with api-breaker form', () => {
const selector = {
break_response_code: '#break_response_code',
alert: '.ant-form-item-explain-error [role=alert]'
}

beforeEach(() => {
cy.login();

cy.fixture('selector.json').as('domSelector');
cy.fixture('data.json').as('data');
});

it('should create route with api-breaker form', function () {
cy.visit('/');
cy.contains('Route').click();
cy.get(this.domSelector.empty).should('be.visible');
cy.contains('Create').click();
cy.contains('Next').click().click();
cy.get(this.domSelector.name).type('routeName');
cy.get(this.domSelector.description).type('desc');
cy.contains('Next').click();

cy.get(this.domSelector.nodes_0_host).type('127.0.0.1');
cy.contains('Next').click();

// config api-breaker plugin
cy.contains('api-breaker').parents(this.domSelector.pluginCardBordered).within(() => {
cy.get('button').click({
force: true
});
});

cy.get(this.domSelector.drawer).should('be.visible').within(() => {
cy.get(this.domSelector.disabledSwitcher).click();
cy.get(this.domSelector.checkedSwitcher).should('exist');
});

// config api-breaker form without break_response_code
cy.get(selector.break_response_code).click();
cy.get(selector.alert).contains('Please Enter break_response_code');
cy.get(this.domSelector.drawer).within(() => {
cy.contains('Submit').click({
force: true,
});
});
cy.get(this.domSelector.notification).should('contain', 'Invalid plugin data');
cy.get(this.domSelector.notificationCloseIcon).click();

// config api-breaker form with break_response_code
cy.get(selector.break_response_code).type('200');
cy.get(selector.alert).should('not.exist');
cy.get(this.domSelector.disabledSwitcher).click();
cy.get(this.domSelector.drawer).within(() => {
cy.contains('Submit').click({
force: true,
});
});
cy.get(this.domSelector.drawer).should('not.exist');

cy.contains('button', 'Next').click();
cy.contains('button', 'Submit').click();
cy.contains(this.data.submitSuccess);

// back to route list page
cy.contains('Goto List').click();
cy.url().should('contains', 'routes/list');
});

it('should delete the route', function () {
cy.visit('/routes/list');
const {
domSelector,
data
} = this;

cy.get(domSelector.name).clear().type('routeName');
cy.contains('Search').click();
cy.contains('routeName').siblings().contains('More').click();
cy.contains('Delete').click();
cy.get(domSelector.deleteAlert).should('be.visible').within(() => {
cy.contains('OK').click();
});
cy.get(domSelector.notification).should('contain', data.deleteRouteSuccess);
cy.get(domSelector.notificationCloseIcon).click();
});
});
186 changes: 186 additions & 0 deletions web/src/components/Plugin/UI/api-breaker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import type { FormInstance } from 'antd/es/form';
import { Button, Form, InputNumber } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { useIntl } from 'umi';

type Props = {
form: FormInstance;
};

const FORM_ITEM_LAYOUT = {
labelCol: {
span: 7,
},
wrapperCol: {
span: 7
},
};

const FORM_ITEM_WITHOUT_LABEL = {
wrapperCol: {
sm: { span: 14, offset: 7 },
},
};

const ApiBreaker: React.FC<Props> = ({ form }) => {
const { formatMessage } = useIntl()

return (
<Form
form={form}
{...FORM_ITEM_LAYOUT}
initialValues={{ unhealthy: { http_statuses: [500] }, healthy: { http_statuses: [200] } }}
>
<Form.Item
label="break_response_code"
name="break_response_code"
rules={[{
required: true,
message: `${formatMessage({ id: 'component.global.pleaseEnter' })} break_response_code`
}]}
tooltip={formatMessage({ id: 'component.pluginForm.api-breaker.break_response_code.tooltip' })}
validateTrigger={['onChange', 'onBlur', 'onClick']}
>
<InputNumber min={200} max={599} required />
</Form.Item>

<Form.Item
label="max_breaker_sec"
name="max_breaker_sec"
initialValue={300}
tooltip={formatMessage({ id: 'component.pluginForm.api-breaker.max_breaker_sec.tooltip' })}
>
<InputNumber min={60} />
</Form.Item>

<Form.List name={['unhealthy', 'http_statuses']}>
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item
{...(index === 0 ? FORM_ITEM_LAYOUT : FORM_ITEM_WITHOUT_LABEL)}
label={index === 0 && 'unhealthy.http_statuses'}
tooltip={formatMessage({ id: 'component.pluginForm.api-breaker.unhealthy.http_statuses.tooltip' })}
key={field.key}
>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
noStyle
>
<InputNumber min={500} max={599} />
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: '0 8px' }}
onClick={() => {
remove(field.name);
}}
/>
) : null}
</Form.Item>
))}
{
<Form.Item {...FORM_ITEM_WITHOUT_LABEL}>
<Button
type="dashed"
onClick={() => {
add();
}}
>
<PlusOutlined /> {formatMessage({ id: 'component.global.create' })}
</Button>
</Form.Item>
}
</div>
);
}}
</Form.List>

<Form.Item
label="unhealthy.failures"
name={['unhealthy', 'failures']}
initialValue={3}
tooltip={formatMessage({ id: 'component.pluginForm.api-breaker.unhealthy.failures.tooltip' })}
>
<InputNumber min={1} />
</Form.Item>

<Form.List name={['healthy', 'http_statuses']}>
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item
{...(index === 0 ? FORM_ITEM_LAYOUT : FORM_ITEM_WITHOUT_LABEL)}
key={field.key}
label={index === 0 && 'healthy.http_statuses'}
tooltip={formatMessage({ id: 'component.pluginForm.api-breaker.healthy.http_statuses.tooltip' })}
>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
noStyle
>
<InputNumber min={200} max={499} />
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: '0 8px' }}
onClick={() => {
remove(field.name);
}}
/>
) : null}
</Form.Item>
))}
{
<Form.Item {...FORM_ITEM_WITHOUT_LABEL}>
<Button
type="dashed"
onClick={() => {
add();
}}
>
<PlusOutlined /> {formatMessage({ id: 'component.global.create' })}
</Button>
</Form.Item>
}
</div>
);
}}
</Form.List>

<Form.Item
label="healthy.successes"
name={['healthy', 'successes']}
initialValue={3}
tooltip={formatMessage({ id: 'component.pluginForm.api-breaker.healthy.successes.tooltip' })}
>
<InputNumber min={1} />
</Form.Item>
</Form >
);
}

export default ApiBreaker;
Loading

0 comments on commit 69b9e7b

Please sign in to comment.