diff --git a/portals/publisher/src/main/webapp/site/public/locales/en.json b/portals/publisher/src/main/webapp/site/public/locales/en.json
index 94264b40d43..c926c3c81ef 100644
--- a/portals/publisher/src/main/webapp/site/public/locales/en.json
+++ b/portals/publisher/src/main/webapp/site/public/locales/en.json
@@ -80,6 +80,7 @@
"Apis.Create.AsyncAPI.Steps.ProvideAsyncAPI.solace.api.label": "Identified as Solace Event Portal API",
"Apis.Create.AsyncAPI.Steps.ProvideAsyncAPI.solace.label": "Solace Event API",
"Apis.Create.AsyncAPI.Steps.ProvideAsyncAPI.url.label": "AsyncAPI URL",
+ "Apis.Create.AsyncAPI.Steps.ProvideAsyncAPI.url.validation.error": "Failed to validate the AsyncAPI URL. Please try again.",
"Apis.Create.Components.DefaultAPIForm.api.actual.context.helper": "API will be exposed in {actualContext} context at the gateway",
"Apis.Create.Components.DefaultAPIForm.api.channel": "Channel",
"Apis.Create.Components.DefaultAPIForm.api.context": "Context",
@@ -91,6 +92,7 @@
"Apis.Create.Components.DefaultAPIForm.display.name.helper.text.api": "Display name for the API",
"Apis.Create.Components.DefaultAPIForm.display.name.helper.text.mcp": "Display name for the MCP Server",
"Apis.Create.Components.DefaultAPIForm.display.name.helper.text.product": "Display name for the API Product",
+ "Apis.Create.Components.DefaultAPIForm.endpoint.validation.error": "Error while validating endpoint URL",
"Apis.Create.Components.DefaultAPIForm.mandatory.fields": "Mandatory fields",
"Apis.Create.Components.DefaultAPIForm.name": "Name",
"Apis.Create.Components.DefaultAPIForm.select.gateway.type": "Gateway type",
@@ -170,6 +172,7 @@
"Apis.Create.OpenAPI.Steps.ProvideOpenAPI.Input.file.dropzone": "Drag & Drop Open API File/Archive here {break} or {break} Browse files",
"Apis.Create.OpenAPI.Steps.ProvideOpenAPI.Input.file.upload": "Browse File to Upload",
"Apis.Create.OpenAPI.Steps.ProvideOpenAPI.Input.type": "Input Type",
+ "Apis.Create.OpenAPI.Steps.ProvideOpenAPI.url.validation.error": "Failed to validate the OpenAPI URL. Please try again.",
"Apis.Create.OpenAPI.Steps.ValidationResults.linter.results": "Linter Results",
"Apis.Create.OpenAPI.Steps.ValidationResults.linting": "Generating Linter Results",
"Apis.Create.OpenAPI.Steps.ValidationResults.validating": "Validating API definition",
@@ -177,6 +180,7 @@
"Apis.Create.OpenAPI.create.api.form.file.label": "OpenAPI File/Archive",
"Apis.Create.OpenAPI.create.api.form.url.label": "OpenAPI URL",
"Apis.Create.OpenAPI.create.api.openapi.content.validation.failed": "OpenAPI content validation failed!",
+ "Apis.Create.OpenAPI.create.api.openapi.validation.error": "Error while validating OpenAPI definition",
"Apis.Create.OpenAPI.create.api.url.helper.text": "Click away to validate the URL",
"Apis.Create.OpenAPI.create.api.url.label": "OpenAPI URL",
"Apis.Create.OpenAPI.create.api.url.placeholder": "Enter OpenAPI URL",
@@ -210,6 +214,7 @@
"Apis.Create.WSDL.url.label": "WSDL URL",
"Apis.Create.WSDL.url.placeholder": "Enter WSDL URL",
"Apis.Create.WSDL.validation.error.response": "Error occurred during validation",
+ "Apis.Create.WSDL.validation.error.title": "Error while validating the WSDL definition",
"Apis.Create.asyncAPI.Components.SelectPolicies.business.plans": "Protocol",
"Apis.Create.streaming.Components.SelectPolicies.business.plans": "Protocol",
"Apis.Create.streaming.Components.create.and.publish.btn": "Create & Publish",
@@ -1008,6 +1013,7 @@
"Apis.Details.Endpoints.GenericEndpoint.check.endpoint": "Check endpoint status",
"Apis.Details.Endpoints.GenericEndpoint.config.endpoint": "Endpoint configurations",
"Apis.Details.Endpoints.GenericEndpoint.config.service.placeholder": "Select a service from the service list",
+ "Apis.Details.Endpoints.GenericEndpoint.endpoint.validation.error": "Error while validating endpoint URL",
"Apis.Details.Endpoints.GenericEndpoint.no.ep.error": "Endpoint URL should not be empty",
"Apis.Details.Endpoints.GenericEndpoint.security.endpoint": "Endpoint security",
"Apis.Details.Endpoints.GenericEndpoint.service.url.input": "Service URL",
@@ -2261,6 +2267,7 @@
"MCPServers.Create.MCPServerCreateProxy.url.required": "MCP Server URL is required",
"MCPServers.Create.MCPServerCreateProxy.validate": "Next",
"MCPServers.Create.MCPServerCreateProxy.validation.error": "Error while validating MCP Server",
+ "MCPServers.Create.MCPServerCreateProxy.validation.error.detail": "Failed to validate MCP Server URL.",
"MCPServers.Create.MCPServerCreateProxy.validation.failed": "MCP Server validation failed. Please check the URL and security credentials (if any) and try again.",
"MCPServers.Create.MCPServerCreateProxy.value.label": "Value",
"MCPServers.Create.MCPServerCreateProxy.wizard.one": "Provide MCP Server URL",
diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AsyncAPI/Steps/ProvideAsyncAPI.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AsyncAPI/Steps/ProvideAsyncAPI.jsx
index 13a1ff51315..ad1599d3acd 100644
--- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AsyncAPI/Steps/ProvideAsyncAPI.jsx
+++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/AsyncAPI/Steps/ProvideAsyncAPI.jsx
@@ -226,7 +226,14 @@ export default function ProvideAsyncAPI(props) {
onValidate(isValidURL);
setIsValidating(false);
}).catch((error) => {
- setValidity({ url: { message: error.message } });
+ setValidity({ ...isValid, url: { message: error.response?.body?.description
+ || error.response?.body?.message
+ || error.message
+ || intl.formatMessage({
+ id: 'Apis.Create.AsyncAPI.Steps.ProvideAsyncAPI.url.validation.error',
+ defaultMessage: 'Failed to validate the AsyncAPI URL. Please try again.',
+ }),
+ } });
onValidate(false);
setIsValidating(false);
console.error(error);
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 535463e5195..744ee6e043e 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
@@ -24,7 +24,7 @@ import { InputAdornment, IconButton, Icon, Select, MenuItem, InputLabel, Box } f
import CircularProgress from '@mui/material/CircularProgress';
import Chip from '@mui/material/Chip';
import Typography from '@mui/material/Typography';
-import { FormattedMessage } from 'react-intl';
+import { FormattedMessage, useIntl } from 'react-intl';
import APIValidation from 'AppData/APIValidation';
import FormControl from '@mui/material/FormControl';
import FormHelperText from '@mui/material/FormHelperText';
@@ -142,6 +142,7 @@ export default function DefaultAPIForm(props) {
const [isUpdating, setUpdating] = useState(false);
const [isErrorCode, setIsErrorCode] = useState(false);
const iff = (condition, then, otherwise) => (condition ? then : otherwise);
+ const intl = useIntl();
/**
* Generate gradient background based on gateway name
@@ -408,7 +409,20 @@ export default function DefaultAPIForm(props) {
setIsEndpointValid(false);
}
}
- }).finally(() => {
+ })
+ .catch((error) => {
+ const message = error.response?.body?.description
+ || error.response?.body?.message
+ || error.message
+ || intl.formatMessage({
+ id: 'Apis.Create.Components.DefaultAPIForm.endpoint.validation.error',
+ defaultMessage: 'Error while validating endpoint URL',
+ });
+ setStatusCode(message);
+ setIsErrorCode(true);
+ setIsEndpointValid(false);
+ })
+ .finally(() => {
setUpdating(false);
});
}
diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/OpenAPI/Steps/ValidationResults.tsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/ValidationResults.tsx
similarity index 98%
rename from portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/OpenAPI/Steps/ValidationResults.tsx
rename to portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/ValidationResults.tsx
index fc51968259a..804aadbb9f6 100644
--- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/OpenAPI/Steps/ValidationResults.tsx
+++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/ValidationResults.tsx
@@ -23,8 +23,8 @@ import { ExpandMore } from '@mui/icons-material';
import { FormattedMessage } from "react-intl";
import LinterUI from "AppComponents/Apis/Details/APIDefinition/LinterUI/LinterUI";
import APILintingSummary from "AppComponents/Apis/Details/APIDefinition/Linting/APILintingSummary";
-import {
- spectralSeverityMap as severityMap } from "../../../Details/APIDefinition/Linting/Linting"
+import {
+ spectralSeverityMap as severityMap } from "AppComponents/Apis/Details/APIDefinition/Linting/Linting"
type APILintingProps = {
inputValue: any,
diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/validationErrorUtils.test.ts b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/validationErrorUtils.test.ts
new file mode 100644
index 00000000000..280c2dfe8b7
--- /dev/null
+++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/validationErrorUtils.test.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import getValidationErrorsFromError from './validationErrorUtils';
+
+const TITLE = 'Error while validating OpenAPI definition';
+
+describe('getValidationErrorsFromError', () => {
+ it('surfaces the backend description from a rejected validation (e.g. an access-control block, HTTP 400 "not trusted")', () => {
+ const notTrusted = 'The provided URL is not trusted. Please contact the system administrator.';
+ const error = {
+ response: { body: { code: 400, message: 'Bad Request', description: notTrusted } },
+ };
+
+ expect(getValidationErrorsFromError(error, TITLE)).toEqual([
+ { message: TITLE, description: notTrusted },
+ ]);
+ });
+
+ it('returns [] without a backend description (network/other errors keep the generic fallback)', () => {
+ const noDescriptionCases = [
+ new Error('Network Error'),
+ { response: { body: {} } },
+ { response: {} },
+ undefined,
+ ];
+ noDescriptionCases.forEach((error) => {
+ expect(getValidationErrorsFromError(error, TITLE)).toEqual([]);
+ });
+ });
+});
diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/validationErrorUtils.ts b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/validationErrorUtils.ts
new file mode 100644
index 00000000000..153d80b084b
--- /dev/null
+++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Components/validationErrorUtils.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com) All Rights Reserved.
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Build "Validation Errors" card entries from a rejected OpenAPI validation request.
+ *
+ * When validate-openapi returns 200 with `isValid:false`, its `errors` array is shown in the
+ * card directly. But when it *rejects* with an HTTP error (e.g. a network access-control block —
+ * 400 UNTRUSTED_URL, "The provided URL is not trusted."), the reason lives in
+ * `error.response.body.description` and was previously dropped to the console, leaving the
+ * card empty and the user with only a generic "validation failed" message.
+ *
+ * This surfaces that backend description as a card entry so a rejected validation is shown as
+ * prominently as the `isValid:false` path. Errors without a backend description (network
+ * failures, etc.) return `[]`, so callers keep their existing generic-message fallback.
+ *
+ * @param {any} error caught error from a validateOpenAPIByFile / validateOpenAPIByUrl call
+ * @param {string} title localized bold title shown above the backend description
+ * @returns {Array<{message: string, description: string}>} card entries (empty when there is
+ * no backend-provided description)
+ */
+export default function getValidationErrorsFromError(
+ error: any,
+ title: string,
+): Array<{ message: string; description: string }> {
+ const description = error?.response?.body?.description;
+ if (!description) {
+ return [];
+ }
+ return [{ message: title, description }];
+}
diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/GraphQL/Steps/ProvideGraphQL.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/GraphQL/Steps/ProvideGraphQL.jsx
index 459c6ce18cc..03f9fc430b9 100644
--- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/GraphQL/Steps/ProvideGraphQL.jsx
+++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/GraphQL/Steps/ProvideGraphQL.jsx
@@ -156,11 +156,14 @@ export default function ProvideGraphQL(props) {
inputsDispatcher({ action: 'graphQLInfo', value: graphQLInfo });
setValidity({ isValidURL, file: null });
} else {
- let errorMessage;
- if (inputType === ProvideGraphQL.INPUT_TYPES.ENDPOINT) {
- errorMessage = 'Error occurred while generating GraphQL schema from endpoint';
- } else if (inputType === ProvideGraphQL.INPUT_TYPES.URL) {
- errorMessage = 'Error occurred while retrieving GraphQL schema from url';
+ let errorMessage = response.body?.description
+ || response.body?.message || response.body?.errorMessage;
+ if (!errorMessage) {
+ if (inputType === ProvideGraphQL.INPUT_TYPES.ENDPOINT) {
+ errorMessage = 'Error occurred while generating GraphQL schema from endpoint';
+ } else if (inputType === ProvideGraphQL.INPUT_TYPES.URL) {
+ errorMessage = 'Error occurred while retrieving GraphQL schema from url';
+ }
}
setValidity({
isValidURL,
@@ -172,7 +175,10 @@ export default function ProvideGraphQL(props) {
};
const handleError = (error) => {
- setValidity({ url: { message: error.message } });
+ setValidity({ url: { message: error.response?.body?.description
+ || error.response?.body?.message
+ || error.message,
+ }});
onValidate(false);
setIsValidating(false);
console.error(error);
diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/OpenAPI/Steps/ProvideOpenAPI.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/OpenAPI/Steps/ProvideOpenAPI.jsx
index c8075399fc5..cd935320fcf 100644
--- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/OpenAPI/Steps/ProvideOpenAPI.jsx
+++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/OpenAPI/Steps/ProvideOpenAPI.jsx
@@ -48,9 +48,10 @@ import API from 'AppData/api';
import MCPServer from 'AppData/MCPServer';
import DropZoneLocal, { humanFileSize } from 'AppComponents/Shared/DropZoneLocal';
import Utils from 'AppData/Utils';
-import {
+import getValidationErrorsFromError from 'AppComponents/Apis/Create/Components/validationErrorUtils';
+import ValidationResults from 'AppComponents/Apis/Create/Components/ValidationResults';
+import {
getLinterResultsFromContent } from "../../../Details/APIDefinition/Linting/Linting";
-import ValidationResults from './ValidationResults';
const PREFIX = 'ProvideOpenAPI';
@@ -90,6 +91,11 @@ export default function ProvideOpenAPI(props) {
const intl = useIntl();
+ const openApiValidationErrorTitle = intl.formatMessage({
+ id: 'Apis.Create.OpenAPI.create.api.openapi.validation.error',
+ defaultMessage: 'Error while validating OpenAPI definition',
+ });
+
function lint(content) {
// Validate and linting
setIsLinting(true);
@@ -124,6 +130,21 @@ export default function ProvideOpenAPI(props) {
const validateURLDebounced = useCallback(
debounce((newURL) => { // Example: https://codesandbox.io/s/debounce-example-l7fq3?file=/src/App.js
+ const handleValidationError = (error) => {
+ const errorMessage = error.response?.body?.description
+ || error.response?.body?.message
+ || error.message
+ || intl.formatMessage({
+ id: 'Apis.Create.OpenAPI.Steps.ProvideOpenAPI.url.validation.error',
+ defaultMessage: 'Failed to validate the OpenAPI URL. Please try again.',
+ });
+ setValidity({ ...isValid, url: { message: errorMessage } });
+ setValidationErrors(getValidationErrorsFromError(error, openApiValidationErrorTitle));
+ onValidate(false);
+ setIsValidating(false);
+ console.error(error);
+ };
+
if (isMCPServer) {
MCPServer.validateOpenAPIByUrl(newURL, { returnContent: true }).then((response) => {
const {
@@ -162,12 +183,7 @@ export default function ProvideOpenAPI(props) {
}
onValidate(isValidURL);
setIsValidating(false);
- }).catch((error) => {
- setValidity({ url: { message: error.message } });
- onValidate(false);
- setIsValidating(false);
- console.error(error);
- });
+ }).catch(handleValidationError);
} else {
API.validateOpenAPIByUrl(newURL, { returnContent: true }).then((response) => {
const {
@@ -197,13 +213,7 @@ export default function ProvideOpenAPI(props) {
}
onValidate(isValidURL);
setIsValidating(false);
- }).catch((error) => {
- setValidity({ url: { message: error.message } });
- onValidate(false);
- setIsValidating(false);
- console.error(error);
-
- });
+ }).catch(handleValidationError);
}
}, 750),
[],
@@ -231,6 +241,7 @@ export default function ProvideOpenAPI(props) {
validFile = file;
inputsDispatcher({ action: 'preSetAPI', value: info });
setValidity({ ...isValid, file: null });
+ setValidationErrors([]);
} else {
setValidity({
...isValid, file: {
@@ -252,6 +263,7 @@ export default function ProvideOpenAPI(props) {
})
}
});
+ setValidationErrors(getValidationErrorsFromError(error, openApiValidationErrorTitle));
console.error(error);
})
.finally(() => {
@@ -270,6 +282,7 @@ export default function ProvideOpenAPI(props) {
validFile = file;
inputsDispatcher({ action: 'preSetAPI', value: info });
setValidity({ ...isValid, file: null });
+ setValidationErrors([]);
} else {
setValidity({
...isValid, file: {
@@ -291,6 +304,7 @@ export default function ProvideOpenAPI(props) {
})
}
});
+ setValidationErrors(getValidationErrorsFromError(error, openApiValidationErrorTitle));
console.error(error);
})
.finally(() => {
diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/WSDL/Steps/ProvideWSDL.jsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/WSDL/Steps/ProvideWSDL.jsx
index dbc62aa6fa1..66a81705b72 100644
--- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/WSDL/Steps/ProvideWSDL.jsx
+++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/WSDL/Steps/ProvideWSDL.jsx
@@ -15,7 +15,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
import { styled } from '@mui/material/styles';
import PropTypes from 'prop-types';
import Radio from '@mui/material/Radio';
@@ -43,8 +43,9 @@ import CheckIcon from '@mui/icons-material/Check';
import APIValidation from 'AppData/APIValidation';
import Wsdl from 'AppData/Wsdl';
-import Banner from 'AppComponents/Shared/Banner';
import DropZoneLocal, { humanFileSize } from 'AppComponents/Shared/DropZoneLocal';
+import getValidationErrorsFromError from 'AppComponents/Apis/Create/Components/validationErrorUtils';
+import ValidationResults from 'AppComponents/Apis/Create/Components/ValidationResults';
const PREFIX = 'ProvideWSDL';
@@ -77,9 +78,31 @@ export default function ProvideWSDL(props) {
const [isError, setValidity] = useState(); // If valid value is `null` else an error object will be there
const [isValidating, setIsValidating] = useState(false);
+ const [validationErrors, setValidationErrors] = useState([]);
const isCreateMode = apiInputs.mode === 'create';
const intl = useIntl();
+
+ const wsdlValidationErrorTitle = intl.formatMessage({
+ id: 'Apis.Create.WSDL.validation.error.title',
+ defaultMessage: 'Error while validating the WSDL definition',
+ });
+
+ /**
+ * Clear the validation state and the selected file/URL, bringing back the drop zone.
+ */
+ function reset() {
+ setValidationErrors([]);
+ setValidity();
+ inputsDispatcher({ action: 'inputValue', value: null });
+ inputsDispatcher({ action: 'isFormValid', value: false });
+ onValidate(false);
+ }
+
+ // Clear validation state when the input type changes so a stale error does not linger.
+ useEffect(() => {
+ reset();
+ }, [apiInputs.inputType]);
/**
* Handles WSDL validation response and returns the state.
*
@@ -91,30 +114,34 @@ export default function ProvideWSDL(props) {
const isWSDLValid = response.body.isValid;
let success = false;
if (isWSDLValid) {
+ setValidationErrors([]);
if (type === 'file') {
setValidity({ ...isError, file: null });
} else {
setValidity({ ...isError, url: null });
}
success = true;
- } else if (type === 'file') {
- setValidity({
- ...isError, file: {
- message: intl.formatMessage({
- id: 'Apis.Create.WSDL.content.validation.file.failed',
- defaultMessage: 'WSDL content validation failed!',
- }),
- }
- });
} else {
- setValidity({
- ...isError, url: {
- message: intl.formatMessage({
- id: 'Apis.Create.WSDL.content.validation.url.failed',
- defaultMessage: 'Invalid WSDL URL!',
- }),
- }
- });
+ setValidationErrors(response.body.errors || []);
+ if (type === 'file') {
+ setValidity({
+ ...isError, file: {
+ message: intl.formatMessage({
+ id: 'Apis.Create.WSDL.content.validation.file.failed',
+ defaultMessage: 'WSDL content validation failed!',
+ }),
+ }
+ });
+ } else {
+ setValidity({
+ ...isError, url: {
+ message: intl.formatMessage({
+ id: 'Apis.Create.WSDL.content.validation.url.failed',
+ defaultMessage: 'Invalid WSDL URL!',
+ }),
+ }
+ });
+ }
}
onValidate(isWSDLValid);
setIsValidating(false);
@@ -132,14 +159,16 @@ export default function ProvideWSDL(props) {
id: 'Apis.Create.WSDL.validation.error.response',
defaultMessage: 'Error occurred during validation',
});
- if (error.response && error.response.body.description) {
+ if (error.response?.body?.description) {
message = error.response.body.description;
}
+ setValidationErrors(getValidationErrorsFromError(error, wsdlValidationErrorTitle));
if (type === 'file') {
setValidity({ ...isError, file: { message } });
} else {
setValidity({ ...isError, url: { message } });
}
+ onValidate(false);
setIsValidating(false);
}
@@ -172,11 +201,12 @@ export default function ProvideWSDL(props) {
if (state === null) {
setIsValidating(true);
Wsdl.validateFileOrArchive(file).then((response) => {
- if (handleWSDLValidationResponse(response, 'file')) {
- inputsDispatcher({ action: 'inputValue', value: file });
- }
+ handleWSDLValidationResponse(response, 'file');
}).catch((error) => {
handleWSDLValidationErrorResponse(error, 'file');
+ }).finally(() => {
+ // Set the file as input value even when invalid so the uploaded-file row replaces the drop zone.
+ inputsDispatcher({ action: 'inputValue', value: file });
});
} else {
setValidity({ ...isError, file: state });
@@ -214,10 +244,7 @@ export default function ProvideWSDL(props) {
{
- inputsDispatcher({ action: 'inputValue', value: null });
- inputsDispatcher({ action: 'isFormValid', value: false });
- }}
+ onClick={reset}
size='large'>
@@ -299,10 +326,10 @@ export default function ProvideWSDL(props) {
return (
<>
-
+
{isCreateMode
&& (
-
+
<>
@@ -320,9 +347,9 @@ export default function ProvideWSDL(props) {
onChange={
(event) => {
inputsDispatcher({ action: 'type', value: event.target.value });
- inputsDispatcher({ action: 'isFormValid', value: false });
- inputsDispatcher({ action: 'inputValue', value: null });
inputsDispatcher({ action: 'inputType', value: 'url' });
+ // Clear validation state so a stale error does not linger on type change.
+ reset();
}
}
>
@@ -350,7 +377,7 @@ export default function ProvideWSDL(props) {
)}
-
+
<>
@@ -390,20 +417,7 @@ export default function ProvideWSDL(props) {
- {isError && isError.file
- && (
-
- setValidity({ file: null })}
- disableActions
- dense
- paperProps={{ elevation: 1 }}
- type='error'
- message={isError.file.message}
- />
-
- )}
-
+
{isFileInput ? renderFileUpload()
: (
+ {}}
+ />
>
);
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 367620acf54..bf5d845c1eb 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
@@ -24,7 +24,7 @@ import {
InputAdornment,
TextField,
} from '@mui/material';
-import { FormattedMessage } from 'react-intl';
+import { FormattedMessage, useIntl } from 'react-intl';
import Tooltip from '@mui/material/Tooltip';
import CircularProgress from '@mui/material/CircularProgress';
import Chip from '@mui/material/Chip';
@@ -126,6 +126,7 @@ function GenericEndpoint(props) {
const [isUpdating, setUpdating] = useState(false);
const [isErrorCode, setIsErrorCode] = useState(false);
const iff = (condition, then, otherwise) => (condition ? then : otherwise);
+ const intl = useIntl();
useEffect(() => {
setServiceUrl(endpointURL);
@@ -149,6 +150,18 @@ function GenericEndpoint(props) {
} else {
setIsEndpointValid(false);
}
+ }).catch((error) => {
+ const message = error.response?.body?.description
+ || error.response?.body?.message
+ || error.message
+ || intl.formatMessage({
+ id: 'Apis.Details.Endpoints.GenericEndpoint.endpoint.validation.error',
+ defaultMessage: 'Error while validating endpoint URL',
+ });
+ setStatusCode(message);
+ setIsErrorCode(true);
+ setIsEndpointValid(false);
+ console.error(error);
}).finally(() => {
setUpdating(false);
});
diff --git a/portals/publisher/src/main/webapp/source/src/app/components/MCPServers/Create/MCPServerCreateProxy.jsx b/portals/publisher/src/main/webapp/source/src/app/components/MCPServers/Create/MCPServerCreateProxy.jsx
index 13ef4377d19..5cb1f752879 100644
--- a/portals/publisher/src/main/webapp/source/src/app/components/MCPServers/Create/MCPServerCreateProxy.jsx
+++ b/portals/publisher/src/main/webapp/source/src/app/components/MCPServers/Create/MCPServerCreateProxy.jsx
@@ -231,7 +231,16 @@ const MCPServerCreateProxy = (props) => {
})
.catch((error) => {
if (error.response && error.response.body) {
- setValidationError('Failed to validate MCP Server URL');
+ const errorMessage =
+ error.response?.body?.description
+ || error.response?.body?.message
+ || error.message
+ || intl.formatMessage({
+ id: 'MCPServers.Create.MCPServerCreateProxy.validation.error.detail',
+ defaultMessage: 'Failed to validate MCP Server URL.',
+ });
+
+ setValidationError(errorMessage);
} else {
const errorMsg = intl.formatMessage({
id: 'MCPServers.Create.MCPServerCreateProxy.validation.error',