Skip to content
Open
10 changes: 10 additions & 0 deletions portals/admin/src/main/webapp/site/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,7 @@
"GatewayEnvironments.AddEditGWEnvironment.general.details.description.platform": "Provide display name and description of the Gateway Environment",
"GatewayEnvironments.AddEditGWEnvironment.mode": "Gateway Mode",
"GatewayEnvironments.AddEditGWEnvironment.mode.description": "Deployability or discoverabilty of APIs",
"GatewayEnvironments.AddEditGWEnvironment.save.failed": "Save failed",
"GatewayEnvironments.AddEditGWEnvironment.type.description": "Key type supported by the Gateway Environment",
"GatewayEnvironments.AddEditGWEnvironment.visibility.add.description": "Configure vhosts",
"GatewayEnvironments.AddEditVhost.add.vhost.btn": "New VHost",
Expand All @@ -591,6 +592,15 @@
"GatewayEnvironments.AddEditVhost.httpsPort": "HTTPS Port",
"GatewayEnvironments.AddEditVhost.wsPort": "WS Port",
"GatewayEnvironments.AddEditVhost.wssPort": "WSS Port",
"GatewayEnvironments.GatewayConfiguration.advancedSettings": "Advanced Settings",
"GatewayEnvironments.PlanMapping.action": "Action",
"GatewayEnvironments.PlanMapping.addValidationError": "Provide a local subscription policy and remote plan name.",
"GatewayEnvironments.PlanMapping.alreadyExists": "Plan Mapping already exists.",
"GatewayEnvironments.PlanMapping.loadingPolicies": "Loading local subscription plans...",
"GatewayEnvironments.PlanMapping.localPolicyRequired": "Select a local subscription policy.",
"GatewayEnvironments.PlanMapping.noCompatibleLocalPlans": "No local subscription plans match the supported API types of this gateway.",
"GatewayEnvironments.PlanMapping.noPoliciesLeft": "No local subscription policies left to map",
"GatewayEnvironments.PlanMapping.remotePlanRequired": "Enter a remote plan name.",
"Gateways.AddEditGateway.back.to.gateways": "Back to Gateways",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"Gateways.AddEditGateway.loading.gateway.details": "Loading gateway details...",
"Gateways.AddEditGateway.loading.platform.gateway.details": "Loading platform gateway details...",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
} from './PlatformGatewayUtils';

const PREFIX = 'AddEditGWEnvironment';
const FEDERATED_GATEWAY_VALIDATION_ERROR_CODE = 900520;

const classes = {
pageContent: `${PREFIX}-pageContent`,
Expand Down Expand Up @@ -365,6 +366,23 @@
return false;
};

