diff --git a/portals/admin/src/main/webapp/site/public/locales/en.json b/portals/admin/src/main/webapp/site/public/locales/en.json index 8fbe43ca97f..aa912039493 100644 --- a/portals/admin/src/main/webapp/site/public/locales/en.json +++ b/portals/admin/src/main/webapp/site/public/locales/en.json @@ -62,6 +62,10 @@ "AdminPages.ApiCategories.table.header.category.description": "Description", "AdminPages.ApiCategories.table.header.category.name": "Category Name", "AdminPages.ApiCategories.table.header.category.number.of.apis": "Number of APIs", + "AdminPages.ApiSettings.EditApi.form.edit.error": "API provider should not be empty.", + "AdminPages.ApiSettings.EditApi.form.edit.other.error": "Given Username is not valid.", + "AdminPages.ApiSettings.EditApi.form.edit.successful": "API provider changed successfully", + "AdminPages.ApiSettings.EditApi.form.edit.user.notvalid": "Error while updating the provider name.", "AdminPages.ApplicationSettings.Edit.form.edit.successful": "Application owner changed successfully", "AdminPages.ApplicationSettings.Edit.form.helperText": "Enter a new Owner. Make sure the new owner has logged into the Developer Portal at least once", "AdminPages.ApplicationSettings.Edit.form.name": "Application Name", @@ -84,6 +88,15 @@ "AdminPagesGatewayEnvironments.AddEditGWEnvironment.form.environment.vhost.empty": "VHost is empty", "Apis.Details.Scopes.CreateScope.roles.help": "Enter a valid role and press `Enter`.", "Apis.Details.Scopes.Roles.Invalid": "A Role is invalid", + "Apis.Listing.Listing.apis.search": "Search", + "Apis.Listing.Listing.apis.search.label": "Search by API", + "Apis.Listing.Listing.apis.searching": "Searching", + "Apis.Listing.Listing.clear.search": "Clear Search", + "Apis.Listing.Listing.empty.message": "No Data to Display", + "Apis.Listing.Listing.search.placeholder": "API Name", + "Apis.Listing.apiTableHead.name": "Name", + "Apis.Listing.apiTableHead.provider": "Provider", + "Apis.Listing.apiTableHead.version": "Version", "Apis.Shared.AdminRootErrorBoundary.refresh": "Refresh", "Apis.Shared.AdminRootErrorBoundary.refresh.or.try.again.message": "You may refresh the page now or try again later", "Apis.Shared.AdminRootErrorBoundary.something.went.wrong.while.rendering.button": "Something went wrong while rendering the", @@ -115,10 +128,11 @@ "Base.RouteMenuMapping.advanced.throttling.policies.Adding": "Add Advanced Policy", "Base.RouteMenuMapping.advanced.throttling.policies.Editing": "Edit Advanced Policy", "Base.RouteMenuMapping.api.categories": "API Categories", + "Base.RouteMenuMapping.apis": "Change API Provider", "Base.RouteMenuMapping.application.creation": "Application Creation", "Base.RouteMenuMapping.application.deletion": "Application Deletion", "Base.RouteMenuMapping.application.throttling.policies": "Application Policies", - "Base.RouteMenuMapping.applications": "Applications", + "Base.RouteMenuMapping.applications": "Change Application Owner", "Base.RouteMenuMapping.blacklisted.items": "Deny Policies", "Base.RouteMenuMapping.custom.throttling.policies": "Custom Policies", "Base.RouteMenuMapping.custom.throttling.policies.items.Adding": "Add Custom Policy", diff --git a/portals/admin/src/main/webapp/site/public/locales/fr.json b/portals/admin/src/main/webapp/site/public/locales/fr.json index dbb9e9608d3..b0911044fb5 100644 --- a/portals/admin/src/main/webapp/site/public/locales/fr.json +++ b/portals/admin/src/main/webapp/site/public/locales/fr.json @@ -62,6 +62,9 @@ "AdminPages.ApiCategories.table.header.category.description": "", "AdminPages.ApiCategories.table.header.category.name": "", "AdminPages.ApiCategories.table.header.category.number.of.apis": "", + "AdminPages.ApiSettings.EditApi.form.edit.successful": "", + "AdminPages.ApiSettings.EditApi.form.helperText": "", + "AdminPages.ApiSettings.EditApi.form.name": "", "AdminPages.ApplicationSettings.Edit.form.edit.successful": "", "AdminPages.ApplicationSettings.Edit.form.helperText": "", "AdminPages.ApplicationSettings.Edit.form.name": "", @@ -84,6 +87,15 @@ "AdminPagesGatewayEnvironments.AddEditGWEnvironment.form.environment.vhost.empty": "", "Apis.Details.Scopes.CreateScope.roles.help": "", "Apis.Details.Scopes.Roles.Invalid": "", + "Apis.Listing.Listing.apis.search": "", + "Apis.Listing.Listing.apis.search.label": "", + "Apis.Listing.Listing.apis.searching": "", + "Apis.Listing.Listing.clear.search": "", + "Apis.Listing.Listing.empty.message": "", + "Apis.Listing.Listing.search.placeholder": "", + "Apis.Listing.apiTableHead.actions": "", + "Apis.Listing.apiTableHead.name": "", + "Apis.Listing.apiTableHead.owner": "", "Apis.Shared.AdminRootErrorBoundary.refresh": "", "Apis.Shared.AdminRootErrorBoundary.refresh.or.try.again.message": "", "Apis.Shared.AdminRootErrorBoundary.something.went.wrong.while.rendering.button": "", @@ -119,6 +131,7 @@ "Base.RouteMenuMapping.application.deletion": "", "Base.RouteMenuMapping.application.throttling.policies": "", "Base.RouteMenuMapping.applications": "", + "Base.RouteMenuMapping.apps": "", "Base.RouteMenuMapping.blacklisted.items": "", "Base.RouteMenuMapping.custom.throttling.policies": "", "Base.RouteMenuMapping.custom.throttling.policies.items.Adding": "", @@ -347,6 +360,7 @@ "Manage.Alerts.unsubscribe.success.msg": "", "RolePermissions.Common.AddRoleWizard.add.mapping.button": "", "RolePermissions.Common.AddRoleWizard.add.mapping.title": "", + "RolePermissions.Common.AddRoleWizard.add.role.warn.empty": "", "RolePermissions.Common.AddRoleWizard.add.scope.error": "", "RolePermissions.Common.DeletePermission.delete.scope.error": "", "RolePermissions.ListRoles.error.retrieving.perm": "", diff --git a/portals/admin/src/main/webapp/source/src/app/components/APISettings/ApisTableContent.jsx b/portals/admin/src/main/webapp/source/src/app/components/APISettings/ApisTableContent.jsx new file mode 100644 index 00000000000..9297233a146 --- /dev/null +++ b/portals/admin/src/main/webapp/source/src/app/components/APISettings/ApisTableContent.jsx @@ -0,0 +1,242 @@ +/* +* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) 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 React, { useState } from 'react'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableRow from '@material-ui/core/TableRow'; +import PropTypes from 'prop-types'; +import API from 'AppData/api'; +import { makeStyles, withStyles } from '@material-ui/core/styles'; +import Alert from 'AppComponents/Shared/Alert'; +import TextField from '@material-ui/core/TextField'; +import EditIcon from '@material-ui/icons/Edit'; +import { FormattedMessage } from 'react-intl'; +import { IconButton } from '@material-ui/core'; +import SaveIcon from '@material-ui/icons/Save'; +import CancelIcon from '@material-ui/icons/Cancel'; + +const useStyles = makeStyles((theme) => ({ + fullHeight: { + height: '100%', + }, + tableRow: { + height: theme.spacing(5), + '& td': { + padding: theme.spacing(0.5), + }, + }, + appOwner: { + pointerEvents: 'none', + }, + appName: { + '& a': { + color: '#1b9ec7 !important', + }, + }, + button: { + marginLeft: theme.spacing(0.5), + marginRight: theme.spacing(0.5), + marginTop: theme.spacing(1), + }, + appTablePaper: { + '& table tr td': { + paddingLeft: theme.spacing(1), + }, + '& table tr td:first-child, & table tr th:first-child': { + paddingLeft: theme.spacing(2), + }, + '& table tr td button.Mui-disabled span.material-icons': { + color: theme.palette.action.disabled, + }, + }, + tableCellWrapper: { + '& td': { + 'word-break': 'break-all', + 'white-space': 'normal', + }, + }, + tableActionBtnContainer: { + display: 'flex', + flexDirection: 'row', + alignItems: 'left', + justifyContent: 'left', + marginLeft: 10, + }, + textfield: { + maxHeight: '10px', + marginTop: '-5px', + maxWidth: '120px', + }, + tableCell: { + marginLeft: 10, + }, +})); + +const StyledTableCell = withStyles((theme) => ({ + head: { + backgroundColor: theme.palette.common.black, + color: theme.palette.common.white, + }, + body: { + fontSize: 14, + }, + root: { + '&:first-child': { + paddingLeft: theme.spacing(2), + }, + }, +}))(TableCell); + +const StyledTableRow = withStyles((theme) => ({ + root: { + '&:nth-of-type(odd)': { + backgroundColor: theme.palette.background.default, + }, + }, +}))(TableRow); + +const ApisTableContent = ({ apis, updateApiList }) => { + const restApi = new API(); + const [provider, setProvider] = useState(''); + const [editableRows, setEditableRows] = useState(new Set()); + + const classes = useStyles(); + + const handleEditClick = (apiId) => { + setEditableRows((prevRows) => { + const newRows = new Set(prevRows); + newRows.add(apiId); + return newRows; + }); + }; + + const handleCancelClick = (apiId) => { + setEditableRows((prevRows) => { + const newRows = new Set(prevRows); + newRows.delete(apiId); + return newRows; + }); + setProvider(''); + }; + + const handleSubmitClick = (apiId, apiProvider) => { + if (apiProvider === '') { + return ( + Alert.error( + , + ) + ); + } else { + return restApi.updateApiProvider(apiId, apiProvider) + .then(() => { + return ( + Alert.success( + , + ) + ); + }) + .catch((error) => { + const { response } = error; + // This api returns 404 when the $provider is not found. + // error codes: 901502, 901500 for user not found and scope not found + if (response?.body?.code === 901502 || response?.body?.code === 901500) { + return ( + Alert.error( + , + ) + ); + } else { + return ( + Alert.error( + , + ) + ); + } + }) + .finally(() => { + updateApiList(); + handleCancelClick(apiId); + }); + } + }; + + return ( + + {apis && apis.map((api) => ( + + + {api.name} + + +
+ {api.version} +
+
+ + {!editableRows.has(api.id) && ( +
+ { api.provider } + handleEditClick(api.id)}> + + +
+ )} + { editableRows.has(api.id) && ( +
+ { setProvider(e.target.value); }} + /> + handleSubmitClick(api.id, provider)}> + + + handleCancelClick(api.id)}> + + +
+ )} +
+
+ ))} +
+ ); +}; + +ApisTableContent.propTypes = { + apis: PropTypes.instanceOf(Map).isRequired, +}; + +export default ApisTableContent; diff --git a/portals/admin/src/main/webapp/source/src/app/components/APISettings/ApisTableHead.jsx b/portals/admin/src/main/webapp/source/src/app/components/APISettings/ApisTableHead.jsx new file mode 100644 index 00000000000..b7222e1b3b1 --- /dev/null +++ b/portals/admin/src/main/webapp/source/src/app/components/APISettings/ApisTableHead.jsx @@ -0,0 +1,107 @@ +/* +* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) 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 React from 'react'; +import PropTypes from 'prop-types'; +import TableCell from '@material-ui/core/TableCell'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import TableSortLabel from '@material-ui/core/TableSortLabel'; +import { FormattedMessage } from 'react-intl'; + +/** + * @inheritdoc + * @class apiTableHead + * @extends {Component} + */ +const apisTableHead = (props) => { + const createSortHandler = (property) => (event) => { + props.onRequestSort(event, property); + }; + const columnData = [ + { + id: 'name', + numeric: false, + disablePadding: true, + label: (), + sorting: true, + width: 200, + }, + { + id: 'version', + numeric: false, + disablePadding: true, + label: (), + sorting: true, + width: 100, + }, + { + id: 'provider', + numeric: false, + disablePadding: false, + label: (), + sorting: true, + width: 130, + }, + ]; + const { order, orderBy } = props; + return ( + + + {columnData.map((column) => { + return ( + + {column.sorting ? ( + + {column.label} + + ) : ( + column.label + )} + + ); + })} + + + ); +}; +apisTableHead.propTypes = { + onRequestSort: PropTypes.func.isRequired, + order: PropTypes.string.isRequired, + orderBy: PropTypes.string.isRequired, +}; +export default apisTableHead; diff --git a/portals/admin/src/main/webapp/source/src/app/components/APISettings/ListApis.jsx b/portals/admin/src/main/webapp/source/src/app/components/APISettings/ListApis.jsx new file mode 100644 index 00000000000..517434daf9d --- /dev/null +++ b/portals/admin/src/main/webapp/source/src/app/components/APISettings/ListApis.jsx @@ -0,0 +1,275 @@ +/* +* Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) 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 React, { useEffect, useState } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import API from 'AppData/api'; +import { useIntl, FormattedMessage } from 'react-intl'; +import ApisTableContent from 'AppComponents/APISettings/ApisTableContent'; +import ApisTableHead from 'AppComponents/APISettings/ApisTableHead'; +import Table from '@material-ui/core/Table'; +import ContentBase from 'AppComponents/AdminPages/Addons/ContentBase'; +import TableFooter from '@material-ui/core/TableFooter'; +import TablePagination from '@material-ui/core/TablePagination'; +import TableRow from '@material-ui/core/TableRow'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import Box from '@material-ui/core/Box'; +import Grid from '@material-ui/core/Grid'; +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; +import HighlightOffRoundedIcon from '@material-ui/icons/HighlightOffRounded'; +import Tooltip from '@material-ui/core/Tooltip'; +import TextField from '@material-ui/core/TextField'; +import SearchIcon from '@material-ui/icons/Search'; +import Alert from '@material-ui/lab/Alert'; +import Typography from '@material-ui/core/Typography'; + +/** + * Render a list + * @returns {JSX} Header AppBar components. + */ + +const useStyles = makeStyles((theme) => ({ + searchBar: { + borderBottom: '1px solid rgba(0, 0, 0, 0.12)', + }, + block: { + display: 'block', + }, + clearSearch: { + position: 'absolute', + right: 111, + top: 13, + }, + addUser: { + marginRight: theme.spacing(1), + }, +})); + +export default function ListApis() { + const intl = useIntl(); + const classes = useStyles(); + const [loading, setLoading] = useState(false); + const [apiList, setApiList] = useState(null); + const [totalApps, setTotalApps] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + const [page, setPage] = useState(0); + const [provider, setProvider] = useState(''); + + /** + * API call to get api list + * @returns {Promise}. + */ + function apiCall(pageNo, query = provider) { + setLoading(true); + const restApi = new API(); + return restApi + .getApiList({ limit: rowsPerPage, offset: pageNo * rowsPerPage, query }) + .then((result) => { + setApiList(result.body.apis); + const { pagination: { total } } = result.body; + setTotalApps(total); + return result.body.apis; + }) + .catch((error) => { + throw error; + }) + .finally(() => { + setLoading(false); + }); + } + + useEffect(() => { + apiCall(page).then((result) => { + setApiList(result); + }); + }, [page]); + + useEffect(() => { + apiCall(page).then((result) => { + setApiList(result); + }); + }, [rowsPerPage]); + + function handleChangePage(event, pageNo) { + setPage(pageNo); + apiCall(pageNo).then((result) => { + setApiList(result); + }); + } + + function handleChangeRowsPerPage(event) { + const nextRowsPerPage = event.target.value; + const rowsPerPageRatio = rowsPerPage / nextRowsPerPage; + const nextPage = Math.floor(page * rowsPerPageRatio); + setPage(nextPage); + setRowsPerPage(nextRowsPerPage); + apiCall(page).then((result) => { + setApiList(result); + }); + } + + function clearSearch() { + setPage(0); + setProvider(''); + apiCall(page, '').then((result) => { + setApiList(result); + }); + } + + function setQuery(event) { + const newQuery = event.target.value; + if (newQuery === '') { + clearSearch(); + } else { + setProvider(newQuery); + } + } + + function filterApps(e) { + e.preventDefault(); + setPage(0); + apiCall(page).then((result) => { + setApiList(result); + }); + } + + return ( + + + +
+ + + + + + + { provider.length > 0 + && ( + + + + + + )} + + + + + +
+
+
+ {apiList && apiList.length > 0 + && ( + + + + + + + + +
+ )} + {apiList && apiList.length === 0 && !loading && ( + + + + + + + + )} +
+ ); +} diff --git a/portals/admin/src/main/webapp/source/src/app/components/Base/RouteMenuMapping.jsx b/portals/admin/src/main/webapp/source/src/app/components/Base/RouteMenuMapping.jsx index 5d4d00fceea..e83a5224260 100644 --- a/portals/admin/src/main/webapp/source/src/app/components/Base/RouteMenuMapping.jsx +++ b/portals/admin/src/main/webapp/source/src/app/components/Base/RouteMenuMapping.jsx @@ -56,6 +56,7 @@ import SecurityIcon from '@material-ui/icons/Security'; import TouchAppIcon from '@material-ui/icons/TouchApp'; import VpnKeyIcon from '@material-ui/icons/VpnKey'; import AccountTreeIcon from '@material-ui/icons/AccountTree'; +import ListApis from '../APISettings/ListApis'; const RouteMenuMapping = (intl) => [ { @@ -301,12 +302,21 @@ const RouteMenuMapping = (intl) => [ { id: intl.formatMessage({ id: 'Base.RouteMenuMapping.applications', - defaultMessage: 'Applications', + defaultMessage: 'Change Application Owner', }), path: '/settings/applications', component: ListApplications, icon: , }, + { + id: intl.formatMessage({ + id: 'Base.RouteMenuMapping.apis', + defaultMessage: 'Change API Provider', + }), + path: '/settings/apis', + component: ListApis, + icon: , + }, { id: intl.formatMessage({ id: 'Base.RouteMenuMapping.role.permissions', diff --git a/portals/admin/src/main/webapp/source/src/app/data/Constants.js b/portals/admin/src/main/webapp/source/src/app/data/Constants.js index 79df9500e8e..34c53cd1659 100644 --- a/portals/admin/src/main/webapp/source/src/app/data/Constants.js +++ b/portals/admin/src/main/webapp/source/src/app/data/Constants.js @@ -39,7 +39,7 @@ const CONSTS = { KEY_MANAGER: ['apim:keymanagers_manage', 'openid', 'apim:tenantInfo', 'apim:admin_settings'], GATEWAY_MANAGER: ['apim:environment_manage', 'openid', 'apim:admin_settings', 'apim:environment_read'], SETTINGS_MANAGER: ['apim:app_owner_change', 'apim:admin_application_view', - 'apim:scope_manage', 'openid', 'apim:admin_settings', 'apim:tenantInfo', + 'apim:scope_manage', 'openid', 'apim:admin_settings', 'apim:tenantInfo', 'apim:api_provider_change', ], }, }; diff --git a/portals/admin/src/main/webapp/source/src/app/data/api.js b/portals/admin/src/main/webapp/source/src/app/data/api.js index 305510cbe41..aa552e7c900 100644 --- a/portals/admin/src/main/webapp/source/src/app/data/api.js +++ b/portals/admin/src/main/webapp/source/src/app/data/api.js @@ -420,6 +420,17 @@ class API extends Resource { }); } + /** + * Get a list of apis from all users + */ + getApiList(params) { + return this.client.then((client) => { + return client.apis['APIs'].getAllAPIs( + params, this._requestMetaData(), + ); + }); + } + /** * Get Subscription Throttling Policies */ @@ -445,6 +456,18 @@ class API extends Resource { }); } + /** + * Update an api's owner + */ + updateApiProvider(apiId, provider) { + return this.client.then((client) => { + return client.apis['Api Provider Change'].providerNamePost( + { provider: provider, apiId: apiId }, + this._requestMetaData(), + ); + }); + } + /** * Get a list of available Gateway Environments */ diff --git a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Default/APICreateDefault.test.tsx b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Default/APICreateDefault.test.tsx index fce436be9bd..b1ed04fcfeb 100644 --- a/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Default/APICreateDefault.test.tsx +++ b/portals/publisher/src/main/webapp/source/src/app/components/Apis/Create/Default/APICreateDefault.test.tsx @@ -64,7 +64,7 @@ describe('Create REST API From scratch', () => { userEvent.clear(NAME_INPUT); // Test for special characters - const NO_SPACE_ERROR = /name should not contain spaces or special characters/i; + const NO_SPACE_ERROR = /name should not contain trailing or leading spaces, special characters, and consecutive spaces/i; userEvent.type(NAME_INPUT, 'invalid@name'); NAME_INPUT.blur(); expect(screen.getByText(NO_SPACE_ERROR)).toBeInTheDocument(); diff --git a/portals/publisher/src/main/webapp/source/src/app/data/APIValidation.js b/portals/publisher/src/main/webapp/source/src/app/data/APIValidation.js index 7bef690a934..964d4242914 100644 --- a/portals/publisher/src/main/webapp/source/src/app/data/APIValidation.js +++ b/portals/publisher/src/main/webapp/source/src/app/data/APIValidation.js @@ -27,12 +27,16 @@ import queryString from 'query-string'; * @param {string} errorType The joi error type * @return {string} simplified error message. * */ -function getMessage(errorType, maxLength) { +function getMessage(errorType, isApiName, maxLength) { switch (errorType) { case 'any.empty': return 'should not be empty'; case 'string.regex.base': - return 'should not contain spaces or special characters'; + if (isApiName) { + return 'should not contain trailing or leading spaces, special characters, and consecutive spaces'; + } else { + return 'should not contain spaces or special characters'; + } case 'string.max': return 'has exceeded the maximum number of ' + maxLength + ' characters'; default: @@ -121,38 +125,39 @@ const documentSchema = Joi.extend((joi) => ({ })); const definition = { - apiName: Joi.string().max(50).regex(/^[^~!@#;:%^*()+={}|\\<>"',&$\s+[\]/]*$/).required() + apiName: Joi.string().max(50).regex(/^(?!.*\s{2})(?!.*[~!@#;:%^*()+={}|\\<>"',&$[\]/]).*$/).required() .error((errors) => { - return errors.map((error) => ({ ...error, message: 'Name ' + getMessage(error.type, 50) })); + return errors.map((error) => ({ ...error, message: 'Name ' + getMessage(error.type, true, 50) })); }), apiVersion: Joi.string().regex(/^[^~!@#;:%^*()+={}|\\<>"',&/$[\]\s]+$/).required().error((errors) => { const tmpErrors = [...errors]; errors.forEach((err, index) => { const tmpError = { ...err }; - tmpError.message = 'API Version ' + getMessage(err.type); + tmpError.message = 'API Version ' + getMessage(err.type, false); tmpErrors[index] = tmpError; }); return tmpErrors; }), apiContext: Joi.string().max(200).regex(/(?!.*\/t\/.*|.*\/t$)^[^~!@#:%^&*+=|\\<>"',&\s[\]]*$/).required() .error((errors) => { - return errors.map((error) => ({ ...error, message: 'Context ' + getMessage(error.type, 200) })); + return errors.map((error) => ({ ...error, message: 'Context ' + getMessage(error.type, false, 200) })); }), gatewayVendor: Joi.string().max(50).regex(/^[^~!@#;:%^*()+={}|\\<>"',&$\s+[\]/]*$/).required() .error((errors) => { - return errors.map((error) => ({ ...error, message: 'Name ' + getMessage(error.type, 50) })); + return errors.map((error) => ({ ...error, message: 'Name ' + getMessage(error.type, false, 50) })); }), documentName: Joi.string().max(50).regex(/^[^~!@#;:%^*()+={}|\\<>"',&$+[\]/]*$/).required() .error((errors) => { - return errors.map((error) => ({ ...error, message: 'Document name ' + getMessage(error.type, 50) })); + return errors.map((error) => ({ ...error, message: 'Document name ' + getMessage(error.type, false, 50) })); }), authorizationHeader: Joi.string().regex(/^[^~!@#;:%^*()+={}|\\<>"',&$\s+]*$/).required() .error((errors) => { - return errors.map((error) => ({ ...error, message: 'Authorization Header ' + getMessage(error.type) })); + return errors.map((error) => ({ ...error, message: 'Authorization Header ' + + getMessage(error.type, false) })); }), apiKeyHeader: Joi.string().regex(/^[^~!@#;:%^*()+={}|\\<>"',&$\s+]*$/).required() .error((errors) => { - return errors.map((error) => ({ ...error, message: 'Api Key Header ' + getMessage(error.type) })); + return errors.map((error) => ({ ...error, message: 'Api Key Header ' + getMessage(error.type, false) })); }), role: roleSchema.systemRole().role(), scope: scopeSchema.scopes().scope(), @@ -160,7 +165,7 @@ const definition = { const tmpErrors = [...errors]; errors.forEach((err, index) => { const tmpError = { ...err }; - tmpError.message = 'URL ' + getMessage(err.type); + tmpError.message = 'URL ' + getMessage(err.type, false); tmpErrors[index] = tmpError; }); return tmpErrors; @@ -171,14 +176,14 @@ const definition = { const tmpError = { ...err }; const errType = err.type; tmpError.message = errType === 'string.uriCustomScheme' ? 'Invalid WebSocket URL' - : 'WebSocket URL ' + getMessage(errType); + : 'WebSocket URL ' + getMessage(errType, false); tmpErrors[index] = tmpError; }); return tmpErrors; }), alias: Joi.string().max(30).regex(/^[^~!@#;:%^*()+={}|\\<>"',&$\s+[\]/]*$/).required() .error((errors) => { - return errors.map((error) => ({ ...error, message: 'Alias ' + getMessage(error.type, 30) })); + return errors.map((error) => ({ ...error, message: 'Alias ' + getMessage(error.type, false, 30) })); }), userRole: userRoleSchema.userRole().role(), apiParameter: apiSchema.api().isAPIParameterExist(), diff --git a/tests/cypress/integration/admin/11-change-the-owner-of-application.spec.js b/tests/cypress/integration/admin/11-change-the-owner-of-application.spec.js index a6bbb81ba17..e2ff199313f 100644 --- a/tests/cypress/integration/admin/11-change-the-owner-of-application.spec.js +++ b/tests/cypress/integration/admin/11-change-the-owner-of-application.spec.js @@ -36,7 +36,7 @@ describe("Change the owner of application", () => { //login to admin portal cy.loginToAdmin(carbonUsername, carbonPassword); - cy.get('[data-testid="Applications-child-link"]').click({ force: true }); + cy.get('[data-testid="Change Application Owner-child-link"]').click({ force: true }); cy.get("#itest-application-list-table").within(() => { cy.contains("tr", appName).within(() => { cy.get("td > span").click({ force: true });