Skip to content
Open
7 changes: 7 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 @@ -591,6 +591,13 @@
"GatewayEnvironments.AddEditVhost.httpsPort": "HTTPS Port",
"GatewayEnvironments.AddEditVhost.wsPort": "WS Port",
"GatewayEnvironments.AddEditVhost.wssPort": "WSS Port",
"GatewayEnvironments.GatewayConfiguration.advancedSettings": "Advanced Settings",
"GatewayEnvironments.PlanMapping.apiType.ai": "AI APIs",
"GatewayEnvironments.PlanMapping.apiType.async": "Async APIs",
"GatewayEnvironments.PlanMapping.apiType.other": "Other APIs",
"GatewayEnvironments.PlanMapping.apiType.rest": "REST APIs",
"GatewayEnvironments.PlanMapping.helper": "Map each local subscription plan to the corresponding gateway plan identifier.",
"GatewayEnvironments.PlanMapping.noCompatibleLocalPlans": "No local subscription plans match the supported API types of this gateway.",
"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,8 @@
} from './PlatformGatewayUtils';

const PREFIX = 'AddEditGWEnvironment';
const PLAN_MAPPING_PROPERTY_PREFIX = 'plan_mapping.';
const FEDERATED_GATEWAY_VALIDATION_ERROR_CODE = 900520;

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

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

Check warning on line 373 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=AZ3xiGXE5yp55qrRGhAS&open=AZ3xiGXE5yp55qrRGhAS&pullRequest=1338
|| !Array.isArray(responseBody.error)) {
return rowErrors;
}

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

Check warning on line 380 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=AZ3xiGXE5yp55qrRGhAT&open=AZ3xiGXE5yp55qrRGhAT&pullRequest=1338
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 @@ -414,6 +433,7 @@
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 Down Expand Up @@ -881,6 +905,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 +1220,7 @@

promiseAPICall
.then((result) => {
setPlanMappingErrors({});
if (id) {
Alert.success(
`${name} ${intl.formatMessage({
Expand Down Expand Up @@ -1226,8 +1258,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 1274 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=AZ3xyMOB-I4oIhIK6zeb&open=AZ3xyMOB-I4oIhIK6zeb&pullRequest=1338

Check warning on line 1274 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=AZ3xyMOB-I4oIhIK6zec&open=AZ3xyMOB-I4oIhIK6zec&pullRequest=1338
: '';
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 @@ -2383,6 +2435,9 @@
validating={
validating
}
planMappingErrors={
planMappingErrors
}
gatewayId={cloneDeep(
id,
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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_PROPERTY_PREFIX = 'plan_mapping.';

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

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=AZ3xiGTF5yp55qrRGhAQ&open=AZ3xiGTF5yp55qrRGhAQ&pullRequest=1338

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=AZ3xiGTF5yp55qrRGhAP&open=AZ3xiGTF5yp55qrRGhAP&pullRequest=1338

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=AZ3xiGTF5yp55qrRGhAR&open=AZ3xiGTF5yp55qrRGhAR&pullRequest=1338
} = props;

const getAllNestedGatewayConfigPropertyNames = (connectorConfigurations, parentKey = '') => {
Expand Down Expand Up @@ -167,7 +173,10 @@

// Clear any properties in additionalProperties that are not in the current valid set
Object.keys(additionalProperties).forEach((propName) => {
if (!currentValidProperties.includes(propName)) {
if (
!currentValidProperties.includes(propName)
&& !propName.startsWith(PLAN_MAPPING_PROPERTY_PREFIX)
) {
setAdditionalProperties(propName, undefined);
}
});
Expand Down Expand Up @@ -207,7 +216,16 @@
});
}
}
if (gatewayConfiguration.type === 'input') {
if (gatewayConfiguration.type === 'mapping') {
return (
<GatewayPlanMapping
gatewayConfiguration={gatewayConfiguration}
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 +351,25 @@
});
};

const regularConfigurations = gatewayConfigurations.filter((config) => config.type !== 'mapping');

Check warning on line 354 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=AZ3S2IODMYklAafZIQbT&open=AZ3S2IODMYklAafZIQbT&pullRequest=1338
const mappingConfigurations = gatewayConfigurations.filter((config) => config.type === 'mapping');

Check warning on line 355 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=AZ3S2IODMYklAafZIQbU&open=AZ3S2IODMYklAafZIQbU&pullRequest=1338

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 +384,5 @@
/>,
hasErrors: () => {},
validating: false,
planMappingErrors: {},
};
Loading
Loading