From beaed0667cdb70e15c94c1646be3abe84db21bec Mon Sep 17 00:00:00 2001 From: dakshina Date: Thu, 30 Jan 2025 01:43:54 +0530 Subject: [PATCH] Implement federated gateway publisher UI changes and Devportal Restrictions --- .../Apis/Details/ApiConsole/ApiConsole.jsx | 5 +- .../components/Apis/Details/GoToTryOut.jsx | 1 + .../src/app/components/Apis/Details/index.jsx | 7 +- .../ApiTryOut/AdvertiseDetailsPanel.jsx | 105 +-- .../Shared/ApiTryOut/TryOutController.jsx | 14 +- .../source/src/app/components/Apis/Apis.jsx | 4 +- .../Apis/Create/AIAPI/APICreateAIAPI.jsx | 12 +- .../Apis/Create/APICreateRoutes.jsx | 91 ++- .../Create/AsyncAPI/ApiCreateAsyncAPI.jsx | 3 +- .../Apis/Create/Components/DefaultAPIForm.jsx | 90 +-- .../Apis/Create/Default/APICreateDefault.jsx | 101 ++- .../Apis/Create/GraphQL/ApiCreateGraphQL.jsx | 13 +- .../StreamingAPI/APICreateStreamingAPI.jsx | 3 +- .../Apis/Create/WSDL/ApiCreateWSDL.jsx | 3 +- .../Create/WebSocket/ApiCreateWebSocket.jsx | 6 +- .../Configuration/DesignConfigurations.jsx | 18 +- .../Configuration/RuntimeConfiguration.jsx | 38 +- .../RuntimeConfigurationWebSocket.jsx | 14 +- .../Apis/Details/Configuration/Topics.jsx | 3 + .../components/APISecurity/APISecurity.jsx | 45 +- .../components/ApplicationLevel.jsx | 109 +-- .../APISecurity/components/TransportLevel.jsx | 36 +- .../Configuration/components/Endpoints.jsx | 86 +- .../Configuration/components/Transports.jsx | 73 +- .../Details/Endpoints/EndpointListing.jsx | 2 + .../Details/Endpoints/EndpointOverview.jsx | 756 +++++++++--------- .../Apis/Details/Endpoints/Endpoints.jsx | 31 +- .../GeneralConfiguration/EndpointSecurity.jsx | 33 +- .../Details/Endpoints/GenericEndpoint.jsx | 47 +- .../Endpoints/LoadbalanceFailoverConfig.jsx | 3 + .../Details/Endpoints/NewEndpointCreate.jsx | 5 +- .../Environments/DeploymentOnbording.jsx | 229 ++++-- .../Details/Environments/Environments.jsx | 548 +++++++------ .../Apis/Details/Environments/Permission.jsx | 2 +- .../Apis/Details/NewOverview/MetaData.jsx | 5 +- .../Apis/Details/Operations/Operation.jsx | 247 +++--- .../Apis/Details/Operations/Operations.jsx | 58 +- .../Apis/Details/Policies/Policies.tsx | 27 +- .../Apis/Details/Policies/PolicyList.tsx | 9 +- .../Apis/Details/Resources/Resources.jsx | 20 +- .../Resources/components/APIRateLimiting.jsx | 310 +++---- .../Resources/components/AsyncOperation.jsx | 2 + .../Resources/components/Operation.jsx | 2 + .../components/OperationsSelector.jsx | 8 +- .../OperationGovernance.jsx | 470 +++++------ .../leftMenu/DevelopSectionMenu.jsx | 17 +- .../src/app/components/Apis/Details/index.jsx | 86 +- .../Listing/Landing/Menus/RestAPIMenu.jsx | 4 +- .../webapp/source/src/app/data/Constants.js | 1 + 49 files changed, 2073 insertions(+), 1729 deletions(-) diff --git a/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/ApiConsole/ApiConsole.jsx b/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/ApiConsole/ApiConsole.jsx index 2cb91d64ddc..596d9d8a5c6 100755 --- a/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/ApiConsole/ApiConsole.jsx +++ b/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/ApiConsole/ApiConsole.jsx @@ -446,7 +446,7 @@ class ApiConsole extends React.Component { securitySchemeType, username, password, productionAccessToken, sandboxAccessToken, selectedKeyType, productionApiKey, sandboxApiKey, api, advAuthHeaderValue, } = this.state; - if (api.advertiseInfo && api.advertiseInfo.advertised) { + if ((api.advertiseInfo && api.advertiseInfo.advertised) || api.gatewayVendor !== 'wso2') { return advAuthHeaderValue; } if (securitySchemeType === 'BASIC') { @@ -556,7 +556,8 @@ class ApiConsole extends React.Component { - {!user && (!api.advertiseInfo || !api.advertiseInfo.advertised) && ( + {!user && (!api.advertiseInfo || !api.advertiseInfo.advertised) + && api.gatewayVendor !== 'wso2' && ( diff --git a/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/GoToTryOut.jsx b/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/GoToTryOut.jsx index c8e697a8d85..b42796af4b9 100644 --- a/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/GoToTryOut.jsx +++ b/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/GoToTryOut.jsx @@ -250,6 +250,7 @@ export default function GoToTryOut() { if (!defaultApplication || subscribedApplications.length > 0 || api.advertiseInfo.advertised + || api.gatewayVendor !== 'wso2' || !user || isAsyncAPI || isPrototypedAPI) { diff --git a/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/index.jsx b/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/index.jsx index b073665e2de..650dc797d9c 100755 --- a/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/index.jsx +++ b/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/index.jsx @@ -587,7 +587,7 @@ class DetailsLegacy extends React.Component { open={open} id='left-menu-overview' /> - {user && showCredentials && !isSubValidationDisabled && ( + {user && showCredentials && !isSubValidationDisabled && api.gatewayVendor === 'wso2' && ( <> )} - {showTryout && (api.gatewayVendor === 'wso2' + {showTryout && (api.gatewayType !== 'wso2/apk' || (api.type === 'APIPRODUCT' && !api.gatewayVendor)) && ( <> - {api.type !== CONSTANTS.API_TYPES.GRAPHQL && !isAsyncApi && apiChatEnabled && ( + {api.type !== CONSTANTS.API_TYPES.GRAPHQL && !isAsyncApi + && apiChatEnabled && api.gatewayVendor === 'wso2' && ( { transport, securityScheme, authorizationHeader, + gatewayVendor, }, } = props; @@ -112,59 +113,61 @@ const AdvertiseDetailsPanel = (props) => { /> - - - - - - )} - value={selectedEndpoint} - name='selectedEndpoint' - onChange={handleChanges} - helperText={( + {gatewayVendor === 'wso2' && + + - )} - margin='normal' - variant='outlined' - > - {advertiseInfo.apiExternalProductionEndpoint && ( - - Production - - )} - {advertiseInfo.apiExternalSandboxEndpoint && ( - - Sandbox - - )} - - + + + )} + value={selectedEndpoint} + name='selectedEndpoint' + onChange={handleChanges} + helperText={( + + )} + margin='normal' + variant='outlined' + > + {advertiseInfo.apiExternalProductionEndpoint && ( + + Production + + )} + {advertiseInfo.apiExternalSandboxEndpoint && ( + + Sandbox + + )} + + + } {(availableTransports || securitySchemes || authorizationHeader) && ( diff --git a/portals/devportal/src/main/webapp/source/src/app/components/Shared/ApiTryOut/TryOutController.jsx b/portals/devportal/src/main/webapp/source/src/app/components/Shared/ApiTryOut/TryOutController.jsx index e6b023f377a..94de7ad745f 100644 --- a/portals/devportal/src/main/webapp/source/src/app/components/Shared/ApiTryOut/TryOutController.jsx +++ b/portals/devportal/src/main/webapp/source/src/app/components/Shared/ApiTryOut/TryOutController.jsx @@ -602,7 +602,8 @@ function TryOutController(props) { - {securitySchemeType !== 'TEST' && (!api.advertiseInfo || !api.advertiseInfo.advertised) && ( + {securitySchemeType !== 'TEST' && (!api.advertiseInfo || !api.advertiseInfo.advertised) + && api.gatewayVendor === 'wso2' && ( <> @@ -638,7 +639,7 @@ function TryOutController(props) { )} {((isApiKeyEnabled || isBasicAuthEnabled || isOAuthEnabled) && showSecurityType) - && (!api.advertiseInfo || !api.advertiseInfo.advertised) && ( + && (!api.advertiseInfo || !api.advertiseInfo.advertised) && api.gatewayVendor === 'wso2' && ( <> 0 && !isSubValidationDisabled)) && securitySchemeType !== 'BASIC' && securitySchemeType !== 'TEST' && (!api.advertiseInfo || !api.advertiseInfo.advertised) + && api.gatewayVendor === 'wso2' && ( )} {subscriptions && subscriptions.length === 0 && securitySchemeType !== 'TEST' - && securitySchemeType !== 'BASIC' + && securitySchemeType !== 'BASIC' && api.gatewayVendor === 'wso2' && (!api.advertiseInfo || !api.advertiseInfo.advertised) && !isSubValidationDisabled ? ( @@ -745,7 +747,7 @@ function TryOutController(props) { ) : ( (!ksGenerated && securitySchemeType === 'OAUTH') && (!api.advertiseInfo - || !api.advertiseInfo.advertised) && ( + || !api.advertiseInfo.advertised) && api.gatewayVendor === 'wso2' && ( @@ -765,7 +767,7 @@ function TryOutController(props) { ) )} - {(!api.advertiseInfo || !api.advertiseInfo.advertised) ? ( + {((!api.advertiseInfo || !api.advertiseInfo.advertised) && api.gatewayVendor === 'wso2') ? ( {securitySchemeType === 'BASIC' && ( @@ -929,7 +931,7 @@ function TryOutController(props) { api={api} /> )} - {(!api.advertiseInfo || !api.advertiseInfo.advertised) && ( + {(!api.advertiseInfo || !api.advertiseInfo.advertised) && api.gatewayVendor === 'wso2' && ( {(environments && environments.length > 0) && ( diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Apis.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Apis.jsx index c6c58048a3c..9b0ec644cef 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Apis.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Apis.jsx @@ -16,7 +16,7 @@ * under the License. */ -import React, { Suspense, lazy } from 'react'; +import React, { Suspense, lazy} from 'react'; import { Route, Switch, Redirect } from 'react-router-dom'; import Progress from 'AppComponents/Shared/Progress'; import AuthManager from 'AppData/AuthManager'; @@ -73,7 +73,7 @@ const Apis = () => { } }} /> - } /> + } /> { diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AIAPI/APICreateAIAPI.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AIAPI/APICreateAIAPI.jsx index 146e3dcd851..1f11bb99367 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AIAPI/APICreateAIAPI.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AIAPI/APICreateAIAPI.jsx @@ -21,7 +21,6 @@ import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; import Grid from '@mui/material/Grid'; import { FormattedMessage, useIntl } from 'react-intl'; -import { usePublisherSettings } from 'AppComponents/Shared/AppContext'; import Stepper from '@mui/material/Stepper'; import Step from '@mui/material/Step'; import StepLabel from '@mui/material/StepLabel'; @@ -80,7 +79,6 @@ function apiInputsReducer(currentState, inputAction) { export default function ApiCreateAIAPI(props) { const [wizardStep, setWizardStep] = useState(0); const { history, multiGateway } = props; - const { data: settings } = usePublisherSettings(); const [apiInputs, inputsDispatcher] = useReducer(apiInputsReducer, { type: 'ApiCreateAIAPI', @@ -124,20 +122,12 @@ export default function ApiCreateAIAPI(props) { const { name, version, context, endpoint, gatewayType, policies = ["Unlimited"], inputValue, llmProviderId, } = apiInputs; - let defaultGatewayType; - if (settings && settings.gatewayTypes.length === 1 && settings.gatewayTypes.includes('Regular')) { - defaultGatewayType = 'wso2/synapse'; - } else if (settings && settings.gatewayTypes.length === 1 && settings.gatewayTypes.includes('APK')) { - defaultGatewayType = 'wso2/apk'; - } else { - defaultGatewayType = 'default'; - } const additionalProperties = { name, version, context, - gatewayType: defaultGatewayType === 'default' ? gatewayType : defaultGatewayType, + gatewayType, policies, subtypeConfiguration: { subtype: 'AIAPI', diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/APICreateRoutes.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/APICreateRoutes.jsx index fcc34b11cd0..34088136cbb 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/APICreateRoutes.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/APICreateRoutes.jsx @@ -15,11 +15,12 @@ * specific language governing permissions and limitations * under the License. */ -import React, { useState, useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { styled } from '@mui/material/styles'; import { Route, Switch } from 'react-router-dom'; import ResourceNotFound from 'AppComponents/Base/Errors/ResourceNotFound'; import { usePublisherSettings } from 'AppComponents/Shared/AppContext'; +import { Progress } from 'AppComponents/Shared'; import APICreateDefault from './Default/APICreateDefault'; import APIProductCreateWrapper from './APIProduct/APIProductCreateWrapper'; import ApiCreateSwagger from './OpenAPI/ApiCreateOpenAPI'; @@ -42,6 +43,27 @@ const Root = styled('div')({ }, }); +const gatewayDetails = { + 'wso2/synapse': { + value: 'wso2/synapse', + name: 'Regular Gateway', + description: 'API gateway embedded in APIM runtime. Connect directly to APIM.', + isNew: false + }, + 'wso2/apk': { + value: 'wso2/apk', + name: 'APK Gateway', + description: 'Fast API gateway on Kubernetes. Manages and secures APIs.', + isNew: false + }, + 'AWS': { + value: 'AWS', + name: 'AWS Gateway', + description: 'API gateway offering from AWS cloud to secures APIs efficiently.', + isNew: true + } +}; + // Wrapper component to pass additional props const WithSomeValue = (Component, additionalProps) => (routeProps) => ( @@ -54,41 +76,64 @@ const WithSomeValue = (Component, additionalProps) => (routeProps) => ( * @returns @inheritdoc */ function APICreateRoutes() { - const { data: settings } = usePublisherSettings(); - const [gateway, setGatewayType] = useState(false); - - const getGatewayType = () => { - if (settings != null) { - if (settings.gatewayTypes && settings.gatewayTypes.length === 2 ) { - setGatewayType(true); - } else { - setGatewayType(false); - } - } - }; + const { data: publisherSettings, isLoading } = usePublisherSettings(); + const [apiTypes, setApiTypes] = useState(null); + const [gatewayTypes, setGatewayTypes] = useState(null); useEffect(() => { - getGatewayType(); - }, [settings]); + if (!isLoading) { + setApiTypes(publisherSettings.gatewayFeatureCatalog.apiTypes); + const data = publisherSettings.gatewayTypes; + const updatedData = data.map(item => { + if (item === "Regular") return "wso2/synapse"; + if (item === "APK") return "wso2/apk"; + return item; + }); + setGatewayTypes(updatedData); + } + }, [isLoading]); + + if (isLoading) { + return ; + } return ( - + gatewayTypes.includes(t)).map(type => gatewayDetails[type]) })} + /> gatewayTypes.includes(t)).map(type => gatewayDetails[type]) })} /> gatewayTypes.includes(t)).map(type => gatewayDetails[type]) })} + /> + gatewayTypes.includes(t)).map(type => gatewayDetails[type]) })} /> - {/* TODO: Remove ApiCreateWebSocket components and associated routes */} - - - + gatewayTypes.includes(t)).map(type => gatewayDetails[type]) })} + /> + gatewayTypes.includes(t)).map(type => gatewayDetails[type]) })} + /> + gatewayTypes.includes(t)).map(type => gatewayDetails[type]) })} + /> + { multiGateway: apiTypes?.ai + .filter(t=>gatewayTypes.includes(t)).map(type => gatewayDetails[type]) })} + /> diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AsyncAPI/ApiCreateAsyncAPI.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AsyncAPI/ApiCreateAsyncAPI.jsx index 9df186a2b32..a6ae408140f 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AsyncAPI/ApiCreateAsyncAPI.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AsyncAPI/ApiCreateAsyncAPI.jsx @@ -81,7 +81,7 @@ const StyledAPICreateBase = styled(APICreateBase)(( */ export default function ApiCreateAsyncAPI(props) { const [wizardStep, setWizardStep] = useState(0); - const { history } = props; + const { history, multiGateway } = props; // eslint-disable-next-line no-use-before-define const [hideEndpoint, setHideEndpoint] = useState(true); @@ -355,6 +355,7 @@ export default function ApiCreateAsyncAPI(props) { hideEndpoint={hideEndpoint} endpointPlaceholderText='Streaming Provider' appendChildrenBeforeEndpoint + multiGateway={multiGateway} > {apiInputs.gatewayVendor === 'solace' diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/DefaultAPIForm.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/DefaultAPIForm.jsx index 7b790250d68..9d95b4250d5 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/DefaultAPIForm.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/DefaultAPIForm.jsx @@ -20,7 +20,7 @@ import { styled } from '@mui/material/styles'; import PropTypes from 'prop-types'; import TextField from '@mui/material/TextField'; import Grid from '@mui/material/Grid'; -import { InputAdornment, IconButton, Icon } from '@mui/material'; +import { InputAdornment, IconButton, Icon, RadioGroup, FormControlLabel, Radio } from '@mui/material'; import CircularProgress from '@mui/material/CircularProgress'; import Chip from '@mui/material/Chip'; import Typography from '@mui/material/Typography'; @@ -29,9 +29,6 @@ import APIValidation from 'AppData/APIValidation'; import FormControl from '@mui/material/FormControl'; import FormHelperText from '@mui/material/FormHelperText'; import FormLabel from '@mui/material/FormLabel'; -import Radio from '@mui/material/Radio'; -import RadioGroup from '@mui/material/RadioGroup'; -import FormControlLabel from '@mui/material/FormControlLabel'; import API from 'AppData/api'; import { green } from '@mui/material/colors'; @@ -655,7 +652,7 @@ export default function DefaultAPIForm(props) { }} /> )} - {multiGateway && + {multiGateway && multiGateway.length > 1 && @@ -671,61 +668,30 @@ export default function DefaultAPIForm(props) { value={api.gatewayType} onChange={onChange} > - - } - label={( -
- - - - - - -
- )} - sx={{ border: getBorderColor('wso2/synapse') }} - /> -
- - } - label={( -
- - - - New - - - -
- )} - sx={{ border: getBorderColor('wso2/apk') }} - /> -
+ {multiGateway.map((gateway) => + + } + label={( +
+ + {gateway.name} + + {gateway.isNew && ( + New + )} + + {gateway.description} + +
+ )} + sx={{ border: getBorderColor(gateway.value) }} + /> +
+ )}
- } + } {!appendChildrenBeforeEndpoint && !!children && children} @@ -762,7 +728,7 @@ DefaultAPIForm.defaultProps = { }; DefaultAPIForm.propTypes = { api: PropTypes.shape({}), - multiGateway: PropTypes.string.isRequired, + multiGateway: PropTypes.isRequired, isAPIProduct: PropTypes.shape({}).isRequired, isWebSocket: PropTypes.shape({}), onChange: PropTypes.func.isRequired, diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Default/APICreateDefault.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Default/APICreateDefault.jsx index d562d2d3540..b4960d74ef9 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Default/APICreateDefault.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Default/APICreateDefault.jsx @@ -37,6 +37,11 @@ import APIProduct from 'AppData/APIProduct'; import AuthManager from 'AppData/AuthManager'; import Progress from 'AppComponents/Shared/Progress'; +const gatewayTypeMap = { + 'Regular': 'wso2/synapse', + 'APK': 'wso2/apk', + 'AWS': 'AWS', +} const getPolicies = async () => { const promisedPolicies = API.policies('subscription'); @@ -62,31 +67,7 @@ function APICreateDefault(props) { const { data: settings, isLoading, error: settingsError } = usePublisherSettings(); const [isAvailbaleGateway, setIsAvailableGateway] = useState(false); const [pageError, setPageError] = useState(null); - useEffect(() => { - if (settingsError) { - setPageError(settingsError.message); - } - }, [settingsError]); - useEffect(() => { - if (settings != null) { - if (settings.gatewayTypes && settings.gatewayTypes.length === 1) { - for (const env of settings.environment) { - if (env.gatewayType === settings.gatewayTypes[0]) { - setIsAvailableGateway(true); - break; - } - } - } - } - }, [settings]); - const [isCreating, setIsCreating] = useState(); - const [isPublishing, setIsPublishing] = useState(false); - - const [isRevisioning, setIsRevisioning] = useState(false); - const [isDeploying, setIsDeploying] = useState(false); - const [isMandatoryPropsConfigured, setIsMandatoryPropsConfigured] = useState(false); - const [isPublishButtonClicked, setIsPublishButtonClicked] = useState(false); /** * * Reduce the events triggered from API input fields to current state @@ -107,7 +88,35 @@ function APICreateDefault(props) { } const [apiInputs, inputsDispatcher] = useReducer(apiInputsReducer, { formValidity: false, + gatewayType: 'wso2/synapse', }); + + useEffect(() => { + if (settingsError) { + setPageError(settingsError.message); + } + }, [settingsError]); + + useEffect(() => { + if (settings != null) { + if (settings.gatewayTypes && settings.gatewayTypes.includes('Regular')) { + for (const env of settings.environment) { + if (env.gatewayType === 'Regular') { + setIsAvailableGateway(true); + break; + } + } + } + } + }, [isLoading]); + const [isCreating, setIsCreating] = useState(); + const [isPublishing, setIsPublishing] = useState(false); + + const [isRevisioning, setIsRevisioning] = useState(false); + const [isDeploying, setIsDeploying] = useState(false); + const [isMandatoryPropsConfigured, setIsMandatoryPropsConfigured] = useState(false); + const [isPublishButtonClicked, setIsPublishButtonClicked] = useState(false); + const isPublishable = apiInputs.endpoint; const isAPICreateDisabled = !(apiInputs.name && apiInputs.version && apiInputs.context) || isCreating || isPublishing; @@ -120,19 +129,15 @@ function APICreateDefault(props) { function handleOnChange(event) { const { name: action, value } = event.target; inputsDispatcher({ action, value }); - const settingsEnvList = settings && settings.environment; - if (settings && settings.gatewayTypes.length === 2 && (value === 'wso2/synapse' || value === 'wso2/apk')) { - for (const env of settingsEnvList) { - let tmpEnv = ''; - if (env.gatewayType === 'APK') { - tmpEnv = 'wso2/apk'; - } else if (env.gatewayType === 'Regular') { - tmpEnv = 'wso2/synapse'; - } - if (tmpEnv === value) { - setIsAvailableGateway(true); - break; - } else { + if (action === 'gatewayType') { + const settingsEnvList = settings && settings.environment; + if (settings && settings.gatewayTypes.length >= 2 && Object.values(gatewayTypeMap).includes(value)) { + for (const env of settingsEnvList) { + const tmpEnv = gatewayTypeMap[env.gatewayType]; + if (tmpEnv === value) { + setIsAvailableGateway(true); + break; + } setIsAvailableGateway(false); } } @@ -176,7 +181,6 @@ function APICreateDefault(props) { } = apiInputs; let promisedCreatedAPI; let policies; - let defaultGatewayType; const allPolicies = await getPolicies(); if (allPolicies.length === 0) { Alert.info(intl.formatMessage({ @@ -188,19 +192,12 @@ function APICreateDefault(props) { } else { policies = [allPolicies[0].name]; } - if (settings && settings.gatewayTypes.length === 1 && settings.gatewayTypes.includes('Regular')) { - defaultGatewayType = 'wso2/synapse'; - } else if (settings && settings.gatewayTypes.length === 1 && settings.gatewayTypes.includes('APK')){ - defaultGatewayType = 'wso2/apk'; - } else { - defaultGatewayType = 'default'; - } const apiData = { name, version, context, - gatewayType: defaultGatewayType === 'default' ? gatewayType : defaultGatewayType, + gatewayType, policies, }; if (endpoint) { @@ -311,8 +308,7 @@ function APICreateDefault(props) { setIsRevisioning(false); const envList = settings.environment.map((env) => env.name); const body1 = []; - const internalGateways = settings.environment.filter((p) => p.provider - && p.provider.toLowerCase().includes('wso2')); + const internalGateways = settings.environment; const getFirstVhost = (envName) => { const env = internalGateways.find( (e) => e.name === envName && e.vhosts.length > 0, @@ -339,12 +335,7 @@ function APICreateDefault(props) { const envList1 = settings.environment; let foundEnv = false; envList1.forEach((env) => { - let tmpEnv = ''; - if (env.gatewayType === 'APK') { - tmpEnv = 'wso2/apk'; - } else if (env.gatewayType === 'Regular') { - tmpEnv = 'wso2/synapse'; - } + const tmpEnv = gatewayTypeMap[env.gatewayType]; if (!foundEnv && tmpEnv === apiInputs.gatewayType && getFirstVhost(env.name)) { body1.push({ name: env.name, @@ -633,7 +624,7 @@ APICreateDefault.WORKFLOW_STATUS = { }; APICreateDefault.propTypes = { history: PropTypes.shape({ push: PropTypes.func }).isRequired, - multiGateway: PropTypes.string.isRequired, + multiGateway: PropTypes.isRequired, isAPIProduct: PropTypes.shape({}), isWebSocket: PropTypes.shape({}), intl: PropTypes.shape({ diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/GraphQL/ApiCreateGraphQL.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/GraphQL/ApiCreateGraphQL.jsx index 8c27ac98215..4210c241db6 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/GraphQL/ApiCreateGraphQL.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/GraphQL/ApiCreateGraphQL.jsx @@ -31,7 +31,6 @@ import Alert from 'AppComponents/Shared/Alert'; import CircularProgress from '@mui/material/CircularProgress'; import DefaultAPIForm from 'AppComponents/Apis/Create/Components/DefaultAPIForm'; import APICreateBase from 'AppComponents/Apis/Create/Components/APICreateBase'; -import { usePublisherSettings } from 'AppComponents/Shared/AppContext'; import ProvideGraphQL from './Steps/ProvideGraphQL'; @@ -47,7 +46,6 @@ export default function ApiCreateGraphQL(props) { const { multiGateway } = props; const [wizardStep, setWizardStep] = useState(0); const history = useHistory(); - const { data: settings } = usePublisherSettings(); const [policies, setPolicies] = useState([]); useEffect(() => { @@ -152,20 +150,11 @@ export default function ApiCreateGraphQL(props) { graphQLInfo: { operations }, } = apiInputs; - let defaultGatewayType; - if (settings && settings.gatewayTypes.length === 1 && settings.gatewayTypes.includes('Regular')) { - defaultGatewayType = 'wso2/synapse'; - } else if (settings && settings.gatewayTypes.length === 1 && settings.gatewayTypes.includes('APK')){ - defaultGatewayType = 'wso2/apk'; - } else { - defaultGatewayType = 'default'; - } - const additionalProperties = { name, version, context, - gatewayType: defaultGatewayType === 'default' ? gatewayType : defaultGatewayType, + gatewayType, policies, operations, }; diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/StreamingAPI/APICreateStreamingAPI.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/StreamingAPI/APICreateStreamingAPI.jsx index 748c4a5b014..07c30ef330a 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/StreamingAPI/APICreateStreamingAPI.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/StreamingAPI/APICreateStreamingAPI.jsx @@ -54,7 +54,7 @@ const StyledAPICreateBase = styled(APICreateBase)(( const APICreateStreamingAPI = (props) => { // const theme = useTheme(); - const { history } = props; + const { history, multiGateway } = props; const intl = useIntl(); const { data: settings, isLoading, error: settingsError } = usePublisherSettings(); const [pageError, setPageError] = useState(null); @@ -437,6 +437,7 @@ const APICreateStreamingAPI = (props) => { endpointPlaceholderText='Streaming Provider' appendChildrenBeforeEndpoint hideEndpoint={hideEndpoint} + multiGateway={multiGateway} isWebSocket={(apiType && apiType === protocolKeys.WebSocket) || apiInputs.protocol === protocolKeys.WebSocket} > diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/WSDL/ApiCreateWSDL.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/WSDL/ApiCreateWSDL.jsx index 62d4a9f39cd..89bf4a67de9 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/WSDL/ApiCreateWSDL.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/WSDL/ApiCreateWSDL.jsx @@ -47,7 +47,7 @@ import ProvideWSDL from './Steps/ProvideWSDL'; export default function ApiCreateWSDL(props) { const intl = useIntl(); const [wizardStep, setWizardStep] = useState(0); - const { history } = props; + const { history, multiGateway } = props; const [policies, setPolicies] = useState([]); useEffect(() => { @@ -255,6 +255,7 @@ export default function ApiCreateWSDL(props) { onChange={handleOnChange} api={apiInputs} isAPIProduct={false} + multiGateway={multiGateway} /> )} diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/WebSocket/ApiCreateWebSocket.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/WebSocket/ApiCreateWebSocket.jsx index 11dd3ac5963..529fd99088b 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/WebSocket/ApiCreateWebSocket.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/WebSocket/ApiCreateWebSocket.jsx @@ -16,7 +16,9 @@ import React from 'react'; import APICreateDefault from 'AppComponents/Apis/Create/Default/APICreateDefault'; -const ApiCreateWebSocket = () => { - return (); +const ApiCreateWebSocket = (props) => { + const {multiGateway} = props; + + return (); }; export default ApiCreateWebSocket; diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/DesignConfigurations.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/DesignConfigurations.jsx index 55873dc5544..7c8a94dfe60 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/DesignConfigurations.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Configuration/DesignConfigurations.jsx @@ -807,7 +807,9 @@ export default function DesignConfigurations() { />
- {api.apiType !== API.CONSTS.APIProduct && ( + {api.apiType !== API.CONSTS.APIProduct && + settings && settings.gatewayFeatureCatalog + .gatewayFeatures[api.gatewayType].basic.includes("advertised") && ( )} - { settings && !settings.portalConfigurationOnlyModeEnabled && ( - - - - )} + { settings && !settings.portalConfigurationOnlyModeEnabled && + settings.gatewayFeatureCatalog.gatewayFeatures[api.gatewayType] + .basic.includes("defaultVersion") && + + + + } - + + security + + + + + + } ) : ( @@ -1098,158 +1109,171 @@ function EndpointOverview(props) { setAdvancedConfigOpen={toggleAdvanceConfig} esCategory='production' setESConfigOpen={toggleEndpointSecurityConfig} - apiId={api.id} + iId={api.id} + componentValidator={componentValidator} /> )} {endpointType.key === 'prototyped' ?
: (
- ( - handleOnChangeEndpointCategoryChange - ( - 'sandbox', - ))} - /> - )} - label={( - - )} - /> - - {endpointType.key === 'default' - ? ( - -
- - - - + ( + handleOnChangeEndpointCategoryChange + ( + 'sandbox', + ))} + /> + )} + label={( + + )} + /> + + {endpointType.key === 'default' + ? ( + +
+ + - - -
-
+ +
+ ) + : ( + <> - - - - ) - : ( - - )} - className={classes. - defaultEndpointWrapper} - endpointURL={getEndpoints - ( - 'sandbox_endpoints' - )} - type='' - index={0} - category='sandbox_endpoints' - editEndpoint={editEndpoint} - esCategory='sandbox' - setAdvancedConfigOpen= - {toggleAdvanceConfig} - setESConfigOpen= - {toggleEndpointSecurityConfig} - apiId={api.id} - /> - )} -
+ )} + className={classes. + defaultEndpointWrapper} + endpointURL={getEndpoints + ( + 'sandbox_endpoints' + )} + type='' + index={0} + category='sandbox_endpoints' + editEndpoint={editEndpoint} + esCategory='sandbox' + setAdvancedConfigOpen= + {toggleAdvanceConfig} + setESConfigOpen= + {toggleEndpointSecurityConfig} + apiId={api.id} + componentValidator={componentValidator} + /> + {endpointCategory.sandbox && // eslint-disable-line + api.subtypeConfiguration?.subtype === 'AIAPI' && // eslint-disable-line + (apiKeyParamConfig.authHeader || apiKeyParamConfig.authQueryParameter) && // eslint-disable-line + ()} // eslint-disable-line + )} + + }
)} @@ -1289,8 +1313,8 @@ function EndpointOverview(props) { || api.type === 'WS' || endpointType.key === 'awslambda' || endpointType.key === 'service' - || api.gatewayType === 'wso2/apk' || api.subtypeConfiguration?.subtype === 'AIAPI' + || !componentValidator.includes('loadBalanceAndFailoverConfigurations') ?
: ( @@ -1316,12 +1340,13 @@ function EndpointOverview(props) { handleEndpointTypeSelect={handleEndpointTypeSelect} globalEpType={endpointType} apiType={api.type} + componentValidator={componentValidator} /> ) } - {api.gatewayType !== 'wso2/apk' && ( + {componentValidator.includes('advancedConfigurations') && ( @@ -1341,77 +1366,84 @@ function EndpointOverview(props) { )} - - - - - - - - {endpointSecurityConfig.category === 'production' ? ( - - ) : ( - - )} - - - - - - - - - - - - - - - - - - + {endpointSecurityTypes && endpointSecurityTypes.length > 0 && + <> + + + + + + + + {endpointSecurityConfig.category === 'production' ? ( + + ) : ( + + )} + + + + + + + + + + + + + + + + + + + + } ); } diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx index 6883c70c87f..c6a0ae0ead4 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/Endpoints.jsx @@ -23,15 +23,14 @@ import Typography from '@mui/material/Typography'; import { FormattedMessage, injectIntl } from 'react-intl'; import PropTypes from 'prop-types'; import Button from '@mui/material/Button'; -import { useAppContext } from 'AppComponents/Shared/AppContext'; +import { useAppContext, usePublisherSettings } from 'AppComponents/Shared/AppContext'; import { Link, withRouter } from 'react-router-dom'; import CustomSplitButton from 'AppComponents/Shared/CustomSplitButton'; import NewEndpointCreate from 'AppComponents/Apis/Details/Endpoints/NewEndpointCreate'; import { APIContext } from 'AppComponents/Apis/Details/components/ApiContext'; import cloneDeep from 'lodash.clonedeep'; import { isRestricted } from 'AppData/AuthManager'; -import { Alert } from 'AppComponents/Shared'; - +import { Alert, Progress } from 'AppComponents/Shared'; import API from 'AppData/api'; import EndpointOverview from './EndpointOverview'; import AIEndpoints from './AIEndpoints'; @@ -101,6 +100,7 @@ const defaultSwagger = { paths: {} }; */ function Endpoints(props) { const { intl, history } = props; + const { data: publisherSettings, isLoading } = usePublisherSettings(); const { api, updateAPI } = useContext(APIContext); const { settings } = useAppContext(); const [swagger, setSwagger] = useState(defaultSwagger); @@ -114,6 +114,8 @@ function Endpoints(props) { authHeader: null, authQueryParameter: null }); + const [componentValidator, setComponentValidator] = useState([]); + const [endpointSecurityTypes, setEndpointSecurityTypes] = useState([]); useEffect(() => { if (api.subtypeConfiguration?.subtype === 'AIAPI') { @@ -127,6 +129,15 @@ function Endpoints(props) { } }, []); + useEffect(() => { + if (!isLoading) { + setComponentValidator(publisherSettings.gatewayFeatureCatalog + .gatewayFeatures[api.gatewayType].endpoints); + setEndpointSecurityTypes(publisherSettings.gatewayFeatureCatalog + .gatewayFeatures[api.gatewayType].endpointSecurity); + } + }, [isLoading]); + const apiReducer = (initState, configAction) => { const tmpEndpointConfig = cloneDeep(initState.endpointConfig); const { action, value } = configAction; @@ -717,11 +728,17 @@ function Endpoints(props) { apiDispatcher({ action: 'endpointImplementationType', value: { endpointType, implementationType } }); }; + if (isLoading) { + return ; + } + return ( ( {/* Since the api is set to the state in component did mount, check both the api and the apiObject. */} - {(api.endpointConfig === null && apiObject.endpointConfig === null) - ? + {(api.endpointConfig === null && apiObject.endpointConfig === null) ? + : (
diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/GeneralConfiguration/EndpointSecurity.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/GeneralConfiguration/EndpointSecurity.jsx index 1caa1ebd99b..8d6d16b6809 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/GeneralConfiguration/EndpointSecurity.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/GeneralConfiguration/EndpointSecurity.jsx @@ -104,6 +104,7 @@ function EndpointSecurity(props) { const { settings } = useAppContext(); const { intl, securityInfo, onChangeEndpointAuth, isProduction, saveEndpointSecurityConfig, closeEndpointSecurityConfig, + endpointSecurityTypes, } = props; const [endpointSecurityInfo, setEndpointSecurityInfo] = useState(CONSTS.DEFAULT_ENDPOINT_SECURITY); @@ -127,30 +128,9 @@ function EndpointSecurity(props) { const endpointType = isProduction ? 'production' : 'sandbox'; const authTypes = () => { - const { gatewayType } = api; // Access gatewayType directly from api - - if (gatewayType === 'wso2/apk') { - return [ - { - key: 'NONE', - value: intl.formatMessage({ - id: 'Apis.Details.Endpoints.GeneralConfiguration.EndpointSecurity.none', - defaultMessage: 'None', - }), - }, - { - key: 'BASIC', - value: intl.formatMessage({ - id: 'Apis.Details.Endpoints.GeneralConfiguration.EndpointSecurity.basic', - defaultMessage: 'Basic Auth', - }), - }, - ]; - } - - // Default authTypes for other gateway types - return [ + const types = [ { + id: 'none', key: 'NONE', value: intl.formatMessage({ id: 'Apis.Details.Endpoints.GeneralConfiguration.EndpointSecurity.none', @@ -158,6 +138,7 @@ function EndpointSecurity(props) { }), }, { + id: 'basicAuth', key: 'BASIC', value: intl.formatMessage({ id: 'Apis.Details.Endpoints.GeneralConfiguration.EndpointSecurity.basic', @@ -165,6 +146,7 @@ function EndpointSecurity(props) { }), }, { + id: 'digest', key: 'DIGEST', value: intl.formatMessage({ id: 'Apis.Details.Endpoints.GeneralConfiguration.EndpointSecurity.digest.auth', @@ -172,6 +154,7 @@ function EndpointSecurity(props) { }), }, { + id: 'oauth2', key: 'OAUTH', value: intl.formatMessage({ id: 'Apis.Details.Endpoints.GeneralConfiguration.EndpointSecurity.oauth', @@ -179,6 +162,10 @@ function EndpointSecurity(props) { }), }, ]; + + const selectedTypes = [types[0]]; + + return selectedTypes.concat(types.filter((type) => endpointSecurityTypes?.includes(type.id))); }; const grantTypes = [ diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/GenericEndpoint.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/GenericEndpoint.jsx index f99c3397c0c..367620acf54 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/GenericEndpoint.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/GenericEndpoint.jsx @@ -117,6 +117,7 @@ function GenericEndpoint(props) { name, id, apiId, + componentValidator, } = props; const [serviceUrl, setServiceUrl] = useState(endpointURL); const { api } = useContext(APIContext); @@ -227,28 +228,30 @@ function GenericEndpoint(props) { ?
: ( <> - setAdvancedConfigOpen(index, type, category)} - disabled={(isRestricted(['apim:api_create'], api))} - id={category + '-endpoint-configuration-icon-btn'} - size='large'> - - )} - > - - settings - - - + {componentValidator.includes('advancedConfigurations') && + setAdvancedConfigOpen(index, type, category)} + disabled={(isRestricted(['apim:api_create'], api))} + id={category + '-endpoint-configuration-icon-btn'} + size='large'> + + )} + > + + settings + + + + } {api.subtypeConfiguration?.subtype !== 'AIAPI' && ( )} @@ -412,6 +414,7 @@ function LoadbalanceFailoverConfig(props) { setESConfigOpen={toggleESConfig} category='sandbox_endpoints' apiId={api.id} + componentValidator={componentValidator} /> )} diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/NewEndpointCreate.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/NewEndpointCreate.jsx index 838a3bf0363..b9699cd2c1b 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/NewEndpointCreate.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Endpoints/NewEndpointCreate.jsx @@ -95,6 +95,7 @@ function NewEndpointCreate(props) { intl, generateEndpointConfig, apiType, + componentValidator, } = props; const [endpointImplType, setImplType] = useState('mock'); const endpointTypes = [ @@ -138,7 +139,7 @@ function NewEndpointCreate(props) { disabled: ['GRAPHQL', 'SSE'], }, { - type: 'prototyped', + type: 'INLINE', name: intl.formatMessage({ id: 'Apis.Details.Endpoints.NewEndpointCreate.create.prototype.endpoint', defaultMessage: 'Mock Implementation', @@ -205,7 +206,7 @@ function NewEndpointCreate(props) { /> - {eligibleTypes.map(((type) => { + {(eligibleTypes.filter(ep => componentValidator.includes(ep.type)).map((type) => { return ( diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx index 8454e2571bf..9540d80a1de 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/DeploymentOnbording.jsx @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { styled } from '@mui/material/styles'; import { FormattedMessage } from 'react-intl'; import Typography from '@mui/material/Typography'; @@ -33,7 +33,7 @@ import AddIcon from '@mui/icons-material/Add'; import CardHeader from '@mui/material/CardHeader'; import PropTypes from 'prop-types'; import { useAppContext } from 'AppComponents/Shared/AppContext'; -import { useTheme } from '@mui/material'; +import { CircularProgress, useTheme } from '@mui/material'; import { useAPI } from 'AppComponents/Apis/Details/components/ApiContext'; import Checkbox from '@mui/material/Checkbox'; import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked'; @@ -142,71 +142,100 @@ export default function DeploymentOnboarding(props) { setDescription, gatewayVendor, advertiseInfo, + isDeploying, } = props; const [api] = useAPI(); const theme = useTheme(); const { maxCommentLength } = theme.custom; - const assignGateway = (api.gatewayType === "wso2/synapse" || api.apiType === "APIPRODUCT") ? "Regular" : "APK"; const { settings: { environment: environments } } = useAppContext(); - const internalGatewaysFiltered = environments.filter((p) => !p.provider.toLowerCase().includes('solace')); - const internalGateways = internalGatewaysFiltered && - internalGatewaysFiltered.filter((p) => p.gatewayType.toLowerCase() === assignGateway.toLowerCase()); - const externalGateways = environments.filter((p) => p.provider.toLowerCase().includes('solace')); - const hasOnlyOneEnvironment = internalGateways.length === 1; + const [internalGateways, setInternalGateways] = useState([]); + const [externalGateways, setExternalGateways] = useState([]); + const [selectedExternalGateway, setSelectedExternalGateway] = useState([]); const isEndpointAvailable = api.endpointConfig !== null; const isDeployButtonDisabled = (((api.type !== 'WEBSUB' && !isEndpointAvailable)) || api.workflowStatus === 'CREATED'); - const defaultVhosts = internalGateways.map((e) => { - if (e.vhosts && e.vhosts.length > 0) { - return { - env: e.name, - vhost: api.isWebSocket() ? e.vhosts[0].wsHost : e.vhosts[0].host - }; - } else { - return undefined; - } - }); - const [descriptionOpen, setDescriptionOpen] = useState(false); - const [selectedEnvironment, setSelectedEnvironment] = useState(hasOnlyOneEnvironment - ? [internalGateways[0].name] : []); + const [selectedEnvironment, setSelectedEnvironment] = useState([]); + const [selectedVhostDeploy, setVhostsDeploy] = useState(null); - /** - * Get Solace environments from the environments list - * @return String Solace gateway environment name - */ - function getSolaceEnvironment(envs) { - const solaceEnv= [] - envs.forEach((env) => { - if (env.provider === 'solace') { - solaceEnv.push(env.name); + useEffect(() => { + let gatewayType; + if (api.apiType === 'APIPRODUCT') { + gatewayType = 'Regular'; + } else { + switch (api.gatewayType) { + case 'wso2/synapse': + gatewayType = 'Regular'; + break; + case 'wso2/apk': + gatewayType = 'APK'; + break; + case 'AWS': + gatewayType = 'AWS'; + break; + default: + gatewayType = 'Regular'; } - }); - return solaceEnv; - } + } + const internalGatewaysFiltered = environments.filter((p) => p.provider.toLowerCase().includes('wso2')); + const selectedInternalGateways = internalGatewaysFiltered.filter((p) => + p.gatewayType.toLowerCase() === gatewayType.toLowerCase()) + if (selectedInternalGateways.length > 0) { + setInternalGateways(selectedInternalGateways); + const defaultVhosts = selectedInternalGateways.map((e) => { + if (e.vhosts && e.vhosts.length > 0) { + return { + env: e.name, + vhost: api.isWebSocket() ? e.vhosts[0].wsHost : e.vhosts[0].host + }; + } else { + return undefined; + } + }); + setVhostsDeploy(defaultVhosts); + setSelectedEnvironment(selectedInternalGateways.length === 1 ? [selectedInternalGateways[0].name] : []); + } else { + const external = environments.filter((p) => !p.provider.toLowerCase().includes('wso2')); + const selectedExternalGateways = external.filter((p) => + p.gatewayType.toLowerCase() === gatewayType.toLowerCase()); + setExternalGateways(selectedExternalGateways); + setSelectedExternalGateway(selectedExternalGateways.map((env) => env.name)); - /** - * Get Organization value of external gateways - * @param {Object} additionalProperties the additionalProperties list - * @return String organization name - */ - function getOrganizationFromAdditionalProperties(additionalProperties) { - let organization; - additionalProperties.forEach((property) => { - if (property.key === 'Organization') { - organization = property.value; + if (selectedExternalGateways.length > 0) { + const defaultVhosts = selectedExternalGateways.map((e) => { + if (e.vhosts && e.vhosts.length > 0) { + return { + env: e.name, + vhost: api.isWebSocket() ? e.vhosts[0].wsHost : e.vhosts[0].host + }; + } else { + return undefined; + } + }); + setVhostsDeploy(defaultVhosts); } - }); - return organization; - } + } + + }, []); - const [selectedSolaceEnvironment, setSelectedSolaceEnvironment] = useState( - getSolaceEnvironment(externalGateways), - ); - const [selectedVhostDeploy, setVhostsDeploy] = useState(defaultVhosts); + + // /** + // * Get Organization value of external gateways + // * @param {Object} additionalProperties the additionalProperties list + // * @return String organization name + // */ + // function getOrganizationFromAdditionalProperties(additionalProperties) { + // let organization; + // additionalProperties.forEach((property) => { + // if (property.key === 'Organization') { + // organization = property.value; + // } + // }); + // return organization; + // } /** * Handle Description @@ -222,12 +251,12 @@ export default function DeploymentOnboarding(props) { }; const handleChange = (event) => { - if (gatewayVendor === 'solace') { + if (gatewayVendor !== 'wso2') { if (event.target.checked) { - setSelectedSolaceEnvironment([...selectedSolaceEnvironment, event.target.value]); + setSelectedExternalGateway([...selectedExternalGateway, event.target.value]); } else { - setSelectedSolaceEnvironment( - selectedSolaceEnvironment.filter((env) => env !== event.target.value), + setSelectedExternalGateway( + selectedExternalGateway.filter((env) => env !== event.target.value), ); } } else { @@ -543,7 +572,10 @@ export default function DeploymentOnboarding(props) { - Solace Environments + ( - - + + + + {getVhostHelperText(row.name, + selectedVhostDeploy)} + + + )} + placement='right' + > + )} + SelectProps={{ + MenuProps: { + getContentAnchorEl: null, + }, + }} + name={row.name} + value={selectedVhostDeploy.find( + (v) => v.env === row.name, + ).vhost} + onChange={handleVhostDeploySelect} margin='dense' - value={row.name} - /> - + {row.vhosts?.map( + (vhost) => ( + + {api.isWebSocket() + ? vhost.wsHost : vhost.host} + + ), )} - /> - + + @@ -671,22 +722,24 @@ export default function DeploymentOnboarding(props) { diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx index 7444654a345..57ed4e0c9a0 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Environments.jsx @@ -62,6 +62,7 @@ import DialogTitle from '@mui/material/DialogTitle'; import CardHeader from '@mui/material/CardHeader'; import Checkbox from '@mui/material/Checkbox'; import InfoIcon from '@mui/icons-material/Info'; +import { CircularProgress } from '@mui/material'; import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import API from 'AppData/api'; @@ -69,7 +70,6 @@ import { ConfirmDialog } from 'AppComponents/Shared/index'; import { useRevisionContext } from 'AppComponents/Shared/RevisionContext'; import Utils from 'AppData/Utils'; import { Parser } from '@asyncapi/parser'; -import { upperCaseString } from 'AppData/stringFormatter'; import GovernanceViolations from 'AppComponents/Shared/Governance/GovernanceViolations'; import DisplayDevportal from './DisplayDevportal'; import DeploymentOnbording from './DeploymentOnbording'; @@ -508,33 +508,88 @@ export default function Environments() { const restApi = new API(); const restProductApi = new APIProduct(); const [selectedRevision, setRevision] = useState([]); - const assignGateway = (api.gatewayType === "wso2/synapse" || api.apiType === "APIPRODUCT") ? "Regular" : "APK"; - const externalGateways = settings && settings.environment.filter((p) => !p.provider.toLowerCase().includes('wso2')); - const internalGatewaysFiltered = settings && settings.environment.filter((p) => - p.provider.toLowerCase().includes('wso2')); - const internalGateways = internalGatewaysFiltered && internalGatewaysFiltered.filter((p) => - p.gatewayType.toLowerCase() === assignGateway.toLowerCase() - ); + const [internalGateways, setInternalGateways] = useState([]); + const [externalGateways, setExternalGateways] = useState([]); const [selectedVhosts, setVhosts] = useState(null); const [selectedVhostDeploy, setVhostsDeploy] = useState([]); + const [SelectedEnvironment, setSelectedEnvironment] = useState([]); + const [allExternalGateways, setAllExternalGateways] = useState([]); + const [noEnv, setNoEnv] = useState(false); + const [isUndeploying, setIsUndeploying] = useState(false); + const [isDeploying, setIsDeploying] = useState(false); + useEffect(() => { if (settings) { - const defaultVhosts = internalGateways.map((e) => { - if (e.vhosts && e.vhosts.length > 0) { - const env = e.name; - const vhost = api.isWebSocket() ? e.vhosts[0].wsHost : e.vhosts[0].host; - return { env, vhost }; + let gatewayType; + if (api.apiType === 'APIPRODUCT') { + gatewayType = 'Regular'; + } else { + switch (api.gatewayType) { + case 'wso2/synapse': + gatewayType = 'Regular'; + break; + case 'wso2/apk': + gatewayType = 'APK'; + break; + case 'AWS': + gatewayType = 'AWS'; + break; + default: + gatewayType = 'Regular'; + } + } + const internalGatewaysFiltered = settings.environment.filter((p) => + p.provider.toLowerCase().includes('wso2')); + const selectedInternalGateways = internalGatewaysFiltered.filter((p) => + p.gatewayType.toLowerCase() === gatewayType.toLowerCase()) + if (selectedInternalGateways.length > 0) { + setInternalGateways(selectedInternalGateways); + const defaultVhosts = selectedInternalGateways.map((e) => { + if (e.vhosts && e.vhosts.length > 0) { + return { + env: e.name, + vhost: api.isWebSocket() ? e.vhosts[0].wsHost : e.vhosts[0].host + }; + } else { + return undefined; + } + }); + setVhosts(defaultVhosts); + setVhostsDeploy(defaultVhosts); + setSelectedEnvironment(selectedInternalGateways.length === 1 ? + [selectedInternalGateways[0].name] : []); + } else { + const external = settings.environment.filter((p) => !p.provider.toLowerCase().includes('wso2')); + const selectedExternalGateways = external.filter((p) => + p.gatewayType.toLowerCase() === gatewayType.toLowerCase()); + setExternalGateways(selectedExternalGateways); + if (selectedExternalGateways.length > 0) { + selectedExternalGateways.forEach((env) => { + setAllExternalGateways((prev) => [...prev, env]); + }); + const defaultVhosts = selectedExternalGateways.map((e) => { + if (e.vhosts && e.vhosts.length > 0) { + return { + env: e.name, + vhost: api.isWebSocket() ? e.vhosts[0].wsHost : e.vhosts[0].host + }; + } else { + return undefined; + } + }); + setVhosts(defaultVhosts); + setVhostsDeploy(defaultVhosts); + setSelectedEnvironment(selectedExternalGateways.length === 1 ? + [selectedExternalGateways[0].name] : []); } else { - return undefined; + setNoEnv(true); } - }); - setVhosts(defaultVhosts); - setVhostsDeploy(defaultVhosts); + } } - }, [settings]); + }, [isLoading]); + const [extraRevisionToDelete, setExtraRevisionToDelete] = useState(null); const [description, setDescription] = useState(''); - const [SelectedEnvironment, setSelectedEnvironment] = useState([]); const [open, setOpen] = useState(false); const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false); const [revisionToDelete, setRevisionToDelete] = useState([]); @@ -542,24 +597,9 @@ export default function Environments() { const [revisionToRestore, setRevisionToRestore] = useState([]); const [currentLength, setCurrentLength] = useState(0); const [openDeployPopup, setOpenDeployPopup] = useState(history.location.state === 'deploy'); - const [externalEnvEndpoints, setExternalEnvEndpoints] = useState(null); const [isGovernanceViolation, setIsGovernanceViolation] = useState(false); const [governanceError, setGovernanceError] = useState(''); - const allExternalGatewaysMap = []; - const allExternalGateways = []; - if (externalGateways) { - externalGateways.forEach((env) => { - const revision = allEnvRevision && allEnvRevision.find( - (r) => r.deploymentInfo.some((e) => e.name === env.name), - ); - const envDetails = revision && revision.deploymentInfo.find((e) => e.name === env.name); - const disPlayDevportal = envDetails && envDetails.displayOnDevportal; - allExternalGatewaysMap[env.name] = { revision, disPlayDevportal }; - allExternalGateways.push(env); - }); - } - const externalEnvWithEndpoints = []; useEffect(() => { @@ -608,7 +648,6 @@ export default function Environments() { }); externalEnvWithEndpoints[env.name] = endpoints; }); - setExternalEnvEndpoints(externalEnvWithEndpoints); } }) }, [api.id]); @@ -654,17 +693,6 @@ export default function Environments() { setRevision(revisions); }; - const handleSelectForBrokers = (event) => { - const revisions = selectedRevision.filter((r) => r.env !== event.target.name); - const oldRevision = selectedRevision.find((r) => r.env === event.target.name); - let displayOnDevPortal = true; - if (oldRevision) { - displayOnDevPortal = oldRevision.displayOnDevPortal; - } - revisions.push({ env: event.target.name, revision: event.target.value, displayOnDevPortal }); - setRevision(revisions); - }; - /* const isDisplayOnDevPortalCheckedForThirdPartyEnv = (env) => { if (allExternalGatewaysMap[env].revision) { return allExternalGatewaysMap[env].revision.deploymentInfo.find( @@ -929,6 +957,7 @@ export default function Environments() { displayOnDevportal: false, }]; if (api.apiType !== API.CONSTS.APIProduct) { + setIsUndeploying(true); restApi.undeployRevision(api.id, revisionId, body) .then(() => { Alert.info(intl.formatMessage({ @@ -950,6 +979,7 @@ export default function Environments() { getRevision(); getDeployedEnv(); }); + setIsUndeploying(false); } else { restProductApi.undeployProductRevision(api.id, revisionId, body) .then(() => { @@ -1017,6 +1047,7 @@ export default function Environments() { vhost, }]; if (api.apiType !== API.CONSTS.APIProduct) { + setIsDeploying(true); restApi.deployRevision(api.id, revisionId, body).then((response) => { if (response && response.obj && response.obj.length > 0) { if (response.obj[0]?.status === null || response.obj[0]?.status === 'APPROVED') { @@ -1044,6 +1075,7 @@ export default function Environments() { }).finally(() => { getRevision(); getDeployedEnv(); + setIsDeploying(false); }); } else { restProductApi.deployProductRevision(api.id, revisionId, body) @@ -1089,10 +1121,11 @@ export default function Environments() { for (const env of envList) { body1.push({ name: env, - vhost: api.gatewayVendor === 'wso2' ? vhostList.find((v) => v.env === env).vhost : ' ', + vhost: vhostList.find((v) => v.env === env).vhost, displayOnDevportal: true, }); } + setIsDeploying(true); restApi.deployRevision(api.id, response.body.id, body1) .then(() => { Alert.info(intl.formatMessage({ @@ -1131,6 +1164,8 @@ export default function Environments() { history.replace(); getRevision(); getDeployedEnv(); + setIsDeploying(false); + setOpenDeployPopup(false); }); }) .catch((error) => { @@ -1159,7 +1194,6 @@ export default function Environments() { } console.error(error); }); - setOpenDeployPopup(false); } else { restProductApi.createProductRevision(api.id, body) .then((response) => { @@ -1266,21 +1300,6 @@ export default function Environments() { /> ); - /** - * Get Organization value of external gateways - * @param {Object} additionalProperties the additionalProperties list - * @return String organization name - */ - function getOrganizationFromAdditionalProperties(additionalProperties) { - let organization; - additionalProperties.forEach((property) => { - if (property.key === 'Organization') { - organization = property.value; - } - }); - return organization; - } - const confirmRestoreDialog = ( r.env === row.name && r.revision) || !selectedVhosts.some((v) => v.env === row.name && v.vhost) || (api.advertiseInfo && api.advertiseInfo.advertised) || - isDeployButtonDisabled + isDeployButtonDisabled || isDeploying } variant='outlined' onClick={() => @@ -1849,6 +1868,8 @@ export default function Environments() { id='Apis.Details.Environments.Environments.deploy.button' defaultMessage='Deploy' /> + {' '} + {isDeploying && }
); @@ -1943,7 +1964,7 @@ export default function Environments() { !selectedRevision.some((r) => r.env === row.name && r.revision) || !selectedVhosts.some((v) => v.env === row.name && v.vhost) || (api.advertiseInfo && api.advertiseInfo.advertised) || - isDeployButtonDisabled + isDeployButtonDisabled || isDeploying } variant='outlined' onClick={() => @@ -2003,7 +2024,7 @@ export default function Environments() { @@ -2956,7 +3025,7 @@ export default function Environments() { fullWidth disabled={ api.isRevision - || (settings + || (settings && settings.portalConfigurationOnlyModeEnabled) || !allRevisions || allRevisions.length === 0 } @@ -3003,13 +3072,13 @@ export default function Environments() { )} - {allRevisions && allRevisions.length !== 0 && (api.gatewayVendor === 'solace') - && (allExternalGateways.length > 0) && ( + {allRevisions && allRevisions.length !== 0 && (api.gatewayVendor !== 'wso2') + && (allExternalGateways.length > 0) && ( @@ -3018,43 +3087,23 @@ export default function Environments() { - {externalEnvEndpoints && ( - - - - )} - - - - - - {api && api.isDefaultVersion !== true ? ( ) @@ -3068,10 +3117,50 @@ export default function Environments() { )} + + + + )} + placement='top-end' + aria-label='New Deployment' + > + + + + + + + + + )} + placement='top-end' + aria-label='New Deployment' + > + + + + + @@ -3080,83 +3169,65 @@ export default function Environments() { {row.displayName} - {externalEnvEndpoints && ( - - {externalEnvEndpoints[row.name] && - externalEnvEndpoints[row.name].map((e) => { - return ( - - - - - - {e.uri} - - - ); - }) - } - - )} - - {row.name} - - - {getOrganizationFromAdditionalProperties(row.additionalProperties)} - - - {row.provider} - - - {allExternalGatewaysMap[row.name].revision != null - ? ( -
- - + {allEnvDeployments[row.name].revision != null ? ( + <> + +
+ {api.isWebSocket() + ? getGatewayAccessUrl(allEnvDeployments[row.name] + .vhost, 'WS') + .primary : getGatewayAccessUrl( + allEnvDeployments[row.name].vhost, 'HTTP', + ).primary} +
+
+ {api.isWebSocket() + ? getGatewayAccessUrl(allEnvDeployments[row.name] + .vhost, 'WS') + .secondary : getGatewayAccessUrl( + allEnvDeployments[row.name].vhost, 'HTTP', + ).secondary}
- ) : ( -
+ {api.isGraphql() + && ( + <> +
+ {getGatewayAccessUrl(allEnvDeployments[row.name] + .vhost, 'WS') + .primary} +
+
+ {getGatewayAccessUrl(allEnvDeployments[row.name] + .vhost, 'WS') + .secondary} +
+ + + )} + + + ) : ( + <> + + + + {getVhostHelperText(row.name, + selectedVhosts)} + + + )} + placement='bottom' + > )} SelectProps={{ @@ -3165,58 +3236,49 @@ export default function Environments() { }, }} name={row.name} - onChange={handleSelectForBrokers} + value={selectedVhosts.find((v) => v.env === row.name).vhost} + onChange={handleVhostSelect} margin='dense' variant='outlined' - style={{ width: '50%' }} - disabled={api.isRevision || - (settings && - settings.portalConfigurationOnlyModeEnabled - ) - || !allRevisions || allRevisions.length === 0} + className={classes.vhostSelect} + fullWidth + disabled={api.isRevision + || (settings && settings.portalConfigurationOnlyModeEnabled) + || !allRevisions || allRevisions.length === 0} + helperText={getVhostHelperText(row.name, selectedVhosts, + true, 100)} > - {allRevisions && allRevisions.length !== 0 - && allRevisions.map( - (number) => ( - - {number.displayName} - - ), - )} + {row.vhosts.map( + (vhost) => ( + + {api.isWebSocket() + ? vhost.wsHost : vhost.host} + + ), + )} - -
- )} + +
+ + )} + + {getDeployedRevisionComponent(row, allEnvRevisionMapping)} + + + {getDeployedRevisionStatusComponent(row, allEnvRevisionMapping)} + + + diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Permission.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Permission.jsx index ad29f151f3b..2c66e11e33f 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Permission.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Environments/Permission.jsx @@ -33,7 +33,7 @@ export default function SimplePopper(props) { } = props; const [anchorEl, setAnchorEl] = React.useState(null); - let msg = roles.toString(); + let msg = roles?.toString(); if (type === 'PUBLIC') { msg = 'No Visibility Restrictions!'; } diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/NewOverview/MetaData.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/NewOverview/MetaData.jsx index c9525681a65..bdd0ff75025 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/NewOverview/MetaData.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/NewOverview/MetaData.jsx @@ -145,7 +145,10 @@ function MetaData(props) { - {api.gatewayType === 'wso2/synapse' ? 'Regular' : 'APK'} + {api.gatewayType === 'wso2/synapse' && 'Regular'} + {api.gatewayType === 'wso2/apk' && 'APK'} + {api.gatewayType !== 'wso2/synapse' && api.gatewayType !== 'wso2/apk' + && api.gatewayType} diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Operations/Operation.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Operations/Operation.jsx index 66da78a503d..140b6d14153 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Operations/Operation.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Operations/Operation.jsx @@ -197,7 +197,7 @@ class Operation extends React.Component { constructor(props) { super(props); this.state = { - isSecurity: false, + isSecurity: props.componentValidator.includes('operationSecurity'), }; this.handleScopeChange = this.handleScopeChange.bind(this); this.handlePolicyChange = this.handlePolicyChange.bind(this); @@ -255,7 +255,7 @@ class Operation extends React.Component { */ render() { const { - operation, theme, apiPolicies, scopes, isOperationRateLimiting, sharedScopes, intl, + operation, theme, apiPolicies, scopes, isOperationRateLimiting, sharedScopes, intl, componentValidator, } = this.props; const dropdownScopes = [...scopes]; const { isSecurity } = this.state; @@ -288,127 +288,138 @@ class Operation extends React.Component { className={classes.chipActive} /> - - - - - (Array.isArray(selected) ? selected.join(', ') : selected), - }} - fullWidth - label={dropdownScopes.length !== 0 || sharedScopes ? intl.formatMessage({ - id: 'Apis.Details.Operations.Operation.operation.scope.label.default', - defaultMessage: 'Operation scope', - }) : intl.formatMessage({ - id: 'Apis.Details.Operations.Operation.operation.scope.label.notAvailable', - defaultMessage: 'No scope available', - })} - value={operation.scopes} - onChange={({ target: { value } }) => this.handleScopeChange({ - data: { value: value ? [value] : [] }, - })} - helperText={( - - )} - margin='dense' - variant='outlined' - disabled={isRestricted(['apim:api_publish', 'apim:api_create'])} - > - - - - {filteredApiScopes.length !== 0 ? filteredApiScopes.map((apiScope) => ( - - - {apiScope.scope.name} - - )) : ( - - + {componentValidator.includes('operationLevelRateLimiting') && + + + + } + {componentValidator.includes('operationSecurity') && + <> + + + (Array.isArray(selected) ? selected.join(', ') : selected), + }} + fullWidth + label={dropdownScopes.length !== 0 || sharedScopes ? intl.formatMessage({ + id: 'Apis.Details.Operations.Operation.operation.scope.label.default', + defaultMessage: 'Operation scope', + }) : intl.formatMessage({ + id: 'Apis.Details.Operations.Operation.operation.scope.label.notAvailable', + defaultMessage: 'No scope available', + })} + value={operation.scopes} + onChange={({ target: { value } }) => this.handleScopeChange({ + data: { value: value ? [value] : [] }, + })} + helperText={( - - - )} - - - - {sharedScopes && sharedScopes.length !== 0 ? sharedScopes.map((sharedScope) => ( - - - {sharedScope.scope.name} - - )) : ( - - + - - - )} - - - - { - if (operation.authType === 'None') { - return false; - } - return true; - })()} - onChange={this.handleChange} - value={isSecurity} - color='primary' - disabled={isRestricted(['apim:api_publish', 'apim:api_create'])} - data-testid={operation.target + '-security-btn'} - /> - + + {filteredApiScopes.length !== 0 ? filteredApiScopes.map((apiScope) => ( + + + {apiScope.scope.name} + + )) : ( + + + + + + )} + + + + {sharedScopes && sharedScopes.length !== 0 ? sharedScopes.map((sharedScope) => ( + + + {sharedScope.scope.name} + + )) : ( + + + + + + )} + + + + { + if (operation.authType === 'None') { + return false; + } + return true; + })()} + onChange={this.handleChange} + value={isSecurity} + color='primary' + disabled={isRestricted(['apim:api_publish', 'apim:api_create'])} + data-testid={operation.target + '-security-btn'} + /> + + + } ); } diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Operations/Operations.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Operations/Operations.jsx index b25f0fa31eb..5662e7b6ec1 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Operations/Operations.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Operations/Operations.jsx @@ -351,7 +351,7 @@ class Operations extends React.Component { * @inheritdoc */ render() { - const { api, resourceNotFoundMessage, intl} = this.props; + const { api, resourceNotFoundMessage, intl, componentValidator} = this.props; const { operations, apiPolicies, apiThrottlingPolicy, isSaving, filterKeyWord, notFound, sharedScopes, enableReadOnly, @@ -429,30 +429,37 @@ class Operations extends React.Component { /> - - - - - - - - - - - - - - - + {componentValidator.includes("operationLevelRateLimiting") && + + + + + + } + {componentValidator.includes("operationSecurity") && + <> + + + + + + + + + + + + } {operations.filter( (operation) => operation.target.toLowerCase().includes(filterKeyWord), @@ -465,6 +472,7 @@ class Operations extends React.Component { sharedScopes={sharedScopes} isOperationRateLimiting={!apiThrottlingPolicy} apiPolicies={apiPolicies} + componentValidator={componentValidator} /> ); })} diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/Policies.tsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/Policies.tsx index 7da252e5d7e..66dd0d0f953 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/Policies.tsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/Policies.tsx @@ -96,7 +96,7 @@ const Policies: React.FC = () => { const [policies, setPolicies] = useState([]); const [allPolicies, setAllPolicies] = useState(null); const [expandedResource, setExpandedResource] = useState(null); - const [isChoreoConnectEnabled, setIsChoreoConnectEnabled] = useState(api.gatewayType === 'wso2/apk'); + const [isChoreoConnectEnabled, setIsChoreoConnectEnabled] = useState(api.gatewayType !== 'wso2/synapse'); const { showMultiVersionPolicies } = Configurations.apis; const [selectedTab, setSelectedTab] = useState((api.apiPolicies != null) ? 0 : 1); @@ -203,10 +203,21 @@ const Policies: React.FC = () => { commonPolicyByPolicyDisplayName.sort( (a: Policy, b: Policy) => a.name.localeCompare(b.name)) + let gatewayType; + if (api.gatewayType === "wso2/apk") { + // Get CC gateway supported policies + gatewayType = 'ChoreoConnect'; + } else if (api.gatewayType === "AWS") { + // Get AWS gateway supported policies + gatewayType = 'AWS'; + } else { + // Get synpase gateway supported policies + gatewayType = 'Synapse'; + } + let filteredApiPolicyByGatewayTypeList = null; let filteredCommonPolicyByGatewayTypeList = null; - let gatewayType = isChoreoConnectEnabled ? 'ChoreoConnect' : 'Synapse'; // Get relevant gateway supported policies filteredApiPolicyByGatewayTypeList = apiPolicyByPolicyDisplayName.filter( (policy: Policy) => policy.supportedGateways.includes(gatewayType)); @@ -460,8 +471,6 @@ const Policies: React.FC = () => { setUpdating(true); const newApiOperations: any = cloneDeep(apiOperations); const newApiLevelPolicies: any = cloneDeep(apiLevelPolicies); - let getewayTypeForPolicies = "wso2/synapse"; - const getewayVendorForPolicies = "wso2"; deletePolicyUuid(newApiLevelPolicies); // Set operation policies to the API object @@ -472,16 +481,11 @@ const Policies: React.FC = () => { } }); - // Handles normal policy savings for choreo connect gateway type. - if(isChoreoConnectEnabled) { - getewayTypeForPolicies = "wso2/apk"; - } - const updatePromise = updateAPI({ operations: newApiOperations, apiPolicies: newApiLevelPolicies, - gatewayVendor: getewayVendorForPolicies, - gatewayType: getewayTypeForPolicies + gatewayVendor: api.gatewayVendor, + gatewayType: api.gatewayType, }); updatePromise .catch((error: any) => { @@ -625,6 +629,7 @@ const Policies: React.FC = () => { commonPolicyList={commonPolicies} fetchPolicies={fetchPolicies} isChoreoConnectEnabled={isChoreoConnectEnabled} + gatewayType={api.gatewayType} /> diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/PolicyList.tsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/PolicyList.tsx index 6a79999aa6d..b3449533941 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/PolicyList.tsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Policies/PolicyList.tsx @@ -72,6 +72,7 @@ interface PolicyListPorps { commonPolicyList: Policy[]; fetchPolicies: () => void; isChoreoConnectEnabled: boolean; + gatewayType: string; } /** @@ -79,11 +80,11 @@ interface PolicyListPorps { * @param {JSON} props Input props from parent components. * @returns {TSX} List of policies local to the API segment. */ -const PolicyList: FC = ({apiPolicyList, commonPolicyList, fetchPolicies, isChoreoConnectEnabled}) => { +const PolicyList: FC = ({apiPolicyList, commonPolicyList, fetchPolicies, isChoreoConnectEnabled, + gatewayType}) => { const [selectedTab, setSelectedTab] = useState(0); // Request flow related tab is active by default const [dialogOpen, setDialogOpen] = React.useState(false); - let gatewayType = CONSTS.GATEWAY_TYPE.synapse; const handleAddPolicy = () => { setDialogOpen(true); @@ -93,10 +94,6 @@ const PolicyList: FC = ({apiPolicyList, commonPolicyList, fetch setDialogOpen(false); }; - if (isChoreoConnectEnabled) { - gatewayType = CONSTS.GATEWAY_TYPE.choreoConnect; - } - return ( diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/Resources.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/Resources.jsx index fe2d2835a96..97d3279bdc0 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/Resources.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/Resources.jsx @@ -34,17 +34,20 @@ import SwaggerClient from 'swagger-client'; import { isRestricted } from 'AppData/AuthManager'; import CONSTS from 'AppData/Constants'; import Configurations from 'Config'; +import { usePublisherSettings } from 'AppComponents/Shared/AppContext'; +import { Progress } from 'AppComponents/Shared'; +import APIRateLimiting from './components/APIRateLimiting'; import Operation from './components/Operation'; import GroupOfOperations from './components/GroupOfOperations'; import AddOperation from './components/AddOperation'; import GoToDefinitionLink from './components/GoToDefinitionLink'; -import APIRateLimiting from './components/APIRateLimiting'; import { extractPathParameters, isSelectAll, mapAPIOperations, getVersion, VERSIONS, } from './operationUtils'; import OperationsSelector from './components/OperationsSelector'; import SaveOperations from './components/SaveOperations'; + /** * This component handles the Resource page in API details though it's written in a sharable way * that anyone could use this to render resources in anywhere else if needed. @@ -62,6 +65,7 @@ export default function Resources(props) { disableAddOperation, } = props; + const { data: publisherSettings, isLoading } = usePublisherSettings(); const [api, updateAPI] = useAPI(); const [pageError, setPageError] = useState(false); const [operationRateLimits, setOperationRateLimits] = useState([]); @@ -75,6 +79,7 @@ export default function Resources(props) { const [resolvedSpec, setResolvedSpec] = useState({ spec: {}, errors: [] }); const [focusOperationLevel, setFocusOperationLevel] = useState(false); const [expandedResource, setExpandedResource] = useState(false); + const [componentValidator, setComponentValidator] = useState([]); const intl = useIntl(); /** @@ -515,6 +520,13 @@ export default function Resources(props) { }); }, []); + useEffect(() => { + if (!isLoading) { + setComponentValidator(publisherSettings.gatewayFeatureCatalog + .gatewayFeatures[api.gatewayType].resources); + } + }, [isLoading]); + useEffect(() => { setApiThrottlingPolicy(api.apiThrottlingPolicy); }, [api.apiThrottlingPolicy]); @@ -611,6 +623,9 @@ export default function Resources(props) { ); } + if (isLoading) { + return ; + } return ( {pageError && ( @@ -621,6 +636,7 @@ export default function Resources(props) { {!disableRateLimiting && ( )} {Object.entries(operations).map(([target, verbObject]) => ( @@ -688,6 +705,7 @@ export default function Resources(props) { setFocusOperationLevel={setFocusOperationLevel} expandedResource={expandedResource} setExpandedResource={setExpandedResource} + componentValidator={componentValidator} /> ) : null; diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/APIRateLimiting.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/APIRateLimiting.jsx index 8026dd54f7e..92629543984 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/APIRateLimiting.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/APIRateLimiting.jsx @@ -38,6 +38,8 @@ import CircularProgress from '@mui/material/CircularProgress'; import { isRestricted } from 'AppData/AuthManager'; import { useAPI } from 'AppComponents/Apis/Details/components/ApiContext'; import { FormattedMessage, useIntl } from 'react-intl'; +import { usePublisherSettings } from 'AppComponents/Shared/AppContext'; +import { Progress } from 'AppComponents/Shared'; const PREFIX = 'APIRateLimiting'; @@ -70,15 +72,16 @@ const RateLimitingLevels = { */ function APIRateLimiting(props) { const { - updateAPI, operationRateLimits, onChange, value: currentApiThrottlingPolicy, isAPIProduct, + api, updateAPI, operationRateLimits, onChange, value: currentApiThrottlingPolicy, isAPIProduct, setFocusOperationLevel, focusOperationLevel, } = props; const intl = useIntl(); + const { data: publisherSettings, isLoading } = usePublisherSettings(); const [apiThrottlingPolicy, setApiThrottlingPolicy] = useState(currentApiThrottlingPolicy); const [isSaving, setIsSaving] = useState(false); - - const isResourceLevel = apiThrottlingPolicy === null; - const rateLimitingLevel = isResourceLevel ? RateLimitingLevels.RESOURCE : RateLimitingLevels.API; + const [componentValidator, setComponentValidator] = useState([]); + const [isResourceLevel, setIsResourceLevel] = useState(false); + const [rateLimitingLevel, setRateLimitingLevel] = useState(RateLimitingLevels.API); const [apiFromContext] = useAPI(); // Following effect is used to handle the controlled component case, If user provide onChange handler to @@ -93,6 +96,18 @@ function APIRateLimiting(props) { } }, [onChange, currentApiThrottlingPolicy]); // Do not expect to change the onChange during the runtime + useEffect(() => { + setIsResourceLevel(apiThrottlingPolicy === null || !componentValidator.includes('apiLevelRateLimiting')); + setRateLimitingLevel(isResourceLevel ? RateLimitingLevels.RESOURCE : RateLimitingLevels.API); + }, [apiThrottlingPolicy, isResourceLevel]); + + useEffect(() => { + if (!isLoading) { + setComponentValidator(publisherSettings.gatewayFeatureCatalog + .gatewayFeatures[api.gatewayType].resources); + } + }, [isLoading]); + /** * * @@ -108,7 +123,7 @@ function APIRateLimiting(props) { } else { setApiThrottlingPolicy(userSelection); } - if (event.target.value === RateLimitingLevels.RESOURCE) { + if (event.target.value === RateLimitingLevels.RESOURCE && setFocusOperationLevel) { setFocusOperationLevel(false); } } @@ -160,154 +175,163 @@ function APIRateLimiting(props) { ); } + if (isLoading) { + return ; + } return ( - - - - - - - componentValidator.includes(type)) && + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + )} + label={intl.formatMessage({ + id: 'Apis.Details.Rate.Limiting.rate.limiting.level.api.level', + defaultMessage: 'API Level', + })} + labelPlacement='end' + id='api-rate-limiting-api-level' /> - )} - label={intl.formatMessage({ - id: 'Apis.Details.Rate.Limiting.rate.limiting.level.api.level', - defaultMessage: 'API Level', - })} - labelPlacement='end' - id='api-rate-limiting-api-level' - /> - + )} + className={focusOperationLevel && classes.focusLabel} + label={intl.formatMessage({ + id: 'Apis.Details.Rate.Limiting.rate.limiting.level.operation.level', + defaultMessage: 'Operation Level', + })} + labelPlacement='end' + id='api-rate-limiting-operation-level' /> - )} - className={focusOperationLevel && classes.focusLabel} - label={intl.formatMessage({ - id: 'Apis.Details.Rate.Limiting.rate.limiting.level.operation.level', - defaultMessage: 'Operation Level', - })} - labelPlacement='end' - id='api-rate-limiting-operation-level' - /> - - - - - - {isResourceLevel ? ( - operationRateLimitMessage - ) : ( - ( - onChange ? onChange(value) : setApiThrottlingPolicy(value))} - helperText={intl.formatMessage({ - id: 'Apis.Details.Rate.Limiting.rate.limiting.policies.helper.text', - defaultMessage: 'Selected rate limiting policy will be applied to whole API', - })} - margin='dense' - variant='outlined' - > - {operationRateLimits.map((rateLimit) => ( - - {rateLimit.displayName} - - ))} - - )} - - - {/* If onChange handler is provided we assume that component is getting controlled by its parent - so that, hide the save cancel action */} - {!onChange && ( - <> - - - - - - - - + + + - - - - )} - - + + + )} + + ); } APIRateLimiting.defaultProps = { diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/AsyncOperation.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/AsyncOperation.jsx index cf5dbe2fe49..f942314a995 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/AsyncOperation.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/Resources/components/AsyncOperation.jsx @@ -94,6 +94,7 @@ function AsyncOperation(props) { target, verb, sharedScopes, + componentValidator } = props; const trimmedVerb = verb === 'publish' || verb === 'subscribe' ? verb.substr(0, 3) : verb; @@ -253,6 +254,7 @@ function AsyncOperation(props) { target={target} verb={verb} sharedScopes={sharedScopes} + componentValidator={componentValidator} /> {(api.type === 'WS' || api.type === 'WEBSUB') && ( {!hideParameters && ( )} - { (operationWithSecurityCount === operationCount) + { (componentValidator.includes('operationSecurity') && + (operationWithSecurityCount === operationCount)) && ( )} - { (operationWithSecurityCount !== 0 && operationWithSecurityCount !== operationCount) + { (componentValidator.includes('operationSecurity') && + operationWithSecurityCount !== 0 && operationWithSecurityCount !== operationCount) && ( ; export default function OperationGovernance(props) { const { operation, operationsDispatcher, operationRateLimits, api, disableUpdate, spec, target, verb, sharedScopes, - setFocusOperationLevel, + setFocusOperationLevel, componentValidator } = props; const operationScopes = getOperationScopes(operation, spec); - const isOperationRateLimiting = api.apiThrottlingPolicy === null; + const isOperationRateLimiting = api.apiThrottlingPolicy === null || + !componentValidator.includes('apiLevelRateLimiting'); const filteredApiScopes = api.scopes.filter((sharedScope) => !sharedScope.shared); const intl = useIntl(); const scrollToTop = () => { @@ -82,251 +83,262 @@ export default function OperationGovernance(props) { - - - operationsDispatcher({ - action: 'authType', - data: { target, verb, value: checked }, - })} - size='small' - color='primary' - /> - )} - label={intl.formatMessage({ - id: 'Apis.Details.Topics.components.operationComponents.OperationGovernance.' - + 'security.switch', - defaultMessage: 'Security', - })} - labelPlacement='start' - /> - - - - )} - fontSize='small' - placement='right-end' - interactive - > - - - - + {componentValidator.includes('operationSecurity') && + + + operationsDispatcher({ + action: 'authType', + data: { target, verb, value: checked }, + })} + size='small' + color='primary' + /> + )} + label={intl.formatMessage({ + id: 'Apis.Details.Topics.components.operationComponents.OperationGovernance.' + + 'security.switch', + defaultMessage: 'Security', + })} + labelPlacement='start' + /> + + + + )} + fontSize='small' + placement='right-end' + interactive + > + + + + + } - - - - + {componentValidator.includes('operationLevelRateLimiting') && + + - -
- ) - } - value={ - isOperationRateLimiting - && operation['x-throttling-tier'] - ? operation['x-throttling-tier'] - : '' - } - onChange={({ target: { value } }) => operationsDispatcher({ - action: 'throttlingPolicy', - data: { target, verb, value }, - })} - helperText={ - isOperationRateLimiting - ? intl.formatMessage({ - id: 'Apis.Details.Resources.components.operationComponents.' - + 'OperationGovernance.rate.limiting.policy.select', - defaultMessage: 'Select a rate limit policy for this operation', - }) - : ( - - - + + + +
+ ) + } + value={ + isOperationRateLimiting + && operation['x-throttling-tier'] + ? operation['x-throttling-tier'] + : '' + } + onChange={({ target: { value } }) => operationsDispatcher({ + action: 'throttlingPolicy', + data: { target, verb, value }, + })} + helperText={ + isOperationRateLimiting + ? intl.formatMessage({ + id: 'Apis.Details.Resources.components.operationComponents.' + + 'OperationGovernance.rate.limiting.policy.select', + defaultMessage: 'Select a rate limit policy for this operation', + }) + : ( + - - - + + + - - - - ) - } - margin='dense' - variant='outlined' - id={verb + target + '-operation_throttling_policy'} - > - {operationRateLimits.map((rateLimit) => ( - - {rateLimit.displayName} - - ))} - - {!isOperationRateLimiting && ( - - )} - + + + + + + ) + } + margin='dense' + variant='outlined' + id={verb + target + '-operation_throttling_policy'} + > + {operationRateLimits.map((rateLimit) => ( + + {rateLimit.displayName} + + ))} + + {!isOperationRateLimiting && ( + + )} + + } - - {operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? ( - option.shared ? 'Shared Scopes' : 'API Scopes'} - noOptionsText={intl.formatMessage({ - id: 'Apis.Details.Topics.components.operationComponents.OperationGovernance.' - + 'no.scopes.available', - defaultMessage: 'No scopes available', - })} - disableCloseOnSelect - value={operationScopes.map((scope) => ({ scope: { name: scope } }))} - getOptionLabel={(option) => option.scope.name} - isOptionEqualToValue={(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] : [] }, - }); - }} - renderOption={(listOfOptions, option, { selected }) => ( -
  • - - {option.scope.name} -
  • - )} - style={{ width: 500 }} - renderInput={(params) => ( - + + {operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? ( + option.shared ? 'Shared Scopes' : 'API Scopes'} + noOptionsText={intl.formatMessage({ id: 'Apis.Details.Topics.components.operationComponents.OperationGovernance.' - + 'search.scopes.placeholder', - defaultMessage: 'Search scopes', + + 'no.scopes.available', + defaultMessage: 'No scopes available', })} - helperText={( - + disableCloseOnSelect + value={operationScopes.map((scope) => ({ scope: { name: scope } }))} + getOptionLabel={(option) => option.scope.name} + isOptionEqualToValue={(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] : [] }, + }); + }} + renderOption={(listOfOptions, option, { selected }) => ( +
  • + + {option.scope.name} +
  • + )} + style={{ width: 500 }} + renderInput={(params) => ( + + )} + margin='dense' + variant='outlined' + id={verb + target + '-operation-scope-select'} /> )} - margin='dense' - variant='outlined' - id={verb + target + '-operation-scope-select'} /> - )} - /> - ) : null} -
    - - { operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' ? !disableUpdate && ( - - - - - - - ) : null} - + ) : null} +
    + + { operation['x-auth-type'] && operation['x-auth-type'].toLowerCase() !== 'none' + ? !disableUpdate && ( + + + + + + + ) : null} + + + } ); diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/components/leftMenu/DevelopSectionMenu.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/components/leftMenu/DevelopSectionMenu.jsx index a47825e6f99..984f6b00a76 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/components/leftMenu/DevelopSectionMenu.jsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Details/components/leftMenu/DevelopSectionMenu.jsx @@ -114,7 +114,8 @@ const AccordionDetails = MuiAccordionDetails; */ export default function DevelopSectionMenu(props) { const { - pathPrefix, isAPIProduct, api, getLeftMenuItemForResourcesByType, getLeftMenuItemForDefinitionByType, + pathPrefix, isAPIProduct, api, getLeftMenuItemForResourcesByType, getLeftMenuItemForDefinitionByType, + componentValidator, } = props; const user = useUser(); const [portalConfigsExpanded, setPortalConfigsExpanded] = useState(user @@ -175,7 +176,7 @@ export default function DevelopSectionMenu(props) { id='left-menu-itembusinessinfo' route='business-info' /> - {!isAPIProduct && ( + {(componentValidator.subscriptions.includes("subscriptions") && !isAPIProduct) && (
    - {!isAPIProduct && !api.isWebSocket() && (api.gatewayVendor === 'wso2') && ( + {!isAPIProduct && !api.isWebSocket() && ( )} - {!isAPIProduct && (api.gatewayVendor === 'wso2') && ( + {(componentValidator.localScopes.includes("localScopes") + && (!isAPIProduct)) && } id='left-menu-itemLocalScopes' /> - )} + } {api.advertiseInfo && !api.advertiseInfo.advertised && !isAPIProduct && (api.type === 'HTTP' || api.type === 'SOAP' || api.type === 'SOAPTOREST') && ( - {!api.isWebSocket() && !isRestricted(['apim:api_publish'], api) && ( + {(componentValidator.monetization.includes("monetization") && + (!api.isWebSocket() && !isRestricted(['apim:api_publish'], api))) && ( <> - {!isAPIProduct && (api.gatewayVendor === 'wso2') && ( + {!isAPIProduct && (