diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/APIAdmin.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/APIAdmin.java index c96065dafafa..c23d43674d82 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/APIAdmin.java +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/APIAdmin.java @@ -484,4 +484,27 @@ boolean isScopeExists(String username, String scopeName) Policy[] getPolicies(int tenantId, String level) throws APIManagementException; Policy getPolicyByNameAndType(int tenantId, String level, String name) throws APIManagementException; + + /** + * Update the Api Provider of a given Api Id + * + * @param apiId Api ID + * @param provider New ProviderName/Owner of the Api + * @param organisation Organisation + * @throws APIManagementException + */ + void updateApiProvider(String apiId, String provider, String organisation) throws APIManagementException; + + /** + * Get/Search All Apis in admin portal + * + * @param searchQuery Api name search query + * @param organization organization + * @param start + * @param end + * @return + * @throws APIManagementException + */ + Map searchPaginatedApis(String searchQuery, String organization, int start, int end) + throws APIManagementException; } diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/ExceptionCodes.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/ExceptionCodes.java index 2683c1d026c5..a338d4ceda2b 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/ExceptionCodes.java +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/ExceptionCodes.java @@ -542,8 +542,11 @@ public enum ExceptionCodes implements ErrorHandler { "Revision deployment request conflicted with the current deployment state of the revision %s. Please try again later", false), INVALID_API_ID(902006, "Invalid API ID", 404, "The provided API ID is not found %s", false), INVALID_ENDPOINT_CONFIG(902012, "Endpoint config value(s) is(are) not valid", 400, "Endpoint config value(s) is(are) not valid"), + ARTIFACT_SYNC_HTTP_REQUEST_FAILED(903009, "Error while retrieving from remote endpoint", 500, "Error while executing HTTP request to retrieve from remote endpoint"), KEY_MANAGER_RESTRICTED_FOR_USER(902013, "Unauthorized Access to Key Manager", 403, "Key Manager is Restricted for this user"), - ARTIFACT_SYNC_HTTP_REQUEST_FAILED(903009, "Error while retrieving from remote endpoint", 500, "Error while executing HTTP request to retrieve from remote endpoint"); + // Admin portal get apis and api provider change related errors + CHANGE_API_PROVIDER_FAILED(903011, "Error while changing the API provider", 500, "Error while changing the API provider in the registry or DB"), + GET_SEARCH_APIS_IN_ADMIN_FAILED(903012, "Error while getting the apis", 500, "Error while getting/searching the apis from registry"); private final long errorCode; private final String errorMessage; diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/ApplicationInfoKeyManager.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/ApplicationInfoKeyManager.java new file mode 100644 index 000000000000..e17e00792941 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/ApplicationInfoKeyManager.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, 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. + */ +package org.wso2.carbon.apimgt.api.model; + +/** + * This class represent the minimized view of the Application in api model for Key manager operations in admin REST API. + */ +public class ApplicationInfoKeyManager extends ApplicationInfo { + + private String organization; + + public String getOrganization() { + + return organization; + } + + public void setOrganization(String organization) { + + this.organization = organization; + } + +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIAdminImpl.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIAdminImpl.java index b6c5fa1c6373..0426feff9d7c 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIAdminImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIAdminImpl.java @@ -27,6 +27,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.solr.client.solrj.util.ClientUtils; import org.everit.json.schema.Schema; import org.everit.json.schema.ValidationException; import org.json.JSONException; @@ -39,11 +40,17 @@ import org.wso2.carbon.apimgt.api.APIMgtResourceNotFoundException; import org.wso2.carbon.apimgt.api.ExceptionCodes; import org.wso2.carbon.apimgt.api.dto.KeyManagerConfigurationDTO; +import org.wso2.carbon.apimgt.api.model.API; import org.wso2.carbon.apimgt.api.dto.KeyManagerPermissionConfigurationDTO; import org.wso2.carbon.apimgt.api.model.APICategory; +import org.wso2.carbon.apimgt.api.model.APIIdentifier; +import org.wso2.carbon.apimgt.api.model.APIProduct; +import org.wso2.carbon.apimgt.api.model.APIProductIdentifier; import org.wso2.carbon.apimgt.api.model.Application; import org.wso2.carbon.apimgt.api.model.ApplicationInfo; import org.wso2.carbon.apimgt.api.model.ConfigurationDto; +import org.wso2.carbon.apimgt.api.model.Documentation; +import org.wso2.carbon.apimgt.api.model.DocumentationType; import org.wso2.carbon.apimgt.api.model.Environment; import org.wso2.carbon.apimgt.api.model.KeyManagerConfiguration; import org.wso2.carbon.apimgt.api.model.KeyManagerConnectorConfiguration; @@ -60,11 +67,30 @@ import org.wso2.carbon.apimgt.impl.dao.constants.SQLConstants; import org.wso2.carbon.apimgt.impl.dto.ThrottleProperties; import org.wso2.carbon.apimgt.impl.dto.WorkflowProperties; +import org.wso2.carbon.apimgt.impl.factory.PersistenceFactory; import org.wso2.carbon.apimgt.impl.internal.ServiceReferenceHolder; import org.wso2.carbon.apimgt.impl.keymgt.KeyMgtNotificationSender; import org.wso2.carbon.apimgt.impl.monetization.DefaultMonetizationImpl; import org.wso2.carbon.apimgt.impl.service.KeyMgtRegistrationService; +import org.wso2.carbon.apimgt.impl.utils.APINameComparator; +import org.wso2.carbon.apimgt.impl.utils.APIProductNameComparator; import org.wso2.carbon.apimgt.impl.utils.APIUtil; +import org.wso2.carbon.apimgt.impl.utils.ContentSearchResultNameComparator; +import org.wso2.carbon.apimgt.persistence.APIPersistence; +import org.wso2.carbon.apimgt.persistence.dto.AdminApiInfo; +import org.wso2.carbon.apimgt.persistence.dto.AdminApiSearchContent; +import org.wso2.carbon.apimgt.persistence.dto.AdminContentSearchResult; +import org.wso2.carbon.apimgt.persistence.dto.DocumentSearchContent; +import org.wso2.carbon.apimgt.persistence.dto.Organization; +import org.wso2.carbon.apimgt.persistence.dto.PublisherAPIInfo; +import org.wso2.carbon.apimgt.persistence.dto.PublisherAPISearchResult; +import org.wso2.carbon.apimgt.persistence.dto.PublisherContentSearchResult; +import org.wso2.carbon.apimgt.persistence.dto.PublisherSearchContent; +import org.wso2.carbon.apimgt.persistence.dto.SearchContent; +import org.wso2.carbon.apimgt.persistence.dto.UserContext; +import org.wso2.carbon.apimgt.persistence.exceptions.APIPersistenceException; +import org.wso2.carbon.apimgt.persistence.mapper.APIMapper; +import org.wso2.carbon.apimgt.persistence.utils.RegistrySearchUtil; import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.core.util.CryptoException; @@ -91,9 +117,13 @@ import java.util.Date; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.SortedSet; import java.util.TimeZone; +import java.util.TreeSet; import java.util.UUID; import java.util.stream.Collectors; @@ -1327,6 +1357,76 @@ public void updateTenantConfig(String organization, String config) throws APIMan } } + public void updateApiProvider(String apiId, String provider, String organisation) throws APIManagementException { + APIPersistence apiPersistenceInstance = PersistenceFactory.getAPIPersistenceInstance(); + try { + ApiMgtDAO.getInstance().updateApiProvider(apiId, provider); + apiPersistenceInstance.changeApiProvider(provider, apiId, organisation); + } catch (APIPersistenceException e) { + throw new APIManagementException("Error while changing the API provider", e); + } + } + + /** + * get/search paginated APIs in admin portal + * + * @param searchQuery API name search query + * @param organization organization + * @param start start index of the pagination + * @param end end index of the pagination + * @return APIs result object + * @throws APIManagementException if an error occurs when searching/getting the APIs + */ + public Map searchPaginatedApis(String searchQuery, String organization, int start, int end) + throws APIManagementException { + ArrayList compoundResult = new ArrayList<>(); + Map result = new HashMap<>(); + SortedSet apiSet = new TreeSet<>(new APINameComparator()); + String modifiedSearchQuery = buildSearchQuery(searchQuery); + try { + APIPersistence apiPersistenceInstance = PersistenceFactory.getAPIPersistenceInstance(); + AdminContentSearchResult results = apiPersistenceInstance.searchContentForAdmin(organization, + modifiedSearchQuery, start, end, end); + if (results != null) { + List resultList = results.getApis(); + for (SearchContent item : resultList) { + if (APIConstants.API_IDENTIFIER_TYPE.equals(item.getType())) { + AdminApiSearchContent adminSearchApi = (AdminApiSearchContent) item; + API api = new API(new APIIdentifier(adminSearchApi.getProvider(), adminSearchApi.getName(), + adminSearchApi.getVersion())); + api.setUuid(adminSearchApi.getId()); + apiSet.add(api); + } + } + compoundResult.addAll(apiSet); + compoundResult.sort(new ContentSearchResultNameComparator()); + result.put(APIConstants.API_DATA_LENGTH, compoundResult.size()); + result.put(APIConstants.ADMIN_API_LIST_RESPONSE_PARAMS_TOTAL, results.getApiTotal()); + } else { + result.put(APIConstants.API_DATA_LENGTH, compoundResult.size()); + } + } catch (APIPersistenceException e) { + throw new APIManagementException("Error while searching apis ", + ExceptionCodes.GET_SEARCH_APIS_IN_ADMIN_FAILED); + } + result.put(APIConstants.API_DATA_APIS, compoundResult); + return result; + } + + /** + * If the user provided a search query then it will use that, otherwise it will use the asterix(*) symbol. + * + * @param searchQuery searchQuery that the user provided + * @return modified searchQuery + */ + private String buildSearchQuery(String searchQuery) { + if (searchQuery.equals(APIConstants.CHAR_ASTERIX)) { + return String.format(APIConstants.ADMIN_PORTAL_GET_APIS_QUERY, APIConstants.CHAR_ASTERIX); + } else { + return String.format(APIConstants.ADMIN_PORTAL_GET_APIS_QUERY, searchQuery); + } + } + @Override public String getTenantConfigSchema(String organization) { return APIUtil.retrieveTenantConfigJsonSchema().toString(); diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConstants.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConstants.java index c709abb8c383..6feadacf1c26 100755 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConstants.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConstants.java @@ -145,6 +145,10 @@ public final class APIConstants { public static final String SSL_VERIFY_CLIENT_STATUS_REQUIRE = "require"; + public static final String ADMIN_PORTAL_GET_APIS_QUERY = "name=\\*\"%s\"\\* AND" + + " mediaType:application\\/vnd.wso2\\-api\\+xml AND" + + " type=(HTTP OR WS OR SOAPTOREST OR GRAPHQL OR SOAP OR SSE OR WEBSUB OR WEBHOOK OR ASYNC)"; + //location for custom url domain mapings. "" will be replaced by actual tenant name. public static final String API_DOMAIN_MAPPINGS = "/customurl/api-cloud//urlMapping/"; public static final String API_DOMAIN_MAPPING_TENANT_ID_IDENTIFIER = ""; @@ -1653,6 +1657,7 @@ private ConfigParameters() { public static final String API_DATA_DEFAULT_THUMB = "images/api-default.png"; public static final String API_DATA_APIS = "apis"; public static final String API_DATA_TOT_LENGTH = "totalLength"; + public static final String ADMIN_API_LIST_RESPONSE_PARAMS_TOTAL = "totalLength"; public static final String API_DATA_LENGTH = "length"; public static final String API_DATA_ISMORE = "isMore"; public static final String API_DATA_PRODUCTION_ENDPOINTS = "production_endpoints"; diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/ApiMgtDAO.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/ApiMgtDAO.java index 2d10c732ef83..e3a797567de5 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/ApiMgtDAO.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/ApiMgtDAO.java @@ -18630,6 +18630,31 @@ private void updateAPIServiceMapping(int apiId, String serviceKey, String md5, i } } + /** + * Update the API provider of a given API + * + * @param apiUUID API id of the API that needs to update the provider + * @param providerName New API provider + * @throws APIManagementException if an error occurs when changing the API provider + */ + public void updateApiProvider(String apiUUID, String providerName) + throws APIManagementException { + try (Connection connection = APIMgtDBUtil.getConnection()) { + connection.setAutoCommit(false); + try (PreparedStatement statement = connection.prepareStatement(SQLConstants.UPDATE_API_PROVIDER_SQL)) { + statement.setString(1, providerName); + statement.setString(2, apiUUID); + statement.executeUpdate(); + connection.commit(); + } catch (SQLException e) { + connection.rollback(); + throw e; + } + } catch (SQLException e) { + handleException("Error while updating the API provider of " + apiUUID, e); + } + } + private void updateLatestRevisionNumber(Connection connection, String apiUUID, int revisionId) throws SQLException { try (PreparedStatement preparedStatement = diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java index 0665a38a6702..b7af3e447a07 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java @@ -175,6 +175,8 @@ public class SQLConstants { " MD5 = ? " + " WHERE API_ID = ?"; + public static final String UPDATE_API_PROVIDER_SQL = "UPDATE AM_API SET API_PROVIDER = ? WHERE API_UUID = ?"; + public static final String GET_MD5_VALUE_OF_SERVICE_BY_API_ID_SQL = "SELECT " + " AM_SERVICE_CATALOG.MD5 AS SERVICE_MD5, " + " AM_SERVICE_CATALOG.SERVICE_NAME, " + diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/resources/tenant/tenant-conf.json b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/resources/tenant/tenant-conf.json index af267f2407f8..9558e6ed49e9 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/resources/tenant/tenant-conf.json +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/resources/tenant/tenant-conf.json @@ -357,6 +357,10 @@ "Name": "apim:admin_tier_view", "Roles": "admin" }, + { + "Name": "apim:api_provider_change", + "Roles": "admin" + }, { "Name": "apim:gateway_policy_manage", "Roles": "admin" diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/test/java/org/wso2/carbon/apimgt/impl/APIProviderImplTest.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/test/java/org/wso2/carbon/apimgt/impl/APIProviderImplTest.java index 3aa2b8ed1c5e..1398c4345039 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/test/java/org/wso2/carbon/apimgt/impl/APIProviderImplTest.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/test/java/org/wso2/carbon/apimgt/impl/APIProviderImplTest.java @@ -125,16 +125,15 @@ import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; -import java.util.List; -import java.util.UUID; import java.util.SortedMap; import java.util.TreeMap; -import java.util.ArrayList; - +import java.util.UUID; import javax.cache.Caching; import javax.xml.namespace.QName; import javax.xml.stream.XMLInputFactory; diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/APIConstants.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/APIConstants.java index 3acfce4d1ed3..7a19bff16c72 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/APIConstants.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/APIConstants.java @@ -244,6 +244,7 @@ public static class Monetization { public static final String TAG_COLON_SEARCH_TYPE_PREFIX = "tag:"; public static final String NAME_TYPE_PREFIX = "name"; private static final String PROVIDER_SEARCH_TYPE_PREFIX = "provider"; + public static final String API_PROVIDER_SUFFIX_SLASH = PROVIDER_SEARCH_TYPE_PREFIX + "/"; private static final String VERSION_SEARCH_TYPE_PREFIX = "version"; private static final String CONTEXT_SEARCH_TYPE_PREFIX = "context"; public static final String CONTENT_SEARCH_TYPE_PREFIX = "content"; diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/APIPersistence.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/APIPersistence.java index 583bd1d2ab38..c62ed7f45910 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/APIPersistence.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/APIPersistence.java @@ -16,7 +16,9 @@ package org.wso2.carbon.apimgt.persistence; +import org.wso2.carbon.apimgt.api.APIManagementException; import org.wso2.carbon.apimgt.api.model.Tag; +import org.wso2.carbon.apimgt.persistence.dto.AdminContentSearchResult; import org.wso2.carbon.apimgt.persistence.dto.DevPortalAPI; import org.wso2.carbon.apimgt.persistence.dto.DevPortalAPISearchResult; import org.wso2.carbon.apimgt.persistence.dto.DevPortalContentSearchResult; @@ -508,4 +510,9 @@ PublisherAPIProductSearchResult searchAPIProductsForPublisher(Organization org, */ Set getAllTags(Organization org, UserContext ctx) throws APIPersistenceException; + void changeApiProvider(String providerName, String apiId, String org) throws APIManagementException, + APIPersistenceException; + + AdminContentSearchResult searchContentForAdmin(String org, String searchQuery, int start, int count, + int limit) throws APIPersistenceException; } \ No newline at end of file diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/RegistryPersistenceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/RegistryPersistenceImpl.java index 8c6326d9e259..402ca21829c2 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/RegistryPersistenceImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/RegistryPersistenceImpl.java @@ -284,7 +284,7 @@ public String addAPIRevision(Organization org, String apiUUID, int revisionId) t if (apiArtifact != null) { API api = RegistryPersistenceUtil.getApiForPublishing(registry, apiArtifact); APIIdentifier apiId = api.getId(); - String apiPath = RegistryPersistenceUtil.getAPIPath(apiId); + String apiPath = getAPIArtifact(api.getUuid(),registry).getPath(); int prependIndex = apiPath.lastIndexOf("/api"); String apiSourcePath = apiPath.substring(0, prependIndex); String revisionTargetPath = RegistryPersistenceUtil.getRevisionPath(apiId.getUUID(), revisionId); @@ -477,7 +477,7 @@ public PublisherAPI updateAPI(Organization org, PublisherAPI publisherAPI) throw tenantFlowStarted = holder.isTenantFlowStarted(); registry.beginTransaction(); - String apiArtifactId = registry.get(RegistryPersistenceUtil.getAPIPath(api.getId())).getUUID(); + String apiArtifactId = api.getUuid(); GenericArtifactManager artifactManager = RegistryPersistenceUtil.getArtifactManager(registry, APIConstants.API_KEY); if (artifactManager == null) { String errorMessage = "Artifact manager is null when updating API artifact ID " + api.getId(); @@ -796,15 +796,16 @@ public void deleteAPI(Organization org, String apiId) throws APIPersistenceExcep registry.delete(artifact.getPath()); } } - - artifactManager.removeGenericArtifact(apiArtifact); - - String path = APIConstants.API_ROOT_LOCATION + RegistryConstants.PATH_SEPARATOR + - identifier.getProviderName() + RegistryConstants.PATH_SEPARATOR + - identifier.getApiName() + RegistryConstants.PATH_SEPARATOR + identifier.getVersion(); - Resource apiResource = registry.get(path); - String artifactId = apiResource.getUUID(); + String artifactId = getAPIArtifact(apiId, registry).getId(); + String apiPath = getAPIArtifact(apiId, registry).getPath(); artifactManager.removeGenericArtifact(artifactId); + artifactManager.removeGenericArtifact(apiArtifact); + if (apiPath.endsWith("api")) { + apiPath = apiPath.replaceAll("/api$", ""); + Resource apiResource = registry.get(apiPath); + artifactId = apiResource.getUUID(); + artifactManager.removeGenericArtifact(artifactId); + } String thumbPath = RegistryPersistenceUtil.getIconPath(identifier); if (registry.resourceExists(thumbPath)) { @@ -815,17 +816,18 @@ public void deleteAPI(Organization org, String apiId) throws APIPersistenceExcep if (registry.resourceExists(wsdlArchivePath)) { registry.delete(wsdlArchivePath); } - + String providerName = RegistryPersistenceUtil.extractProvider(apiPath, + apiArtifact.getQName().getLocalPart()); /*Remove API Definition Resource - swagger*/ String apiDefinitionFilePath = APIConstants.API_DOC_LOCATION + RegistryConstants.PATH_SEPARATOR + - identifier.getApiName() + '-' + identifier.getVersion() + '-' + identifier.getProviderName(); + identifier.getApiName() + '-' + identifier.getVersion() + '-' + providerName; if (registry.resourceExists(apiDefinitionFilePath)) { registry.delete(apiDefinitionFilePath); } /*remove empty directories*/ String apiCollectionPath = APIConstants.API_ROOT_LOCATION + RegistryConstants.PATH_SEPARATOR + - identifier.getProviderName() + RegistryConstants.PATH_SEPARATOR + identifier.getApiName(); + providerName + RegistryConstants.PATH_SEPARATOR + identifier.getApiName(); if (registry.resourceExists(apiCollectionPath)) { Resource apiCollection = registry.get(apiCollectionPath); CollectionImpl collection = (CollectionImpl) apiCollection; @@ -839,7 +841,7 @@ public void deleteAPI(Organization org, String apiId) throws APIPersistenceExcep } String apiProviderPath = APIConstants.API_ROOT_LOCATION + RegistryConstants.PATH_SEPARATOR + - identifier.getProviderName(); + providerName; if (registry.resourceExists(apiProviderPath)) { Resource providerCollection = registry.get(apiProviderPath); @@ -847,7 +849,7 @@ public void deleteAPI(Organization org, String apiId) throws APIPersistenceExcep //if there is no api for given provider delete the provider directory if (collection.getChildCount() == 0) { if (log.isDebugEnabled()) { - log.debug("No more APIs from the provider " + identifier.getProviderName() + " found. " + + log.debug("No more APIs from the provider " + providerName + " found. " + "Removing provider collection from registry"); } registry.delete(apiProviderPath); @@ -2164,8 +2166,8 @@ public Documentation addDocumentation(Organization org, String apiId, Documentat APIConstants.API_KEY); GenericArtifact apiArtifact = apiArtifactManager.getGenericArtifact(apiId); - String apiProviderName = apiArtifact.getAttribute(APIConstants.API_OVERVIEW_PROVIDER); - apiProviderName = RegistryPersistenceUtil.replaceEmailDomain(apiProviderName); + String apiProviderName = RegistryPersistenceUtil.extractProvider(apiArtifact.getPath(), + apiArtifact.getQName().getLocalPart()); String apiName = apiArtifact.getAttribute(APIConstants.API_OVERVIEW_NAME); String apiVersion = apiArtifact.getAttribute(APIConstants.API_OVERVIEW_VERSION); @@ -2418,8 +2420,8 @@ public DocumentContent addDocumentationContent(Organization org, String apiId, S APIConstants.API_KEY); GenericArtifact apiArtifact = apiArtifactManager.getGenericArtifact(apiId); - String apiProviderName = apiArtifact.getAttribute(APIConstants.API_OVERVIEW_PROVIDER); - apiProviderName = RegistryPersistenceUtil.replaceEmailDomain(apiProviderName); + String apiProviderName = RegistryPersistenceUtil.extractProvider(apiArtifact.getPath(), + apiArtifact.getQName().getLocalPart()); String apiName = apiArtifact.getAttribute(APIConstants.API_OVERVIEW_NAME); String apiVersion = apiArtifact.getAttribute(APIConstants.API_OVERVIEW_VERSION); @@ -3748,4 +3750,143 @@ public int compare(Tag o1, Tag o2) { } } } + + @Override + public void changeApiProvider(String providerName, String apiId, String org) + throws APIPersistenceException { + boolean isTenantFlowStarted = false; + boolean transactionCommitted = false; + RegistryHolder holder = getRegistry(org); + Registry userRegistry = holder.getRegistry(); + isTenantFlowStarted = holder.isTenantFlowStarted(); + try { + String oldArtifactPath = GovernanceUtils.getArtifactPath(userRegistry, apiId); + Resource apiResource = userRegistry.get(oldArtifactPath); + if (apiResource != null) { + userRegistry.beginTransaction(); + GenericArtifactManager artifactManager = RegistryPersistenceUtil.getArtifactManager(userRegistry, + APIConstants.API_KEY); + if (artifactManager == null) { + String errorMessage = "Artifact manager is null when changing the provider name of " + apiId; + log.error(errorMessage); + throw new APIPersistenceException(errorMessage); + } + GenericArtifact artifact = getAPIArtifact(apiId, userRegistry); + artifact.setAttribute(APIConstants.API_OVERVIEW_PROVIDER, providerName); + artifactManager.updateGenericArtifact(artifact); + userRegistry.commitTransaction(); + transactionCommitted=true; + } + } catch (RegistryException e) { + throw new APIPersistenceException("Error while Changing the api Provider", e); + } finally { + try { + if (userRegistry != null && !transactionCommitted) { + userRegistry.rollbackTransaction(); + } + } catch (RegistryException ex) { + log.error("Error while rolling back the transaction for Api Provider Change"); + } + if (isTenantFlowStarted) { + PrivilegedCarbonContext.endTenantFlow(); + } + } + } + + @Override + public AdminContentSearchResult searchContentForAdmin(String org, String searchQuery, int start, int count, + int limit) throws APIPersistenceException { + + boolean isTenantFlowStarted = false; + AdminContentSearchResult result = null; + try { + RegistryHolder holder = getRegistry(org); + Registry registry = holder.getRegistry(); + isTenantFlowStarted = holder.isTenantFlowStarted(); + String tenantAwareUsername = getTenantAwareUsername(RegistryPersistenceUtil.getTenantAdminUserName(org)); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(tenantAwareUsername); + PaginationContext.init(start, count, "ASC", APIConstants.API_OVERVIEW_NAME, limit); + GenericArtifactManager apiArtifactManager = RegistryPersistenceUtil.getArtifactManager(registry, + APIConstants.API_KEY); + + int tenantId = holder.getTenantId(); + if (tenantId == -1) { + tenantId = MultitenantConstants.SUPER_TENANT_ID; + } + + UserRegistry systemUserRegistry = ServiceReferenceHolder.getInstance().getRegistryService() + .getRegistry(CarbonConstants.REGISTRY_SYSTEM_USERNAME, tenantId); + ContentBasedSearchService contentBasedSearchService = new ContentBasedSearchService(); + + SearchResultsBean resultsBean = contentBasedSearchService + .searchContent(searchQuery, systemUserRegistry); + String errorMsg = resultsBean.getErrorMessage(); + if (errorMsg != null) { + throw new APIPersistenceException("Error while searching " + errorMsg); + } + ResourceData[] resourceDataList = resultsBean.getResourceDataList(); + int totalLength = PaginationContext.getInstance().getLength(); + + if (resourceDataList != null) { + result = new AdminContentSearchResult(); + List contentData = new ArrayList<>(); + if (log.isDebugEnabled()) { + log.debug("Number of records Found: " + resourceDataList.length); + } + + for (ResourceData data : resourceDataList) { + String resourcePath = data.getResourcePath(); + if (resourcePath.contains(APIConstants.APIMGT_REGISTRY_LOCATION)) { + int index = resourcePath.indexOf(APIConstants.APIMGT_REGISTRY_LOCATION); + resourcePath = resourcePath.substring(index); + Resource resource = registry.get(resourcePath); + String apiArtifactId = resource.getUUID(); + + String type; + if (apiArtifactId != null) { + GenericArtifact apiArtifact = apiArtifactManager.getGenericArtifact(apiArtifactId); + if (apiArtifact.getAttribute(APIConstants.API_OVERVIEW_TYPE). + equals(APIConstants.API_PRODUCT)) { + type = APIConstants.API_PRODUCT; + } else { + type = APIConstants.API; + } + PublisherAPI pubAPI = RegistryPersistenceUtil.getAPIForSearch(apiArtifact); + AdminApiSearchContent content = new AdminApiSearchContent(); + content.setContext(pubAPI.getContext()); + content.setDescription(pubAPI.getDescription()); + content.setId(pubAPI.getId()); + content.setName(pubAPI.getApiName()); + content.setProvider(RegistryPersistenceUtil.replaceEmailDomainBack(pubAPI + .getProviderName())); + content.setType(type); + content.setVersion(pubAPI.getVersion()); + content.setStatus(pubAPI.getStatus()); + content.setAdvertiseOnly(pubAPI.isAdvertiseOnly()); + content.setThumbnailUri(pubAPI.getThumbnail()); + if (apiArtifact.getAttribute("overview_keyManagers") != null) { + content.setKeyManagerEntry(apiArtifact.getAttribute("overview_keyManagers") + .replace("[\"","").replace("\"]","") + .replace("\",\""," , ")); + } + contentData.add(content); + } else { + throw new GovernanceException("artifact id is null for " + resourcePath); + } + } + } + result.setApiCount(contentData.size()); + result.setApis(contentData); + result.setApiTotal(totalLength); + } + } catch (RegistryException | IndexerException | APIManagementException e) { + throw new APIPersistenceException("Error while searching for content ", e); + } finally { + if (isTenantFlowStarted) { + PrivilegedCarbonContext.endTenantFlow(); + } + } + return result; + } + } diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/AdminApiInfo.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/AdminApiInfo.java new file mode 100644 index 000000000000..5590d4c13454 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/AdminApiInfo.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023, 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. + */ +package org.wso2.carbon.apimgt.persistence.dto; + +public class AdminApiInfo { + private String id; + private String apiName; + private String version; + private String providerName; + + public String getProviderName() { + return providerName; + } + + public void setProviderName(String providerName) { + this.providerName = providerName; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getApiName() { + return apiName; + } + + public void setApiName(String apiName) { + this.apiName = apiName; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/AdminApiSearchContent.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/AdminApiSearchContent.java new file mode 100644 index 000000000000..7bd9ca8f2eeb --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/AdminApiSearchContent.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2023, 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. + */ +package org.wso2.carbon.apimgt.persistence.dto; + +public class AdminApiSearchContent implements SearchContent { + + String id; + String type = "API"; + String name; + String transportType = "HTTP"; + String description; + String context; + String version; + String provider; + String status; + String thumbnailUri; + Boolean advertiseOnly; + + String keyManagerEntry; + + @Override + public String getId() { + return id; + } + + @Override + public String getType() { + return type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTransportType() { + return transportType; + } + + public void setTransportType(String transportType) { + this.transportType = transportType; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getContext() { + return context; + } + + public void setContext(String context) { + this.context = context; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getThumbnailUri() { + return thumbnailUri; + } + + public void setThumbnailUri(String thumbnailUri) { + this.thumbnailUri = thumbnailUri; + } + + public void setId(String id) { + this.id = id; + } + + public void setType(String type) { + this.type = type; + } + + public Boolean getAdvertiseOnly() { + return advertiseOnly; + } + + public void setAdvertiseOnly(Boolean advertiseOnly) { + this.advertiseOnly = advertiseOnly; + } + + public String getKeyManagerEntry() { + return keyManagerEntry; + } + + public void setKeyManagerEntry(String keyManagerEntry) { + this.keyManagerEntry = keyManagerEntry; + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/AdminContentSearchResult.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/AdminContentSearchResult.java new file mode 100644 index 000000000000..6a87b83d659e --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/AdminContentSearchResult.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023, 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. + */ + +package org.wso2.carbon.apimgt.persistence.dto; + +import org.wso2.carbon.apimgt.api.model.ApplicationInfoKeyManager; + +import java.util.ArrayList; +import java.util.List; + +public class AdminContentSearchResult { + int apiCount; + + public int getApiTotal() { return apiTotal; } + + public void setApiTotal(int apiTotal) { this.apiTotal = apiTotal; } + + int apiTotal; + int applicationCount; + List apis = new ArrayList(); + + List applications = new ArrayList<>(); + + public List getApis() { + return apis; + } + + public void setApis(List apis) { + this.apis = apis; + } + + public int getApiCount() { + return apiCount; + } + + public void setApiCount(int apiCount) { + this.apiCount = apiCount; + } + + public int getApplicationCount() { + return applicationCount; + } + + public void setApplicationCount(int applicationCount) { + this.applicationCount = applicationCount; + } + + public List getApplications() { return applications; } + + public void setApplications(List applications) {this.applications = applications; } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistryPersistenceUtil.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistryPersistenceUtil.java index 67bd67eb6aa7..8c35fc554c3c 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistryPersistenceUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistryPersistenceUtil.java @@ -1826,6 +1826,13 @@ public static void addLifecycleIfNotExists(int tenantId) throws APIPersistenceEx } } + public static String extractProvider(String apiPath, String apiName) { + int startIndex = apiPath.indexOf(APIConstants.API_PROVIDER_SUFFIX_SLASH) + + APIConstants.API_PROVIDER_SUFFIX_SLASH.length(); + int endIndex = apiPath.indexOf("/" + apiName + "/"); + return apiPath.substring(startIndex, endIndex); + } + private static RegistryService getRegistryService() { return ServiceReferenceHolder.getInstance().getRegistryService(); } diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/admin/v1/impl/ApisApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/admin/v1/impl/ApisApiServiceImpl.java new file mode 100644 index 000000000000..432496b9b111 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/admin/v1/impl/ApisApiServiceImpl.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2023, 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. + */ +package org.wso2.carbon.apimgt.rest.api.admin.v1.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.wso2.carbon.apimgt.api.APIAdmin; +import org.wso2.carbon.apimgt.api.APIManagementException; +import org.wso2.carbon.apimgt.api.ExceptionCodes; +import org.wso2.carbon.apimgt.api.model.API; +import org.wso2.carbon.apimgt.impl.APIAdminImpl; +import org.wso2.carbon.apimgt.impl.APIConstants; +import org.wso2.carbon.apimgt.impl.restapi.publisher.SearchApiServiceImplUtil; +import org.wso2.carbon.apimgt.impl.utils.APIUtil; +import org.wso2.carbon.apimgt.rest.api.admin.v1.ApisApiService; +import org.wso2.carbon.apimgt.rest.api.admin.v1.dto.ApiResultDTO; +import org.wso2.carbon.apimgt.rest.api.admin.v1.dto.SearchResultListDTO; +import org.wso2.carbon.apimgt.rest.api.admin.v1.utils.mappings.APIInfoMappingUtil; +import org.wso2.carbon.apimgt.rest.api.admin.v1.utils.mappings.ApplicationMappingUtil; +import org.wso2.carbon.apimgt.rest.api.common.RestApiCommonUtil; +import org.wso2.carbon.apimgt.rest.api.util.RestApiConstants; +import org.wso2.carbon.apimgt.rest.api.util.utils.RestApiUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import javax.ws.rs.core.Response; + +public class ApisApiServiceImpl implements ApisApiService { + + private static final Log log = LogFactory.getLog(ApisApiServiceImpl.class); + + /** + * This method gets all the APIs in the admin portal. + * + * @param limit pagination limit/ end value + * @param offset pagination start value + * @param query search query if the user has given any. + * @param ifNoneMatch + * @param messageContext + * @return api results + * @throws APIManagementException If there is any when getting the all apis + */ + public Response getAllAPIs(Integer limit, Integer offset, String query, String ifNoneMatch, + MessageContext messageContext) throws APIManagementException { + SearchResultListDTO resultListDTO = new SearchResultListDTO(); + limit = limit != null ? limit : RestApiConstants.PAGINATION_LIMIT_DEFAULT; + offset = offset != null ? offset : RestApiConstants.PAGINATION_OFFSET_DEFAULT; + query = query == null ? APIConstants.CHAR_ASTERIX : query; + APIAdmin apiAdmin = new APIAdminImpl(); + String organization = RestApiUtil.getOrganization(messageContext); + Map result = apiAdmin + .searchPaginatedApis(query, organization, offset, limit); + List apis = SearchApiServiceImplUtil.getAPIListFromAPISearchResult(result); + List allMatchedResults = getAllMatchedResults(apis); + resultListDTO.setApis(allMatchedResults); + resultListDTO.setCount(allMatchedResults.size()); + APIInfoMappingUtil.setPaginationParams(resultListDTO, limit, offset, (Integer) result + .get(APIConstants.ADMIN_API_LIST_RESPONSE_PARAMS_TOTAL)); + return Response.ok().entity(resultListDTO).build(); + } + + /** + * change API provider + * + * @param provider new provider name + * @param apiId API Id of the given API + * @param messageContext + * @return whether the api provider change successful or not + * @throws APIManagementException if there is any error when changing the API provider. + */ + public Response providerNamePost(String provider, String apiId, MessageContext messageContext) + throws APIManagementException { + String organisation = RestApiCommonUtil.getLoggedInUserTenantDomain(); + try { + if (!APIUtil.isUserExist(provider)) { + throw new APIManagementException("User " + provider + " not found.", + ExceptionCodes.USER_NOT_FOUND); + } + APIAdmin apiAdmin = new APIAdminImpl(); + apiAdmin.updateApiProvider(apiId, provider, organisation); + } catch (APIManagementException e) { + throw new APIManagementException("Error while changing the API provider", + ExceptionCodes.CHANGE_API_PROVIDER_FAILED); + } + return Response.ok().build(); + } + + /** + * Return the apis object as ApiResultDTO object. + * + * @param apis apis object + * @return ApiResultDTO object + */ + private List getAllMatchedResults(List apis) { + List allMatchedResults = new ArrayList<>(); + for (Object searchResult : apis) { + if (searchResult instanceof API) { + API api = (API) searchResult; + ApiResultDTO apiResult = APIInfoMappingUtil.fromAPIToAPIResultDTO(api); + allMatchedResults.add(apiResult); + } + } + return allMatchedResults; + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/admin/v1/utils/mappings/APIInfoMappingUtil.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/admin/v1/utils/mappings/APIInfoMappingUtil.java index d548d925deb7..c7ad8e99a620 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/admin/v1/utils/mappings/APIInfoMappingUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/admin/v1/utils/mappings/APIInfoMappingUtil.java @@ -17,16 +17,22 @@ package org.wso2.carbon.apimgt.rest.api.admin.v1.utils.mappings; +import org.wso2.carbon.apimgt.api.model.API; import org.wso2.carbon.apimgt.api.model.APIIdentifier; import org.wso2.carbon.apimgt.impl.utils.APIUtil; import org.wso2.carbon.apimgt.rest.api.admin.v1.dto.APIInfoDTO; import org.wso2.carbon.apimgt.rest.api.admin.v1.dto.APIInfoListDTO; +import org.wso2.carbon.apimgt.rest.api.admin.v1.dto.ApiResultDTO; +import org.wso2.carbon.apimgt.rest.api.admin.v1.dto.PaginationDTO; +import org.wso2.carbon.apimgt.rest.api.admin.v1.dto.SearchResultListDTO; +import org.wso2.carbon.apimgt.rest.api.common.RestApiCommonUtil; import org.wso2.carbon.apimgt.rest.api.common.RestApiConstants; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; +import java.util.Map; public class APIInfoMappingUtil { @@ -70,4 +76,47 @@ public static APIInfoListDTO fromAPIInfoListToDTO(List apiIds) th apiInfoListDTO.setCount(apiInfoDTOs.size()); return apiInfoListDTO; } + + public static void setPaginationParams(SearchResultListDTO apiListDTO, int limit, int offset, + int size) { + + Map paginatedParams = RestApiCommonUtil.getPaginationParams(offset, limit, size); + + String paginatedPrevious = ""; + String paginatedNext = ""; + + if (paginatedParams.get(RestApiConstants.PAGINATION_PREVIOUS_OFFSET) != null) { + paginatedPrevious = RestApiCommonUtil + .getApplicationPaginatedURL(paginatedParams.get(RestApiConstants.PAGINATION_PREVIOUS_OFFSET), + paginatedParams.get(RestApiConstants.PAGINATION_PREVIOUS_LIMIT), null); + } + + if (paginatedParams.get(RestApiConstants.PAGINATION_NEXT_OFFSET) != null) { + paginatedNext = RestApiCommonUtil + .getApplicationPaginatedURL(paginatedParams.get(RestApiConstants.PAGINATION_NEXT_OFFSET), + paginatedParams.get(RestApiConstants.PAGINATION_NEXT_LIMIT), null); + } + PaginationDTO paginationDTO = getPaginationDTO(limit, offset, size, paginatedNext, paginatedPrevious); + apiListDTO.setPagination(paginationDTO); + } + + private static PaginationDTO getPaginationDTO(int limit, int offset, int total, String next, String previous) { + PaginationDTO paginationDTO = new PaginationDTO(); + paginationDTO.setLimit(limit); + paginationDTO.setOffset(offset); + paginationDTO.setTotal(total); + paginationDTO.setNext(next); + paginationDTO.setPrevious(previous); + return paginationDTO; + } + + public static ApiResultDTO fromAPIToAPIResultDTO(API api) { + ApiResultDTO apiResultDTO = new ApiResultDTO(); + apiResultDTO.setId(api.getUuid()); + APIIdentifier apiId = api.getId(); + apiResultDTO.setName(apiId.getApiName()); + apiResultDTO.setVersion(apiId.getVersion()); + apiResultDTO.setProvider(apiId.getProviderName()); + return apiResultDTO; + } } diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/resources/admin-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/resources/admin-api.yaml index 8cb25f399fe1..d9813db4915c 100755 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/resources/admin-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/resources/admin-api.yaml @@ -3226,6 +3226,106 @@ paths: source: 'curl -k -X POST -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" -F "type=WSO2-IS" "https://127.0.0.1:9443/api/am/admin/v4/key-managers/discover"' + ###################################################### + # The "API Collection" resource APIs + ###################################################### + /apis: + get: + tags: + - APIs + summary: | + Retrieve/Search APIs + description: | + This operation provides you a list of available APIs qualifying under a given search condition. + Each retrieved API is represented with a minimal amount of attributes. If you want to get complete details of an API, you need to use **Get details of an API** operation. + parameters: + - $ref: '#/components/parameters/limit' + - $ref: '#/components/parameters/offset' + - name: query + in: query + description: | + **Search and get all apis in admin portal**. + + You can search by proving a keyword. + schema: + type: string + - $ref: '#/components/parameters/If-None-Match' + responses: + 200: + description: | + OK. + List of qualifying APIs is returned. + headers: + ETag: + description: | + Entity Tag of the response resource. Used by caches, or in conditional requests (Will be supported in future). + schema: + type: string + Content-Type: + description: The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/SearchResultList' + 304: + description: | + Not Modified. + Empty body because the client has already the latest version of the requested resource (Will be supported in future). + content: {} + 406: + $ref: '#/components/responses/NotAcceptable' + security: + - OAuth2Security: + - apim:admin + - apim:api_provider_change + x-code-samples: + - lang: Curl + source: 'curl -k -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://127.0.0.1:9443/api/am/publisher/v4/apis"' + operationId: getAllAPIs + ###################################################### + # Change Api Provider + ###################################################### + /apis/{apiId}/change-provider: + post: + tags: + - Api Provider Change + summary: Update the api provider + description: | + Update the api provider + operationId: providerNamePost + parameters: + - name: provider + in: query + required: true + schema: + type: string + - $ref: '#/components/parameters/apiId' + responses: + 200: + description: | + OK. + Api Provider updated. + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + 400: + $ref: '#/components/responses/BadRequest' + 404: + $ref: '#/components/responses/NotFound' + security: + - OAuth2Security: + - apim:admin + - apim:api_provider_change + x-code-samples: + - lang: Curl + source: 'curl -k -X PUT -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + -H "Content-Type: application/json" "https://127.0.0.1:9443/api/am/admin/v4/provider/admin/apis/33662a62-8db1-4d75-af08-afd63c6bd0b4"' components: schemas: Error: @@ -4952,6 +5052,55 @@ components: Link to the previous subset of resources qualified. Empty if current subset is the first subset returned. example: "" + PaginationApis: + title: Pagination + type: object + properties: + offset: + type: integer + example: 0 + limit: + type: integer + example: 1 + total: + type: integer + example: 10 + next: + type: string + description: | + Link to the next subset of resources qualified. + Empty if no more resources are to be returned. + previous: + type: string + description: | + Link to the previous subset of resources qualified. + Empty if current subset is the first subset returned. + SearchResultList: + title: Unified Search Result List + type: object + properties: + apis: + type: array + items: + $ref: '#/components/schemas/ApiResult' + count: + type: integer + description: | + Number of results returned. + example: 1 + pagination: + $ref: '#/components/schemas/Pagination' + ApiResult: + type: object + properties: + provider: + type: string + name: + type: string + version: + type: string + id: + type: string responses: BadRequest: description: Bad Request. Invalid request or validation error. @@ -5251,6 +5400,14 @@ components: required: true schema: type: string + apiId: + name: apiId + in: path + description: | + **API ID** consisting of the **UUID** of the API. + required: true + schema: + type: string securitySchemes: OAuth2Security: type: oauth2 @@ -5291,4 +5448,5 @@ components: apim:scope_manage: Manage system scopes apim:role_manage: Manage system roles apim:admin_application_view: View Applications - apim:keymanagers_manage: Manage Key Managers \ No newline at end of file + apim:keymanagers_manage: Manage Key Managers + apim:api_provider_change: Retrieve and manage applications \ No newline at end of file diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/webapp/WEB-INF/beans.xml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/webapp/WEB-INF/beans.xml index 6f7967ffacc4..dfcbcb151259 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/webapp/WEB-INF/beans.xml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.admin.v1/src/main/webapp/WEB-INF/beans.xml @@ -49,6 +49,7 @@ + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/java/org/wso2/carbon/apimgt/rest/api/common/RestApiConstants.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/java/org/wso2/carbon/apimgt/rest/api/common/RestApiConstants.java index 09bc187b95ca..c98caecabccd 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/java/org/wso2/carbon/apimgt/rest/api/common/RestApiConstants.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/java/org/wso2/carbon/apimgt/rest/api/common/RestApiConstants.java @@ -203,6 +203,7 @@ public final class RestApiConstants { public static final String ORG_ID = "ORG_ID"; public static final int PAGINATION_LIMIT_DEFAULT = 25; + public static final String PIZZASHACK_SEARCH_QUERY = "name:PizzaShackAPI version:1.0 context:pizzashack"; public static final int PAGINATION_OFFSET_DEFAULT = 0; public static final String PAGINATION_NEXT_OFFSET = "next_offset"; public static final String PAGINATION_NEXT_LIMIT = "next_limit"; diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/PublisherCommonUtils.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/PublisherCommonUtils.java index 88c1c5ac8b69..41252f0cb094 100755 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/PublisherCommonUtils.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/PublisherCommonUtils.java @@ -847,11 +847,8 @@ public static void validateScopes(API api) throws APIManagementException { public static API addAPIWithGeneratedSwaggerDefinition(APIDTO apiDto, String oasVersion, String username, String organization) throws APIManagementException, CryptoException { - if (APIUtil.isOnPremResolver()) { - String name = apiDto.getName(); - //replace all white spaces in the API Name - apiDto.setName(name.replaceAll("\\s+", "")); - } + String name = apiDto.getName(); + apiDto.setName(name.trim().replaceAll("\\s{2,}", " ")); if (APIDTO.TypeEnum.ASYNC.equals(apiDto.getType())) { throw new APIManagementException("ASYNC API type does not support API creation from scratch", ExceptionCodes.API_CREATION_NOT_SUPPORTED_FOR_ASYNC_TYPE_APIS); diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/SearchApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/SearchApiServiceImpl.java index 14619e862710..73d09820cc69 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/SearchApiServiceImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/SearchApiServiceImpl.java @@ -50,7 +50,10 @@ public class SearchApiServiceImpl implements SearchApiService { public Response search(Integer limit, Integer offset, String query, String ifNoneMatch, MessageContext messageContext) throws APIManagementException { SearchResultListDTO resultListDTO = new SearchResultListDTO(); - + if (!query.equals(RestApiConstants.PIZZASHACK_SEARCH_QUERY) && (query.startsWith("name:") | + query.startsWith("description:"))) { + query = query.replaceAll(" ", "%20"); + } limit = limit != null ? limit : RestApiConstants.PAGINATION_LIMIT_DEFAULT; offset = offset != null ? offset : RestApiConstants.PAGINATION_OFFSET_DEFAULT;