const extractPlanMappingErrors = (responseBody) => {
const rowErrors = {};
if (!responseBody
|| responseBody.code !== FEDERATED_GATEWAY_VALIDATION_ERROR_CODE

Check warning on line 372 in portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/AddEditGWEnvironment.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ4BTAD3yd-KZZrxMJyL&open=AZ4BTAD3yd-KZZrxMJyL&pullRequest=1343
|| !Array.isArray(responseBody.error)) {
return rowErrors;
}

responseBody.error.forEach((errorItem) => {
const fieldKey = errorItem?.message;
if (fieldKey && fieldKey.startsWith('plan_mapping.')) {

Check warning on line 379 in portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/AddEditGWEnvironment.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ4BTAD3yd-KZZrxMJyM&open=AZ4BTAD3yd-KZZrxMJyM&pullRequest=1343
rowErrors[fieldKey] = errorItem.description || 'Invalid external plan assignment';
Comment on lines +377 to +380

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard errorItem.message type before calling startsWith.

Line 380 can throw if errorItem.message is a non-string truthy value. Add a string type guard to keep error handling resilient to payload drift.

Suggested fix
     responseBody.error.forEach((errorItem) => {
         const fieldKey = errorItem?.message;
-        if (fieldKey && fieldKey.startsWith(PLAN_MAPPING_PROPERTY_PREFIX)) {
+        if (typeof fieldKey === 'string' && fieldKey.startsWith(PLAN_MAPPING_PROPERTY_PREFIX)) {
             rowErrors[fieldKey] = errorItem.description || 'Invalid external plan assignment';
         }
     });
🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[warning] 380-380: Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ3xiGXE5yp55qrRGhAT&open=AZ3xiGXE5yp55qrRGhAT&pullRequest=1338

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/AddEditGWEnvironment.jsx`
around lines 378 - 381, The loop over responseBody.error calls startsWith on
errorItem.message which can be a non-string; update the handler in the
responseBody.error.forEach callback to first check typeof errorItem.message ===
'string' (or coerce safely) before using startsWith with
PLAN_MAPPING_PROPERTY_PREFIX, and only then assign rowErrors[fieldKey] =
errorItem.description || 'Invalid external plan assignment'; ensure fieldKey
references the validated string value (e.g., const fieldKey = errorItem.message)
so non-string values are skipped and no runtime exception is thrown.

}
});
return rowErrors;
};

/**
* Reducer
* @param {JSON} state State
Expand Down Expand Up @@ -411,9 +429,11 @@
const [invalidRoles, setInvalidRoles] = useState([]);
const [roleValidity, setRoleValidity] = useState(true);
const [gatewayConfigurations, setGatewayConfiguration] = useState([]);
const [selectedGatewaySupportedApiTypes, setSelectedGatewaySupportedApiTypes] = useState([]);
const [supportedModes, setSupportedModes] = useState([]);
const [validating, setValidating] = useState(false);
const [saving, setSaving] = useState(false);
const [planMappingErrors, setPlanMappingErrors] = useState({});
const { gatewayTypes } = settings;
const gatewayVersions = useMemo(() => getPlatformGatewayVersions(settings), [settings]);
const {
Expand Down Expand Up @@ -638,6 +658,10 @@
}
}, [permissions]);

useEffect(() => {
setPlanMappingErrors({});
}, [gatewayType]);

useEffect(() => {
if (!platformHeaderEditMode) {
setPlatformDisplayNameDraft(
Expand All @@ -657,7 +681,7 @@
]);

useEffect(() => {
const config = settings.gatewayConfiguration.filter(
const config = (settings.gatewayConfiguration || []).filter(

Check warning on line 684 in portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/AddEditGWEnvironment.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `.find(…)` over `.filter(…)[0]`.

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ4BTAD3yd-KZZrxMJyN&open=AZ4BTAD3yd-KZZrxMJyN&pullRequest=1343
(t) => t.type === gatewayType,
)[0];
if (
Expand All @@ -667,11 +691,13 @@
) {
setGatewayConfiguration([]);
setSupportedModes([]);
setSelectedGatewaySupportedApiTypes([]);
} else {
setGatewayConfiguration(config.configurations || []);
setSupportedModes(config.supportedModes || []);
setSelectedGatewaySupportedApiTypes(config.supportedApiTypes || []);
}
}, [gatewayType]);
}, [gatewayType, settings.gatewayConfiguration]);

let permissionType = '';
if (permissions) {
Expand Down Expand Up @@ -881,6 +907,13 @@
};

const setAdditionalProperties = (key, value) => {
if (key && planMappingErrors[key]) {
setPlanMappingErrors((prevErrors) => {
const updatedErrors = { ...prevErrors };
delete updatedErrors[key];
return updatedErrors;
});
}
const clonedAdditionalProperties = cloneDeep(additionalProperties);
if (value === undefined) {
delete clonedAdditionalProperties[key];
Expand Down Expand Up @@ -1189,6 +1222,7 @@

promiseAPICall
.then((result) => {
setPlanMappingErrors({});
if (id) {
Alert.success(
`${name} ${intl.formatMessage({
Expand Down Expand Up @@ -1226,8 +1260,28 @@
})
.catch((error) => {
const { response } = error;
if (response.body) {
Alert.error(response.body.description);
if (response?.body) {
setPlanMappingErrors(extractPlanMappingErrors(response.body));
Alert.error(
response.body.description
|| error?.message
|| intl.formatMessage({
id: 'GatewayEnvironments.AddEditGWEnvironment.save.failed',
defaultMessage: 'Save failed',
}),
);
} else {
setPlanMappingErrors({});
const statusSuffix = response?.status
? ` (${response.status}${response?.statusText ? ` ${response.statusText}` : ''})`

Check warning on line 1276 in portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/AddEditGWEnvironment.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this code to not use nested template literals.

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ4BTAD3yd-KZZrxMJyP&open=AZ4BTAD3yd-KZZrxMJyP&pullRequest=1343

Check warning on line 1276 in portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/AddEditGWEnvironment.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ4BTAD3yd-KZZrxMJyO&open=AZ4BTAD3yd-KZZrxMJyO&pullRequest=1343
: '';
Alert.error(
error?.message
|| `${intl.formatMessage({
id: 'GatewayEnvironments.AddEditGWEnvironment.save.failed',
defaultMessage: 'Save failed',
})}${statusSuffix}`,
);
}
setSaving(false);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});
Expand Down Expand Up @@ -2371,6 +2425,7 @@
gatewayConfigurations={
gatewayConfigurations
}
supportedApiTypes={selectedGatewaySupportedApiTypes}
additionalProperties={cloneDeep(
additionalProperties,
)}
Expand All @@ -2383,6 +2438,9 @@
validating={
validating
}
planMappingErrors={
planMappingErrors
}
gatewayId={cloneDeep(
id,
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useMemo } from 'react';
import { styled } from '@mui/material/styles';
import TextField from '@mui/material/TextField';
import FormControlLabel from '@mui/material/FormControlLabel';
Expand All @@ -10,9 +10,15 @@
import { FormattedMessage } from 'react-intl';
import InputLabel from '@mui/material/InputLabel';
import FormHelperText from '@mui/material/FormHelperText';
import Accordion from '@mui/material/Accordion';
import AccordionSummary from '@mui/material/AccordionSummary';
import AccordionDetails from '@mui/material/AccordionDetails';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import CustomGatewayInputField from 'AppComponents/GatewayEnvironments/CustomGatewayInputField';
import GatewayPlanMapping from './GatewayPlanMapping';

const StyledSpan = styled('span')(({ theme }) => ({ color: theme.palette.error.dark }));
const PLAN_MAPPING_CONFIG_TYPE = 'plan_mapping';

// Styled wrapper to mimic TextField's outlined style
const StyledFormControl = styled(FormControl)(({ theme }) => ({
Expand Down Expand Up @@ -42,9 +48,15 @@
export default function GatewayConfiguration(props) {
const {
gatewayConfigurations, additionalProperties = {}, setAdditionalProperties = () => {}, gatewayId,
hasErrors, validating,
hasErrors, validating, planMappingErrors = {}, supportedApiTypes = [],

Check warning on line 51 in portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/GatewayConfiguration.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'hasErrors' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ4BTABgyd-KZZrxMJyE&open=AZ4BTABgyd-KZZrxMJyE&pullRequest=1343

Check warning on line 51 in portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/GatewayConfiguration.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'planMappingErrors' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ4BTABgyd-KZZrxMJyG&open=AZ4BTABgyd-KZZrxMJyG&pullRequest=1343

Check warning on line 51 in portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/GatewayConfiguration.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'supportedApiTypes' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ4BTABgyd-KZZrxMJyH&open=AZ4BTABgyd-KZZrxMJyH&pullRequest=1343

Check warning on line 51 in portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/GatewayConfiguration.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'validating' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ4BTABgyd-KZZrxMJyF&open=AZ4BTABgyd-KZZrxMJyF&pullRequest=1343
} = props;

const mappingConfigurationNames = useMemo(() => {
return gatewayConfigurations
.filter((config) => config.type === PLAN_MAPPING_CONFIG_TYPE)

Check warning on line 56 in portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/GatewayConfiguration.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'gatewayConfigurations.filter' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ4BTABgyd-KZZrxMJyI&open=AZ4BTABgyd-KZZrxMJyI&pullRequest=1343
.map((config) => config.name);
}, [gatewayConfigurations]);

const getAllNestedGatewayConfigPropertyNames = (connectorConfigurations, parentKey = '') => {
const gatewayConfigPropertyNames = [];

Expand Down Expand Up @@ -167,7 +179,13 @@

// Clear any properties in additionalProperties that are not in the current valid set
Object.keys(additionalProperties).forEach((propName) => {
if (!currentValidProperties.includes(propName)) {
const isMappingProperty = mappingConfigurationNames.some((configName) => (
propName.startsWith(`${configName}.`)
));
if (
!currentValidProperties.includes(propName)
&& !isMappingProperty
) {
setAdditionalProperties(propName, undefined);
}
});
Expand Down Expand Up @@ -207,7 +225,17 @@
});
}
}
if (gatewayConfiguration.type === 'input') {
if (gatewayConfiguration.type === PLAN_MAPPING_CONFIG_TYPE) {
return (
<GatewayPlanMapping
gatewayConfiguration={gatewayConfiguration}
supportedApiTypes={supportedApiTypes}
additionalProperties={additionalProperties}
setAdditionalProperties={setAdditionalProperties}
planMappingErrors={planMappingErrors}
/>
);
} else if (gatewayConfiguration.type === 'input') {
if (gatewayConfiguration.mask) {
return (
<FormControl variant='outlined' fullWidth disabled={disabled}>
Expand Down Expand Up @@ -333,9 +361,29 @@
});
};

const regularConfigurations = gatewayConfigurations.filter(

Check warning on line 364 in portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/GatewayConfiguration.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'gatewayConfigurations.filter' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ4BTABgyd-KZZrxMJyJ&open=AZ4BTABgyd-KZZrxMJyJ&pullRequest=1343
(config) => config.type !== PLAN_MAPPING_CONFIG_TYPE,
);
const mappingConfigurations = gatewayConfigurations.filter(

Check warning on line 367 in portals/admin/src/main/webapp/source/src/app/components/GatewayEnvironments/GatewayConfiguration.jsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'gatewayConfigurations.filter' is missing in props validation

See more on https://sonarcloud.io/project/issues?id=wso2_apim-apps&issues=AZ4BTABgyd-KZZrxMJyK&open=AZ4BTABgyd-KZZrxMJyK&pullRequest=1343
(config) => config.type === PLAN_MAPPING_CONFIG_TYPE,
);

return (
<div>
{renderConnectorConfigurations(gatewayConfigurations)}
{renderConnectorConfigurations(regularConfigurations)}
{mappingConfigurations.length > 0 && (
<Accordion sx={{ mt: 2 }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<FormattedMessage
id='GatewayEnvironments.GatewayConfiguration.advancedSettings'
defaultMessage='Advanced Settings'
/>
</AccordionSummary>
<AccordionDetails>
{renderConnectorConfigurations(mappingConfigurations)}
</AccordionDetails>
</Accordion>
)}
</div>
);
}
Expand All @@ -350,4 +398,6 @@
/>,
hasErrors: () => {},
validating: false,
planMappingErrors: {},
supportedApiTypes: [],
};
Loading
Loading