diff --git a/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/SubscribeToApi.jsx b/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/SubscribeToApi.jsx
index e5752854a63..0c8221393b5 100755
--- a/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/SubscribeToApi.jsx
+++ b/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/SubscribeToApi.jsx
@@ -112,12 +112,15 @@ const styles = theme => ({
},
appDropDown: {
color: theme.palette.getContrastText(theme.palette.background.paper),
+ '&:hover': {
+ backgroundColor: 'unset',
+ },
},
});
const subscribeToApi = (props) => {
const [appSelected, setAppSelected] = useState('');
- const [policySelected, setPolicySelected] = useState('');
+ const [policySelected, setPolicySelected] = useState({tierName:''});
const [applicationsList, setApplicationsList] = useState([]);
const {
classes,
@@ -128,9 +131,22 @@ const subscribeToApi = (props) => {
renderSmall,
} = props;
+ let sortedThrottlingPolicyList = throttlingPolicyList;
+
useEffect(() => {
+ sortedThrottlingPolicyList = throttlingPolicyList.sort((a, b) => {
+ // Sort by 'COMMERCIAL' tier plan first
+ if (a.tierPlan === 'COMMERCIAL' && b.tierPlan !== 'COMMERCIAL') {
+ return -1;
+ } else if (a.tierPlan !== 'COMMERCIAL' && b.tierPlan === 'COMMERCIAL') {
+ return 1;
+ }
+
+ // For options within the same tier plan, sort alphabetically
+ return a.tierName.localeCompare(b.tierName);
+ });
if (throttlingPolicyList && throttlingPolicyList[0]) {
- setPolicySelected(throttlingPolicyList[0].tierName);
+ setPolicySelected(sortedThrottlingPolicyList[0]);
}
}, [throttlingPolicyList]);
@@ -158,8 +174,8 @@ const subscribeToApi = (props) => {
setAppSelected(value);
break;
case 'throttlingPolicy':
- newRequest.throttlingPolicy = target.value;
- setPolicySelected(target.value);
+ newRequest.throttlingPolicy = value.tierName;
+ setPolicySelected(value);
break;
default:
break;
@@ -182,6 +198,7 @@ const subscribeToApi = (props) => {
id="application-subscribe"
aria-describedby='application-helper-text'
options={applicationsList}
+ disableClearable
value={(applicationsList.length !== 0 && appSelected === '') ?
applicationsList[0] : appSelected}
onChange={(e, value) => handleChange('application', e, value)}
@@ -197,7 +214,7 @@ const subscribeToApi = (props) => {
)}
- {throttlingPolicyList && (
+ {sortedThrottlingPolicyList && (
{
defaultMessage='Business Plan'
/>
-
+ )}
+ />
{' '}
- {throttlingPolicyList.map((policy, index) => (
+ {sortedThrottlingPolicyList.map((policy, index) => (
{policy.tierName}
- {index !== throttlingPolicyList.length - 1 && ,}
+ {index !== sortedThrottlingPolicyList.length - 1 && ,}
))}
diff --git a/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/Tokens.jsx b/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/Tokens.jsx
index 7ff8363a566..347afe84aa4 100755
--- a/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/Tokens.jsx
+++ b/portals/devportal/src/main/webapp/source/src/app/components/Shared/AppsAndKeys/Tokens.jsx
@@ -19,13 +19,14 @@ import React from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
-import Input from '@material-ui/core/Input';
-import InputLabel from '@material-ui/core/InputLabel';
+import Autocomplete from '@material-ui/lab/Autocomplete';
+import Checkbox from '@material-ui/core/Checkbox';
+import CheckBoxIcon from '@material-ui/icons/CheckBox';
+import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
import FormControl from '@material-ui/core/FormControl';
+import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
-import Chip from '@material-ui/core/Chip';
-import MenuItem from '@material-ui/core/MenuItem';
-import Select from '@material-ui/core/Select';
+
// Styles for Grid and Paper elements
const styles = theme => ({
@@ -68,8 +69,11 @@ const MenuProps = {
getContentAnchorEl: null,
};
+const icon = ;
+const checkedIcon = ;
+
/**
- * Used to display generate acctoken UI
+ * Used to display generate access token UI
*/
const tokens = (props) => {
/**
@@ -107,37 +111,41 @@ const tokens = (props) => {
className={classes.FormControlOdd}
disabled={subscriptionScopes.length === 0}
>
-
-
-
-
-
+ />
({
textOverflow: 'ellipsis',
},
}));
+
+const icon = ;
+const checkedIcon = ;
+
/**
* Render the categories drop down.
* @param {JSON} props props passed from it's parents.
@@ -60,104 +65,61 @@ function APICategories(props) {
if (!categories.list) {
return null;
- } else if (categories.list.length === 0) {
+ } else {
return (
- category.name)}
+ noOptionsText='No API categories defined'
+ disableCloseOnSelect
+ value={api.categories}
+ onChange={(e, newValue) => configDispatcher({ action: 'categories', value: newValue })}
+ renderOption={(category, { selected }) => (
<>
-
+ {category}
>
)}
- margin='normal'
- variant='outlined'
- disabled
- value='emptyMessage'
- >
-
-
- )}
+ renderInput={(params) => (
+
+ ) : (
+
+ )
+ }
+ placeholder='Search API categories'
+ helperText='Select API Categories for the API'
+ margin='normal'
+ variant='outlined'
+ id='APICategories'
/>
-
-
-
- );
- } else {
- return (
-
-
-
- >
)}
- name='categories'
- margin='normal'
- variant='outlined'
- disabled={isRestricted(['apim:api_create', 'apim:api_publish'], apiFromContext)}
- value={api.categories}
- SelectProps={{
- multiple: true,
- renderValue: (selected) => (Array.isArray(selected) ? selected.join(', ') : selected),
- MenuProps: {
- anchorOrigin: {
- vertical: 'bottom',
- horizontal: 'left',
- },
- getContentAnchorEl: null,
- keepMounted: true,
- PaperProps: {
- style: {
- maxHeight: 300,
- maxWidth: 300,
- },
- },
- },
- }}
- onChange={(e) => configDispatcher({ action: 'categories', value: e.target.value })}
- InputProps={{
- id: 'itest-id-categories-input',
- }}
- helperText='Select API Categories for the API'
- >
- { categories.list.map((category) => (
-
-
-
-
- ))}
-
+ />
diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/OperationGovernance.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/OperationGovernance.jsx
index 828e047f3f5..94a544eaa7e 100644
--- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/OperationGovernance.jsx
+++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/OperationGovernance.jsx
@@ -19,9 +19,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import Grid from '@material-ui/core/Grid';
+import Autocomplete from '@material-ui/lab/Autocomplete';
import Button from '@material-ui/core/Button';
import Box from '@material-ui/core/Box';
import Checkbox from '@material-ui/core/Checkbox';
+import CheckBoxIcon from '@material-ui/icons/CheckBox';
+import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
import Divider from '@material-ui/core/Divider';
import Typography from '@material-ui/core/Typography';
import Switch from '@material-ui/core/Switch';
@@ -32,13 +35,15 @@ import MenuItem from '@material-ui/core/MenuItem';
import Tooltip from '@material-ui/core/Tooltip';
import HelpOutline from '@material-ui/icons/HelpOutline';
import LaunchIcon from '@material-ui/icons/Launch';
-import ListSubheader from '@material-ui/core/ListSubheader';
import { Link } from 'react-router-dom';
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
import ExpandLessIcon from '@material-ui/icons/ExpandLess';
import { useIntl, FormattedMessage } from 'react-intl';
import { getOperationScopes } from '../../operationUtils';
+const icon = ;
+const checkedIcon = ;
+
/**
*
* Renders the security , throttling policies and scopes selection section in the operation collapsed page
@@ -235,106 +240,68 @@ export default function OperationGovernance(props) {
-
- { operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? (
- (Array.isArray(selected) ? selected.join(', ') : selected),
+
+ {operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? (
+ option.shared ? 'Shared Scopes' : 'API Scopes'}
+ noOptionsText='No scopes available'
+ disableCloseOnSelect
+ value={operationScopes.map((scope) => ({ scope: { name: scope } }))}
+ getOptionLabel={(option) => option.scope.name}
+ getOptionSelected={(option, value) => option.scope.name === value.scope.name}
+ onChange={(event, newValue) => {
+ const selectedScopes = newValue.map((val) => val.scope.name);
+ operationsDispatcher({
+ action: 'scopes',
+ data: { target, verb, value: selectedScopes ? [selectedScopes] : [] },
+ });
}}
- disabled={disableUpdate}
- fullWidth
- label={api.scopes.length !== 0 || sharedScopes ? intl.formatMessage({
- id: 'Apis.Details.Resources.components.operationComponents.'
- + 'OperationGovernance.operation.scope.label.default',
- defaultMessage: 'Operation scope',
- }) : intl.formatMessage({
- id: 'Apis.Details.Resources.components.operationComponents.'
- + 'OperationGovernance.operation.scope.label.notAvailable',
- defaultMessage: 'No scope available',
- })}
- value={operationScopes}
- onChange={({ target: { value } }) => operationsDispatcher({
- action: 'scopes',
- data: { target, verb, value: value ? [value] : [] },
- })}
- helperText={(
-
- )}
- margin='dense'
- variant='outlined'
- id={verb + target + '-operation-scope-select'}
- >
-
-
-
- {filteredApiScopes.length !== 0 ? filteredApiScopes.map((apiScope) => (
-
-
- {apiScope.scope.name}
-
- )) : (
-
-
-
-
-
+ renderOption={(option, { selected }) => (
+ <>
+
+ {option.scope.name}
+ >
)}
-
-
-
- {sharedScopes && sharedScopes.length !== 0 ? sharedScopes.map((sharedScope) => (
-
-
- {sharedScope.scope.name}
-
- )) : (
-
-
+ style={{ width: 500 }}
+ renderInput={(params) => (
+
-
-
+ )}
+ margin='dense'
+ variant='outlined'
+ id={verb + target + '-operation-scope-select'} />
)}
-
- ) : null }
+ />
+ ) : null}
-
+
{ operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? !disableUpdate && (
diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/asyncapi/OperationGovernance.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/asyncapi/OperationGovernance.jsx
index eba44dd017a..49fbdd0432d 100644
--- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/asyncapi/OperationGovernance.jsx
+++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/operationComponents/asyncapi/OperationGovernance.jsx
@@ -19,18 +19,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import Grid from '@material-ui/core/Grid';
+import Autocomplete from '@material-ui/lab/Autocomplete';
import Checkbox from '@material-ui/core/Checkbox';
+import CheckBoxIcon from '@material-ui/icons/CheckBox';
+import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
import Divider from '@material-ui/core/Divider';
import Typography from '@material-ui/core/Typography';
import Switch from '@material-ui/core/Switch';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormControl from '@material-ui/core/FormControl';
import TextField from '@material-ui/core/TextField';
-import MenuItem from '@material-ui/core/MenuItem';
import Tooltip from '@material-ui/core/Tooltip';
import HelpOutline from '@material-ui/icons/HelpOutline';
import LaunchIcon from '@material-ui/icons/Launch';
-import ListSubheader from '@material-ui/core/ListSubheader';
import { Link } from 'react-router-dom';
import { useIntl, FormattedMessage } from 'react-intl';
import { isRestricted } from 'AppData/AuthManager';
@@ -50,6 +51,8 @@ export default function OperationGovernance(props) {
const operationScopes = getAsyncAPIOperationScopes(operation[verb]);
const filteredApiScopes = api.scopes.filter((sharedScope) => !sharedScope.shared);
const intl = useIntl();
+ const icon = ;
+ const checkedIcon = ;
return (
<>
@@ -108,110 +111,68 @@ export default function OperationGovernance(props) {
-
- {
- operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? (
- (Array.isArray(selected) ? selected.join(', ') : selected),
- }}
- disabled={disableUpdate}
- fullWidth
- label={api.scopes.length !== 0 || sharedScopes ? intl.formatMessage({
- id: 'Apis.Details.Topics.components.operationComponents.'
- + 'OperationGovernance.operation.scope.label.default',
- defaultMessage: 'Operation scope',
- }) : intl.formatMessage({
- id: 'Apis.Details.Topics.components.operationComponents.'
- + 'OperationGovernance.operation.scope.label.notAvailable',
- defaultMessage: 'No scope available',
- })}
- value={operationScopes}
- onChange={({ target: { value } }) => operationsDispatcher({
+
+ {operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? (
+ option.shared ? 'Shared Scopes' : 'API Scopes'}
+ noOptionsText='No scopes available'
+ disableCloseOnSelect
+ value={operationScopes.map((scope) => ({ scope: { name: scope } }))}
+ getOptionLabel={(option) => option.scope.name}
+ getOptionSelected={(option, value) => option.scope.name === value.scope.name}
+ onChange={(event, newValue) => {
+ const selectedScopes = newValue.map((val) => val.scope.name);
+ operationsDispatcher({
action: 'scopes',
- data: { target, verb, value: value ? [value] : [] },
- })}
- helperText={(
-
- )}
- margin='dense'
- variant='outlined'
- >
-
-
-
- {filteredApiScopes.length !== 0 ? filteredApiScopes.map((apiScope) => (
-
-
- {apiScope.scope.name}
-
- )) : (
-
-
-
-
-
- )}
-
- (
+ <>
+
-
- {sharedScopes && sharedScopes.length !== 0 ? sharedScopes.map((sharedScope) => (
-
-
+ )}
+ style={{ width: 500 }}
+ renderInput={(params) => (
+
- {sharedScope.scope.name}
-
- )) : (
-
-
-
-
-
- )}
-
- ) : null
- }
+ )}
+ margin='dense'
+ variant='outlined'
+ id={verb + target + '-operation-scope-select'} />
+ )}
+ />
+ ) : null}
-
+
{
operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? !disableUpdate && (
({
@@ -653,7 +655,6 @@ class SubscriptionsTable extends Component {
),
options: {
sort: false,
- filterType: 'textField',
customBodyRender: (value, tableMeta) => {
if (tableMeta.rowData) {
let claimsObject;
@@ -695,6 +696,33 @@ class SubscriptionsTable extends Component {
return null;
},
filter: true,
+ display: true,
+ filterType: 'custom',
+ filterOptions: {
+ logic: (sub, filters) => {
+ if (filters.length) return !filters.includes(sub);
+ return false;
+ },
+ display: (filterList, onChange, index, column) => {
+ return ( sub.subscriber)))}
+ value={filterList[index][0] ? filterList[index][0] : null}
+ onChange={(event, newValue) => {
+ const updatedFilterList = [...filterList];
+ updatedFilterList[index] = newValue ? [newValue] : [];
+ onChange(updatedFilterList[index], index, column);
+ }}
+ renderInput={(params) => (
+
+ )}
+ />);
+ },
+ },
},
},
{
@@ -707,6 +735,34 @@ class SubscriptionsTable extends Component {
),
options: {
sort: false,
+ filter: true,
+ display: true,
+ filterType: 'custom',
+ filterOptions: {
+ logic: (app, filters) => {
+ if (filters.length) return !filters.includes(app);
+ return false;
+ },
+ display: (filterList, onChange, index, column) => {
+ return ( sub.name)))}
+ value={filterList[index][0] ? filterList[index][0] : null}
+ onChange={(event, newValue) => {
+ const updatedFilterList = [...filterList];
+ updatedFilterList[index] = newValue ? [newValue] : [];
+ onChange(updatedFilterList[index], index, column);
+ }}
+ renderInput={(params) => (
+
+ )}
+ />);
+ },
+ },
},
},
{
@@ -733,6 +789,34 @@ class SubscriptionsTable extends Component {
),
options: {
sort: false,
+ filter: true,
+ display: true,
+ filterType: 'custom',
+ filterOptions: {
+ logic: (tier, filters) => {
+ if (filters.length) return !filters.includes(tier);
+ return false;
+ },
+ display: (filterList, onChange, index, column) => {
+ return ( sub.throttlingPolicy)))}
+ value={filterList[index][0] ? filterList[index][0] : null}
+ onChange={(event, newValue) => {
+ const updatedFilterList = [...filterList];
+ updatedFilterList[index] = newValue ? [newValue] : [];
+ onChange(updatedFilterList[index], index, column);
+ }}
+ renderInput={(params) => (
+
+ )}
+ />);
+ },
+ },
},
},
{