From 309b21cdfe3ab8d45dbb3f41fb94a2395749c838 Mon Sep 17 00:00:00 2001 From: Avishka-Shamendra Date: Thu, 4 Jul 2024 10:03:34 +0530 Subject: [PATCH 1/4] Add API Definition content search for REST,ASync and GraphQl APIs --- .../APIDefinitionContentSearchResult.java | 107 +++++++++ .../wso2/carbon/apimgt/impl/APIConstants.java | 5 +- .../carbon/apimgt/impl/APIConsumerImpl.java | 54 +++-- .../carbon/apimgt/impl/APIProviderImpl.java | 22 +- .../indexer/GraphQLAPIDefinitionIndexer.java | 89 ++++++++ .../RESTAsyncAPIDefinitionIndexer.java | 93 ++++++++ .../carbon/apimgt/impl/utils/IndexerUtil.java | 68 ++++++ ...mpleContentSearchResultNameComparator.java | 79 +++++++ .../apimgt/persistence/APIConstants.java | 6 +- .../persistence/RegistryPersistenceImpl.java | 106 ++++++++- .../persistence/dto/APIDefSearchContent.java | 137 ++++++++++++ .../persistence/utils/RegistrySearchUtil.java | 62 ++++-- .../src/main/resources/devportal-api.yaml | 33 +++ .../src/main/resources/publisher-api.yaml | 33 +++ .../APIDefinitionSearchResultAllOfDTO.java | 208 +++++++++++++++++ .../v1/dto/APIDefinitionSearchResultDTO.java | 210 ++++++++++++++++++ .../api/publisher/v1/dto/SearchResultDTO.java | 3 +- .../mappings/SearchResultMappingUtil.java | 31 +++ .../v1/impl/SearchApiServiceImpl.java | 6 + .../src/main/resources/publisher-api.yaml | 33 +++ .../APIDefinitionSearchResultAllOfDTO.java | 208 +++++++++++++++++ .../v1/dto/APIDefinitionSearchResultDTO.java | 210 ++++++++++++++++++ .../api/store/v1/dto/SearchResultDTO.java | 3 +- .../store/v1/impl/SearchApiServiceImpl.java | 6 + .../v1/mappings/SearchResultMappingUtil.java | 31 +++ .../src/main/resources/devportal-api.yaml | 33 +++ 26 files changed, 1824 insertions(+), 52 deletions(-) create mode 100644 components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/APIDefinitionContentSearchResult.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/GraphQLAPIDefinitionIndexer.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/RESTAsyncAPIDefinitionIndexer.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/IndexerUtil.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/SimpleContentSearchResultNameComparator.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/APIDefSearchContent.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/APIDefinitionSearchResultAllOfDTO.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/APIDefinitionSearchResultDTO.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/APIDefinitionSearchResultAllOfDTO.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/APIDefinitionSearchResultDTO.java diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/APIDefinitionContentSearchResult.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/APIDefinitionContentSearchResult.java new file mode 100644 index 000000000000..fa763f50e569 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/APIDefinitionContentSearchResult.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * 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 model is used to represent the API defintion content search results. + */ +public class APIDefinitionContentSearchResult { + + private String id; + private String name; + private String apiUuid; + private String apiName; + private String apiContext; + private String apiVersion; + private String apiProvider; + private String apiType; + private String associatedType; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getApiUuid() { + return apiUuid; + } + + public void setApiUuid(String apiUuid) { + this.apiUuid = apiUuid; + } + + public String getApiName() { + return apiName; + } + + public void setApiName(String apiName) { + this.apiName = apiName; + } + + public String getApiVersion() { + return apiVersion; + } + + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + public String getApiProvider() { + return apiProvider; + } + + public void setApiProvider(String apiProvider) { + this.apiProvider = apiProvider; + } + + public String getApiType() { + return apiType; + } + + public void setApiType(String apiType) { + this.apiType = apiType; + } + + public String getAssociatedType() { + return associatedType; + } + + public void setAssociatedType(String associatedType) { + this.associatedType = associatedType; + } + + public String getApiContext() { + return apiContext; + } + + public void setApiContext(String apiContext) { + this.apiContext = apiContext; + } +} 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 85c06021182c..e9306c64df34 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 @@ -1337,7 +1337,7 @@ public static class AccessTokenConstants { public static final String REST_METHOD = "REST_METHOD"; // GraphQL related constants - public static final String API_TYPE = "API_TYPE"; + public static final String API_TYPE = "ApiType"; public static final String HTTP_VERB = "HTTP_VERB"; public static final String GRAPHQL_API = "GRAPHQL"; public static final String GRAPHQL_SUBSCRIPTION_REQUEST = "isGraphqlSubscriptionRequest"; @@ -1826,6 +1826,9 @@ private ConfigParameters() { public static final String REGISTRY_RESOURCE_URL_PREFIX = "/registry/resource/_system/governance/apimgt/applicationdata/provider/"; + public static final String APPLICATION_DATA_RESOURCE_URL_PREFIX = + "/apimgt/applicationdata/provider/"; + public enum RegistryResourceTypesForUI { TAG_THUMBNAIL } diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java index 7ba7f75129f4..0ef6f00f8c63 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java @@ -46,6 +46,7 @@ import org.wso2.carbon.apimgt.api.dto.KeyManagerConfigurationDTO; import org.wso2.carbon.apimgt.api.dto.KeyManagerPermissionConfigurationDTO; import org.wso2.carbon.apimgt.api.model.API; +import org.wso2.carbon.apimgt.api.model.APIDefinitionContentSearchResult; import org.wso2.carbon.apimgt.api.model.APIIdentifier; import org.wso2.carbon.apimgt.api.model.APIKey; import org.wso2.carbon.apimgt.api.model.APIProduct; @@ -117,6 +118,7 @@ import org.wso2.carbon.apimgt.impl.utils.APIVersionComparator; import org.wso2.carbon.apimgt.impl.utils.ApplicationUtils; import org.wso2.carbon.apimgt.impl.utils.ContentSearchResultNameComparator; +import org.wso2.carbon.apimgt.impl.utils.SimpleContentSearchResultNameComparator; import org.wso2.carbon.apimgt.impl.utils.VHostUtils; import org.wso2.carbon.apimgt.impl.workflow.ApplicationDeletionApprovalWorkflowExecutor; import org.wso2.carbon.apimgt.impl.workflow.ApplicationRegistrationSimpleWorkflowExecutor; @@ -137,6 +139,7 @@ import org.wso2.carbon.apimgt.persistence.dto.DocumentSearchContent; import org.wso2.carbon.apimgt.persistence.dto.Organization; import org.wso2.carbon.apimgt.persistence.dto.SearchContent; +import org.wso2.carbon.apimgt.persistence.dto.APIDefSearchContent; import org.wso2.carbon.apimgt.persistence.dto.UserContext; import org.wso2.carbon.apimgt.persistence.exceptions.APIPersistenceException; import org.wso2.carbon.apimgt.persistence.exceptions.OASPersistenceException; @@ -4160,6 +4163,7 @@ public Map searchPaginatedContent(String searchQuery, String org Map result = new HashMap(); SortedSet apiSet = new TreeSet(new APINameComparator()); SortedSet apiProductSet = new TreeSet(new APIProductNameComparator()); + List defSearchList = new ArrayList<>(); int totalLength = 0; String userame = (userNameWithoutChange != null) ? userNameWithoutChange : username; @@ -4187,23 +4191,36 @@ public Map searchPaginatedContent(String searchQuery, String org docItem.getApiVersion())); api.setUuid(docItem.getApiUUID()); docMap.put(doc, api); + } else if (item instanceof APIDefSearchContent) { + APIDefSearchContent definitionItem = (APIDefSearchContent) item; + APIDefinitionContentSearchResult apiDefSearchResult = new APIDefinitionContentSearchResult(); + apiDefSearchResult.setId(definitionItem.getId()); + apiDefSearchResult.setName(definitionItem.getName()); + apiDefSearchResult.setApiUuid(definitionItem.getApiUUID()); + apiDefSearchResult.setApiName(definitionItem.getApiName()); + apiDefSearchResult.setApiContext(definitionItem.getApiContext()); + apiDefSearchResult.setApiProvider(definitionItem.getApiProvider()); + apiDefSearchResult.setApiVersion(definitionItem.getApiVersion()); + apiDefSearchResult.setApiType(definitionItem.getApiType()); + apiDefSearchResult.setAssociatedType(definitionItem.getAssociatedType()); //API or API product + defSearchList.add(apiDefSearchResult); } else if ("API".equals(item.getType())) { - DevPortalSearchContent publiserAPI = (DevPortalSearchContent) item; - API api = new API(new APIIdentifier(publiserAPI.getProvider(), publiserAPI.getName(), - publiserAPI.getVersion())); - api.setUuid(publiserAPI.getId()); - api.setContext(publiserAPI.getContext()); - api.setContextTemplate(publiserAPI.getContext()); - api.setStatus(publiserAPI.getStatus()); - api.setBusinessOwner(publiserAPI.getBusinessOwner()); - api.setBusinessOwnerEmail(publiserAPI.getBusinessOwnerEmail()); - api.setTechnicalOwner(publiserAPI.getTechnicalOwner()); - api.setTechnicalOwnerEmail(publiserAPI.getTechnicalOwnerEmail()); - api.setMonetizationEnabled(publiserAPI.getMonetizationStatus()); - api.setAdvertiseOnly(publiserAPI.getAdvertiseOnly()); - api.setRating(APIUtil.getAverageRating(publiserAPI.getId())); - api.setDescription(publiserAPI.getDescription()); - api.setType(publiserAPI.getTransportType()); + DevPortalSearchContent publisherAPI = (DevPortalSearchContent) item; + API api = new API(new APIIdentifier(publisherAPI.getProvider(), publisherAPI.getName(), + publisherAPI.getVersion())); + api.setUuid(publisherAPI.getId()); + api.setContext(publisherAPI.getContext()); + api.setContextTemplate(publisherAPI.getContext()); + api.setStatus(publisherAPI.getStatus()); + api.setBusinessOwner(publisherAPI.getBusinessOwner()); + api.setBusinessOwnerEmail(publisherAPI.getBusinessOwnerEmail()); + api.setTechnicalOwner(publisherAPI.getTechnicalOwner()); + api.setTechnicalOwnerEmail(publisherAPI.getTechnicalOwnerEmail()); + api.setMonetizationEnabled(publisherAPI.getMonetizationStatus()); + api.setAdvertiseOnly(publisherAPI.getAdvertiseOnly()); + api.setRating(APIUtil.getAverageRating(publisherAPI.getId())); + api.setDescription(publisherAPI.getDescription()); + api.setType(publisherAPI.getTransportType()); apiSet.add(api); } else if ("APIProduct".equals(item.getType())) { DevPortalSearchContent devAPIProduct = (DevPortalSearchContent) item; @@ -4226,10 +4243,11 @@ public Map searchPaginatedContent(String searchQuery, String org compoundResult.addAll(apiSet); compoundResult.addAll(docMap.entrySet()); compoundResult.addAll(apiProductSet); - compoundResult.sort(new ContentSearchResultNameComparator()); + compoundResult.addAll(defSearchList); + compoundResult.sort(new SimpleContentSearchResultNameComparator()); result.put("length", sResults.getTotalCount()); } else { - result.put("length", compoundResult.size()); + result.put("length", 0); } } catch (APIPersistenceException e) { throw new APIManagementException("Error while searching content ", e); diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIProviderImpl.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIProviderImpl.java index 11eb10bc9343..41f22328ae91 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIProviderImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIProviderImpl.java @@ -55,6 +55,7 @@ import org.wso2.carbon.apimgt.api.dto.EnvironmentPropertiesDTO; import org.wso2.carbon.apimgt.api.dto.UserApplicationAPIUsage; import org.wso2.carbon.apimgt.api.model.API; +import org.wso2.carbon.apimgt.api.model.APIDefinitionContentSearchResult; import org.wso2.carbon.apimgt.api.model.APIIdentifier; import org.wso2.carbon.apimgt.api.model.APIInfo; import org.wso2.carbon.apimgt.api.model.APIProduct; @@ -164,6 +165,7 @@ import org.wso2.carbon.apimgt.impl.utils.APIVersionStringComparator; import org.wso2.carbon.apimgt.impl.utils.ContentSearchResultNameComparator; import org.wso2.carbon.apimgt.impl.utils.LifeCycleUtils; +import org.wso2.carbon.apimgt.impl.utils.SimpleContentSearchResultNameComparator; import org.wso2.carbon.apimgt.impl.workflow.APIStateWorkflowDTO; import org.wso2.carbon.apimgt.impl.workflow.WorkflowConstants; import org.wso2.carbon.apimgt.impl.workflow.WorkflowException; @@ -171,6 +173,7 @@ import org.wso2.carbon.apimgt.impl.workflow.WorkflowExecutorFactory; import org.wso2.carbon.apimgt.impl.workflow.WorkflowStatus; import org.wso2.carbon.apimgt.impl.wsdl.WSDLProcessor; +import org.wso2.carbon.apimgt.persistence.dto.APIDefSearchContent; import org.wso2.carbon.apimgt.persistence.dto.DocumentContent; import org.wso2.carbon.apimgt.persistence.dto.DocumentSearchContent; import org.wso2.carbon.apimgt.persistence.dto.DocumentSearchResult; @@ -5594,6 +5597,7 @@ public Map searchPaginatedContent(String searchQuery, String org Map result = new HashMap(); SortedSet apiSet = new TreeSet(new APINameComparator()); SortedSet apiProductSet = new TreeSet(new APIProductNameComparator()); + List defSearchList = new ArrayList<>(); String userame = userNameWithoutChange; Organization org = new Organization(organization); @@ -5662,14 +5666,28 @@ public Map searchPaginatedContent(String searchQuery, String org api.setUuid(docItem.getApiUUID()); productDocMap.put(doc, api); } + } else if (item instanceof APIDefSearchContent) { + APIDefSearchContent definitionItem = (APIDefSearchContent) item; + APIDefinitionContentSearchResult apiDefSearchResult = new APIDefinitionContentSearchResult(); + apiDefSearchResult.setId(definitionItem.getId()); + apiDefSearchResult.setName(definitionItem.getName()); + apiDefSearchResult.setApiUuid(definitionItem.getApiUUID()); + apiDefSearchResult.setApiName(definitionItem.getApiName()); + apiDefSearchResult.setApiContext(definitionItem.getApiContext()); + apiDefSearchResult.setApiProvider(definitionItem.getApiProvider()); + apiDefSearchResult.setApiVersion(definitionItem.getApiVersion()); + apiDefSearchResult.setApiType(definitionItem.getApiType()); + apiDefSearchResult.setAssociatedType(definitionItem.getAssociatedType()); //API or API product + defSearchList.add(apiDefSearchResult); } } compoundResult.addAll(apiSet); compoundResult.addAll(apiProductSet); compoundResult.addAll(docMap.entrySet()); compoundResult.addAll(productDocMap.entrySet()); - compoundResult.sort(new ContentSearchResultNameComparator()); - result.put("length", results.getTotalCount() ); + compoundResult.addAll(defSearchList); + compoundResult.sort(new SimpleContentSearchResultNameComparator()); + result.put("length", results.getTotalCount()); } else { result.put("length", compoundResult.size() ); } diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/GraphQLAPIDefinitionIndexer.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/GraphQLAPIDefinitionIndexer.java new file mode 100644 index 000000000000..b8d975e4aef6 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/GraphQLAPIDefinitionIndexer.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * 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.impl.indexing.indexer; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.solr.common.SolrException; +import org.wso2.carbon.apimgt.api.APIManagementException; +import org.wso2.carbon.apimgt.impl.APIConstants; +import org.wso2.carbon.apimgt.impl.utils.IndexerUtil; +import org.wso2.carbon.governance.api.util.GovernanceUtils; +import org.wso2.carbon.registry.core.Registry; +import org.wso2.carbon.registry.core.RegistryConstants; +import org.wso2.carbon.registry.core.Resource; +import org.wso2.carbon.registry.core.exceptions.RegistryException; +import org.wso2.carbon.registry.indexing.AsyncIndexer; +import org.wso2.carbon.registry.indexing.IndexingManager; +import org.wso2.carbon.registry.indexing.solr.IndexDocument; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * This is the graphql definition indexer introduced to .graphgl definition artifacts for unified content search. + */ +public class GraphQLAPIDefinitionIndexer extends PlainTextIndexer { + public static final Log log = LogFactory.getLog(GraphQLAPIDefinitionIndexer.class); + + @Override + public IndexDocument getIndexedDocument(AsyncIndexer.File2Index fileData) throws SolrException, RegistryException { + Registry registry = GovernanceUtils + .getGovernanceSystemRegistry(IndexingManager.getInstance().getRegistry(fileData.tenantId)); + String definitionResourcePath = + fileData.path.substring(RegistryConstants.GOVERNANCE_REGISTRY_BASE_PATH.length()); + + // If the file is not a graphql definition file in provider path do not index + if (!definitionResourcePath.contains(APIConstants.APPLICATION_DATA_RESOURCE_URL_PREFIX) + || !definitionResourcePath.contains(APIConstants.GRAPHQL_SCHEMA_FILE_EXTENSION)) { + return null; + } + + IndexDocument indexDocument = super.getIndexedDocument(fileData); + IndexDocument newIndexDocument = indexDocument; + + if (log.isDebugEnabled()) { + log.debug("Executing GraphQL file indexer for resource at " + definitionResourcePath); + } + + Resource definitionResource = null; + Map> fields = indexDocument.getFields(); + + if (registry.resourceExists(definitionResourcePath)) { + definitionResource = registry.get(definitionResourcePath); + } + + if (definitionResource != null) { + try { + IndexerUtil.fetchRequiredDetailsFromAssociatedAPI(registry, definitionResource, fields); + newIndexDocument = + new IndexDocument(fileData.path, null, + indexDocument.getRawContent(), indexDocument.getTenantId()); + fields.put(APIConstants.DOCUMENT_INDEXER_INDICATOR, Arrays.asList("true")); + newIndexDocument.setFields(fields); + } catch (APIManagementException e) { + //error occurred while fetching details from API, but continuing indexing + log.error("Error while updating indexed document.", e); + } + } + + return newIndexDocument; + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/RESTAsyncAPIDefinitionIndexer.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/RESTAsyncAPIDefinitionIndexer.java new file mode 100644 index 000000000000..af6742c97b05 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/RESTAsyncAPIDefinitionIndexer.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * 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.impl.indexing.indexer; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.solr.common.SolrException; +import org.wso2.carbon.apimgt.api.APIManagementException; +import org.wso2.carbon.apimgt.impl.APIConstants; +import org.wso2.carbon.apimgt.impl.utils.IndexerUtil; +import org.wso2.carbon.governance.api.util.GovernanceUtils; +import org.wso2.carbon.registry.core.Registry; +import org.wso2.carbon.registry.core.RegistryConstants; +import org.wso2.carbon.registry.core.Resource; +import org.wso2.carbon.registry.core.exceptions.RegistryException; +import org.wso2.carbon.registry.indexing.AsyncIndexer.File2Index; +import org.wso2.carbon.registry.indexing.IndexingManager; +import org.wso2.carbon.registry.indexing.indexer.JSONIndexer; +import org.wso2.carbon.registry.indexing.solr.IndexDocument; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * This is indexer introduced to index swagger,async api artifacts for unified content search. + */ +public class RESTAsyncAPIDefinitionIndexer extends JSONIndexer { + public static final Log log = LogFactory.getLog(RESTAsyncAPIDefinitionIndexer.class); + + @Override + public IndexDocument getIndexedDocument(File2Index fileData) throws SolrException, RegistryException { + Registry registry = GovernanceUtils + .getGovernanceSystemRegistry(IndexingManager.getInstance().getRegistry(fileData.tenantId)); + String definitionResourcePath = fileData.path + .substring(RegistryConstants.GOVERNANCE_REGISTRY_BASE_PATH.length()); + + // If the file is not a def file in provider path, do not index + if (!definitionResourcePath.contains(APIConstants.APPLICATION_DATA_RESOURCE_URL_PREFIX) + || !(definitionResourcePath.contains(APIConstants.OPENAPI_MASTER_JSON) + || definitionResourcePath.contains(APIConstants.API_ASYNCAPI_DEFINITION_RESOURCE_NAME))) { + return null; + } + + IndexDocument indexDocument = super.getIndexedDocument(fileData); + IndexDocument newIndexDocument = indexDocument; + + if (log.isDebugEnabled()) { + log.debug("Executing json api definition indexer for resource at " + definitionResourcePath); + } + + Resource resource = null; + Map> fields = indexDocument.getFields(); + + if (registry.resourceExists(definitionResourcePath)) { + resource = registry.get(definitionResourcePath); + } + + if (resource != null) { + try { + IndexerUtil.fetchRequiredDetailsFromAssociatedAPI(registry, resource, fields); + newIndexDocument = + new IndexDocument(fileData.path, null, + indexDocument.getRawContent(), indexDocument.getTenantId()); + fields.put(APIConstants.DOCUMENT_INDEXER_INDICATOR, Arrays.asList("true")); + newIndexDocument.setFields(fields); + } catch (APIManagementException e) { + //error occurred while fetching details from API, but continuing indexing + log.error("Error while updating indexed document.", e); + } + } + + return newIndexDocument; + } + + +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/IndexerUtil.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/IndexerUtil.java new file mode 100644 index 000000000000..eca6b3e55171 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/IndexerUtil.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * 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.impl.utils; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.apimgt.api.APIManagementException; +import org.wso2.carbon.apimgt.impl.APIConstants; +import org.wso2.carbon.governance.api.generic.GenericArtifactManager; +import org.wso2.carbon.governance.api.generic.dataobjects.GenericArtifact; +import org.wso2.carbon.registry.core.Registry; +import org.wso2.carbon.registry.core.Resource; +import org.wso2.carbon.registry.core.exceptions.RegistryException; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Utility class for Indexers. + */ +public class IndexerUtil { + private static final Log log = LogFactory.getLog(IndexerUtil.class); + + /** + * Method to fetch API details for a given resource. + * + * @param registry Registry + * @param resource Resource + * @param fields Fields list + * @throws RegistryException on failure + * @throws APIManagementException on failure + */ + public static void fetchRequiredDetailsFromAssociatedAPI(Registry registry, Resource resource, + Map> fields) + throws RegistryException, APIManagementException { + String resourceFilePath = resource.getPath(); + String apiPath = resourceFilePath.substring(0, resourceFilePath.lastIndexOf('/') + 1) + + APIConstants.API_KEY; + if (registry.resourceExists(apiPath)) { + Resource apiResource = registry.get(apiPath); + GenericArtifactManager apiArtifactManager = APIUtil.getArtifactManager(registry, APIConstants.API_KEY); + GenericArtifact apiArtifact = apiArtifactManager.getGenericArtifact(apiResource.getUUID()); + String apiStatus = apiArtifact.getAttribute(APIConstants.API_OVERVIEW_STATUS).toLowerCase(); + String publisherRoles = apiResource.getProperty(APIConstants.PUBLISHER_ROLES); + fields.put(APIConstants.API_OVERVIEW_STATUS, Arrays.asList(apiStatus)); + fields.put(APIConstants.PUBLISHER_ROLES, Arrays.asList(publisherRoles)); + } else { + log.warn("API does not exist at " + apiPath); + } + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/SimpleContentSearchResultNameComparator.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/SimpleContentSearchResultNameComparator.java new file mode 100644 index 000000000000..511c71283804 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/SimpleContentSearchResultNameComparator.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * 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.impl.utils; + +import org.wso2.carbon.apimgt.api.model.API; +import org.wso2.carbon.apimgt.api.model.APIDefinitionContentSearchResult; +import org.wso2.carbon.apimgt.api.model.APIProduct; +import org.wso2.carbon.apimgt.api.model.Documentation; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * This new class can be used instead of old ContentSearchResultNameComparator. This is written in an expandable + * manner unlike ContentSearchResultNameComparator class. + */ +public class SimpleContentSearchResultNameComparator implements Comparator { + + APINameComparator nameComparator = new APINameComparator(); + APIProductNameComparator productNameComparator = new APIProductNameComparator(); + + @Override + public int compare(Object o1, Object o2) { + + // Handle simple API, APIProduct comparisons + if (o1 instanceof API && o2 instanceof API) { + API api1 = (API) o1; + API api2 = (API) o2; + return nameComparator.compare(api1, api2); + } else if (o1 instanceof APIProduct && o2 instanceof APIProduct) { + APIProduct apiProduct1 = (APIProduct) o1; + APIProduct apiProduct2 = (APIProduct) o2; + return productNameComparator.compare(apiProduct1, apiProduct2); + } + + // Handle other comparisons + Object[] objects = {o1, o2}; + List names = new ArrayList<>(); + + for (Object obj : objects) { + if (obj instanceof API) { + API api = (API) obj; + names.add(api.getId().getName()); + } else if (obj instanceof APIProduct) { + APIProduct product = (APIProduct) obj; + names.add(product.getId().getName()); + } else if (obj instanceof APIDefinitionContentSearchResult) { + APIDefinitionContentSearchResult defSearch = (APIDefinitionContentSearchResult) obj; + names.add(defSearch.getApiName()); + } else if (obj instanceof Map.Entry) { + Map.Entry entry = (Map.Entry) obj; + if (entry.getKey() instanceof Documentation) { + Map.Entry docEntry = (Map.Entry) entry; + names.add(docEntry.getKey().getName()); + } + } + } + return names.get(0).compareToIgnoreCase(names.get(1)); + + } +} 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 48334184b9e9..69f6a8745d8a 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 @@ -363,14 +363,16 @@ public enum APITransportType { public static final String USER_CTX_PROPERTY_ISADMIN = "isAdmin"; public static final String USER_CTX_PROPERTY_SKIP_ROLES = "skipRoles"; public static final String API = "API"; - + public static final String API_CUSTOM_SEQUENCE_TYPE_IN = "in"; public static final String API_CUSTOM_SEQUENCE_TYPE_OUT = "out"; public static final String API_CUSTOM_SEQUENCE_TYPE_FAULT = "fault"; - + public static final String GRAPHQL_SCHEMA_FILE_EXTENSION = ".graphql"; public static final String GRAPHQL_LOCAL_ENTRY_EXTENSION = "_graphQL"; public static final String GRAPHQL_SCHEMA_PROVIDER_SEPERATOR = "--"; + public static final String GRAPHQL_API_TYPE = "GRAPHQL"; + public static final String GRAPHQL_DEFINITION_MEDIA_TYPE = "text/plain; charset=ISO-8859-1"; public static final String ALLOW_MULTIPLE_STATUS = "allowMultipleStatus"; public static final String ALLOW_MULTIPLE_VERSIONS = "allowMultipleVersions"; 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 3d49b9c429cd..bc75c23d536c 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 @@ -591,7 +591,31 @@ public PublisherAPI updateAPI(Organization org, PublisherAPI publisherAPI) throw RegistryPersistenceUtil.clearResourcePermissions(resourcePath, api.getId(), ((UserRegistry) registry).getTenantId()); RegistryPersistenceUtil.setResourcePermissions(api.getId().getProviderName(), api.getVisibility(), - visibleRoles, resourcePath); + visibleRoles, resourcePath, registry); + } + + // Update .graphgl and .asyncapi file permissions, required for API definition content search functionality + if (APIConstants.GRAPHQL_API_TYPE.equals(api.getType())) { + String resourcePath = RegistryPersistenceUtil.getOpenAPIDefinitionFilePath(api.getId().getName(), + api.getId().getVersion(), api.getId().getProviderName()); + resourcePath += api.getId().getProviderName() + APIConstants.GRAPHQL_SCHEMA_PROVIDER_SEPERATOR + + api.getId().getName() + api.getId().getVersion() + APIConstants.GRAPHQL_SCHEMA_FILE_EXTENSION; + if (registry.resourceExists(resourcePath)) { + RegistryPersistenceUtil.clearResourcePermissions(resourcePath, api.getId(), + ((UserRegistry) registry).getTenantId()); + RegistryPersistenceUtil.setResourcePermissions(api.getId().getProviderName(), api.getVisibility(), + visibleRoles, resourcePath, registry); + } + } else if (api.isAsync()) { + String resourcePath = RegistryPersistenceUtil.getOpenAPIDefinitionFilePath(api.getId().getName(), + api.getId().getVersion(), api.getId().getProviderName()); + resourcePath += APIConstants.API_ASYNC_API_DEFINITION_RESOURCE_NAME; + if (registry.resourceExists(resourcePath)) { + RegistryPersistenceUtil.clearResourcePermissions(resourcePath, api.getId(), + ((UserRegistry) registry).getTenantId()); + RegistryPersistenceUtil.setResourcePermissions(api.getId().getProviderName(), api.getVisibility(), + visibleRoles, resourcePath, registry); + } } // doc visibility change @@ -1571,6 +1595,10 @@ public PublisherContentSearchResult searchContentForPublisher(Organization org, throw new GovernanceException("artifact id is null of " + apiPath); } + } else if (APIConstants.APPLICATION_JSON_MEDIA_TYPE.equals(resource.getMediaType()) || + APIConstants.GRAPHQL_DEFINITION_MEDIA_TYPE.equals(resource.getMediaType())) { + + addAPIDefinitionSearchContent(resourcePath, registry, apiArtifactManager, contentData); } else { String apiArtifactId = resource.getUUID(); //API api; @@ -1727,6 +1755,10 @@ public DevPortalContentSearchResult searchContentForDevPortal(Organization org, throw new GovernanceException("artifact id is null of " + apiPath); } + } else if (APIConstants.APPLICATION_JSON_MEDIA_TYPE.equals(resource.getMediaType()) || + APIConstants.GRAPHQL_DEFINITION_MEDIA_TYPE.equals(resource.getMediaType())) { + + addAPIDefinitionSearchContent(resourcePath, registry, apiArtifactManager, contentData); } else { String apiArtifactId = resource.getUUID(); if (apiArtifactId != null) { @@ -1835,7 +1867,8 @@ public void saveWSDL(Organization org, String apiId, ResourceFile wsdlResourceFi if (visibleRolesList != null) { visibleRoles = visibleRolesList.split(","); } - RegistryPersistenceUtil.setResourcePermissions(apiProviderName, visibility, visibleRoles, wsdlResourcePath); + RegistryPersistenceUtil.setResourcePermissions(apiProviderName, visibility, visibleRoles, + wsdlResourcePath, registry); if (isZip) { //Delete any WSDL file if exists @@ -1986,7 +2019,8 @@ public void saveOASDefinition(Organization org, String apiId, String apiDefiniti // Need to set anonymous if the visibility is public RegistryPersistenceUtil.clearResourcePermissions(resourcePath, new APIIdentifier(apiProviderName, apiName, apiVersion), ((UserRegistry) registry).getTenantId()); - RegistryPersistenceUtil.setResourcePermissions(apiProviderName, visibility, visibleRolesArr, resourcePath); + RegistryPersistenceUtil.setResourcePermissions(apiProviderName, visibility, visibleRolesArr, + resourcePath, registry); } catch (RegistryException | APIPersistenceException | APIManagementException e) { throw new OASPersistenceException("Error while adding OSA Definition for " + apiId, e); @@ -2083,7 +2117,8 @@ public void saveAsyncDefinition(Organization org, String apiId, String apiDefini RegistryPersistenceUtil .clearResourcePermissions(resourcePath, new APIIdentifier(apiProviderName, apiName, apiVersion), ((UserRegistry) registry).getTenantId()); - RegistryPersistenceUtil.setResourcePermissions(apiProviderName, visibility, visibleRolesArr, resourcePath); + RegistryPersistenceUtil.setResourcePermissions(apiProviderName, visibility, visibleRolesArr, resourcePath + , registry); } catch (RegistryException | APIPersistenceException | APIManagementException e) { throw new AsyncSpecPersistenceException("Error while adding AsyncApi Definition for " + apiId, e); @@ -2174,7 +2209,7 @@ public void saveGraphQLSchemaDefinition(Organization org, String apiId, String s new APIIdentifier(api.apiProvider, api.apiName, api.apiVersion), ((UserRegistry) registry).getTenantId()); RegistryPersistenceUtil.setResourcePermissions(api.apiProvider, api.visibility, api.visibleRoles, - saveResourcePath); + saveResourcePath, registry); } catch (RegistryException | APIManagementException | APIPersistenceException e) { throw new GraphQLPersistenceException("Error while adding Graphql Definition for api " + apiId, e); @@ -3981,7 +4016,7 @@ public void updateSoapToRestSequences(Organization org, String apiId, List contentData) + throws APIPersistenceException, RegistryException { + APIDefSearchContent content = new APIDefSearchContent(); + int index; + + if (resourcePath.contains(APIConstants.API_ASYNC_API_DEFINITION_RESOURCE_NAME)) { + index = resourcePath.indexOf(APIConstants.API_ASYNC_API_DEFINITION_RESOURCE_NAME); + content.setApiType(APIDefSearchContent.ApiType.ASYNC); + } else if (resourcePath.contains(APIConstants.GRAPHQL_SCHEMA_FILE_EXTENSION)) { + index = resourcePath.lastIndexOf('/') + 1; + content.setApiType(APIDefSearchContent.ApiType.GRAPHQL); + } else { + index = resourcePath.indexOf(APIConstants.API_OAS_DEFINITION_RESOURCE_NAME); + content.setApiType(APIDefSearchContent.ApiType.REST); + } + + String apiPath = resourcePath.substring(0, index) + APIConstants.API_KEY; + Resource apiResource = registry.get(apiPath); + Resource defResource = registry.get(resourcePath); + String apiArtifactId = apiResource.getUUID(); + String defResourceId = defResource.getUUID(); + String defResourceName = defResource.getId().substring(defResource.getId().lastIndexOf('/') + 1); + DevPortalAPI devAPI; + + if (apiArtifactId != null) { + GenericArtifact apiArtifact = apiArtifactManager.getGenericArtifact(apiArtifactId); + devAPI = RegistryPersistenceUtil.getDevPortalAPIForSearch(apiArtifact); + content.setId(defResourceId); + content.setName(defResourceName); + content.setApiUUID(devAPI.getId()); + content.setApiName(devAPI.getApiName()); + content.setApiContext(devAPI.getContext()); + content.setApiProvider(devAPI.getProviderName()); + content.setApiVersion(devAPI.getVersion()); + if (apiArtifact.getAttribute(APIConstants.API_OVERVIEW_TYPE) + .equals(APIConstants.AuditLogConstants.API_PRODUCT)) { + content.setAssociatedType(APIConstants.API_PRODUCT); + } else { + content.setAssociatedType(APIConstants.API); + } + contentData.add(content); + } else { + throw new GovernanceException("artifact id is null of " + apiPath); + } + + } } diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/APIDefSearchContent.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/APIDefSearchContent.java new file mode 100644 index 000000000000..ba3bfee763e8 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/APIDefSearchContent.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * 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; + +/** + * This hold swagger content search result content. + */ +public class APIDefSearchContent implements SearchContent { + + String id; + String type = "DEFINITION"; + String name; // Definition file name + String apiName; + String apiContext; + String apiVersion; + String apiProvider; + String apiUUID; + String apiRating; + ApiType apiType; + String associatedType = "API"; + + /** + * Holds different API Types for content search. + */ + public enum ApiType { + REST, + ASYNC, + GRAPHQL, + SOAP + } + + @Override + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getApiName() { + return apiName; + } + + public void setApiName(String apiName) { + this.apiName = apiName; + } + + public String getApiVersion() { + return apiVersion; + } + + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + public String getApiProvider() { + return apiProvider; + } + + public void setApiProvider(String apiProvider) { + this.apiProvider = apiProvider; + } + + public String getApiUUID() { + return apiUUID; + } + + public void setApiUUID(String apiUUID) { + this.apiUUID = apiUUID; + } + + public String getAssociatedType() { + return associatedType; + } + + public void setAssociatedType(String associatedType) { + this.associatedType = associatedType; + } + + public String getApiType() { + return apiType.toString(); + } + + public void setApiType(ApiType apiType) { + this.apiType = apiType; + } + + public String getApiContext() { + return apiContext; + } + + public void setApiContext(String apiContext) { + this.apiContext = apiContext; + } + + public String getApiRating() { + return apiRating; + } + + public void setApiRating(String apiRating) { + this.apiRating = apiRating; + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistrySearchUtil.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistrySearchUtil.java index 7d95b95cc65d..e7e0ba0f06a4 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistrySearchUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistrySearchUtil.java @@ -39,6 +39,7 @@ import static org.wso2.carbon.apimgt.persistence.APIConstants.API_GLOBAL_VISIBILITY; import static org.wso2.carbon.apimgt.persistence.APIConstants.API_OVERVIEW_KEY_MANAGERS; import static org.wso2.carbon.apimgt.persistence.APIConstants.API_OVERVIEW_VISIBILITY; +import static org.wso2.carbon.apimgt.persistence.APIConstants.APPLICATION_JSON_MEDIA_TYPE; public class RegistrySearchUtil { @@ -53,15 +54,21 @@ public class RegistrySearchUtil { public static final String API_STATUS = "STATUS"; public static final String API_PROVIDER = "Provider"; public static final String DOCUMENT_INDEXER = "org.wso2.carbon.apimgt.impl.indexing.indexer.DocumentIndexer"; + public static final String REST_ASYNC_API_DEFINITION_INDEXER = "org.wso2.carbon.apimgt.impl.indexing.indexer" + + ".RESTAsyncAPIDefinitionIndexer"; + public static final String GRAPHQL_DEFINITION_INDEXER = "org.wso2.carbon.apimgt.impl.indexing.indexer" + + ".GraphQLAPIDefinitionIndexer"; public static final String STORE_VIEW_ROLES = "store_view_roles"; public static final String PUBLISHER_ROLES = "publisher_roles"; public static final String DOCUMENT_MEDIA_TYPE_KEY = "application/vnd.wso2-document\\+xml"; - public static final String DOCUMENT_INDEXER_INDICATOR = "document_indexed"; - public static final String DOCUMENTATION_SEARCH_MEDIA_TYPE_FIELD = "mediaType"; + public static final String API_DEF_MEDIA_TYPE_KEY = "application/json"; + public static final String GRAPHQL_DEF_MEDIA_TYPE_KEY = "text/plain(.)+charset=ISO-8859-1"; + public static final String SEARCH_MEDIA_TYPE_FIELD = "mediaType"; public static final String DOCUMENTATION_INLINE_CONTENT_TYPE = "text/plain"; public static final String API_RXT_MEDIA_TYPE = "application/vnd.wso2-api+xml"; public static final String LCSTATE_SEARCH_KEY = "lcState"; public static final String DOCUMENT_RXT_MEDIA_TYPE = "application/vnd.wso2-document+xml"; + public static final String GRAPHQL_DEFINITION_MEDIA_TYPE = "text/plain; charset=ISO-8859-1"; public static final String API_OVERVIEW_STATUS = "overview_status"; public static final String API_RELATED_CUSTOM_PROPERTIES_PREFIX = "api_meta."; public static final String API_RELATED_CUSTOM_PROPERTIES_DISPLAY_DEV = "__display"; @@ -250,33 +257,46 @@ private static Map getSearchAttributes(String searchQuery) { RegistryConfigLoader registryConfig = RegistryConfigLoader.getInstance(); Map indexerMap = registryConfig.getIndexerMap(); Indexer documentIndexer = indexerMap.get(DOCUMENT_MEDIA_TYPE_KEY); - String complexAttribute; - if (documentIndexer != null && DOCUMENT_INDEXER.equals(documentIndexer.getClass().getName())) { - //field check on document_indexed was added to prevent unindexed(by new DocumentIndexer) from coming up as search results - //on indexed documents this property is always set to true - complexAttribute = ClientUtils.escapeQueryChars(API_RXT_MEDIA_TYPE) + " OR mediaType_s:(" + ClientUtils - .escapeQueryChars(DOCUMENT_RXT_MEDIA_TYPE) + " AND document_indexed_s:true)"; + Indexer jsonIndexer = indexerMap.get(API_DEF_MEDIA_TYPE_KEY); + Indexer graphqlIndexer = indexerMap.get(GRAPHQL_DEF_MEDIA_TYPE_KEY); + String complexAttribute = ClientUtils.escapeQueryChars(API_RXT_MEDIA_TYPE); + if (!StringUtils.isEmpty(publisherRoles)) { + complexAttribute = + "(" + ClientUtils.escapeQueryChars(API_RXT_MEDIA_TYPE) + " AND publisher_roles_ss:" + + publisherRoles + ")"; + } - //construct query such that publisher roles is checked in properties for api artifacts and in fields for document artifacts - //this was designed this way so that content search can be fully functional if registry is re-indexed after engaging DocumentIndexer + if (documentIndexer != null && DOCUMENT_INDEXER.equals(documentIndexer.getClass().getName())) { if (!StringUtils.isEmpty(publisherRoles)) { - complexAttribute = - "(" + ClientUtils.escapeQueryChars(API_RXT_MEDIA_TYPE) + " AND publisher_roles_ss:" - + publisherRoles + ") OR mediaType_s:(" + ClientUtils - .escapeQueryChars(DOCUMENT_RXT_MEDIA_TYPE) + " AND publisher_roles_s:" + publisherRoles + ")"; + complexAttribute += " OR mediaType_s:(" + ClientUtils + .escapeQueryChars(DOCUMENT_RXT_MEDIA_TYPE) + " AND publisher_roles_s:" + publisherRoles + ")"; + } else { + complexAttribute += " OR mediaType_s:(" + ClientUtils + .escapeQueryChars(DOCUMENT_RXT_MEDIA_TYPE) + " AND document_indexed_s:true)"; } - } else { - //document indexer required for document content search is not engaged, therefore carry out the search only for api artifact contents - complexAttribute = ClientUtils.escapeQueryChars(API_RXT_MEDIA_TYPE); + } + + if (jsonIndexer != null && REST_ASYNC_API_DEFINITION_INDEXER.equals(jsonIndexer.getClass().getName())) { if (!StringUtils.isEmpty(publisherRoles)) { - complexAttribute = - "(" + ClientUtils.escapeQueryChars(API_RXT_MEDIA_TYPE) + " AND publisher_roles_ss:" - + publisherRoles + ")"; + complexAttribute += " OR mediaType_s:(" + ClientUtils + .escapeQueryChars(APPLICATION_JSON_MEDIA_TYPE) + " AND publisher_roles_s:" + publisherRoles + ")"; + } else { + complexAttribute += " OR mediaType_s:(" + ClientUtils + .escapeQueryChars(APPLICATION_JSON_MEDIA_TYPE) + " AND document_indexed_s:true)"; } } + if (graphqlIndexer != null && GRAPHQL_DEFINITION_INDEXER.equals(graphqlIndexer.getClass().getName())) { + if (!StringUtils.isEmpty(publisherRoles)) { + complexAttribute += " OR mediaType_s:(" + ClientUtils + .escapeQueryChars(GRAPHQL_DEFINITION_MEDIA_TYPE) + " AND publisher_roles_s:" + publisherRoles + ")"; + } else { + complexAttribute += " OR mediaType_s:(" + ClientUtils + .escapeQueryChars(GRAPHQL_DEFINITION_MEDIA_TYPE) + " AND document_indexed_s:true)"; + } + } - attributes.put(DOCUMENTATION_SEARCH_MEDIA_TYPE_FIELD, complexAttribute); + attributes.put(SEARCH_MEDIA_TYPE_FIELD, complexAttribute); attributes.put(API_OVERVIEW_STATUS, apiState); return attributes; } diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/devportal-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/devportal-api.yaml index b99d43642ed8..90a501b43f27 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/devportal-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/devportal-api.yaml @@ -5320,6 +5320,7 @@ components: - DOC - API - APIProduct + - DEFINITION transportType: type: string description: Accepted values are HTTP, WS, SOAPTOREST, GRAPHQL @@ -5420,6 +5421,38 @@ components: example: admin apiUUID: type: string + APIDefinitionSearchResult: + title: API Definition Search Result + allOf: + - $ref: '#/components/schemas/SearchResult' + - properties: + apiName: + type: string + description: The name of the associated API + example: TestAPI + apiVersion: + type: string + description: The version of the associated API + example: 1.0.0 + apiContext: + type: string + description: The context of the associated API + example: /test + apiUUID: + type: string + description: The UUID of the associated API + apiProvider: + type: string + description: The provider name of the associated API + example: publisher + apiType: + type: string + description: The type of the associated API + example: REST + associatedType: + type: string + description: API or APIProduct + example: API CommenterInfo: type: object properties: diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml index 3d0ff1a8a44c..0183499f3c00 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml @@ -12266,6 +12266,7 @@ components: - DOC - API - APIProduct + - DEFINITION transportType: type: string description: Accepted values are HTTP, WS, SOAPTOREST, GRAPHQL @@ -12446,6 +12447,38 @@ components: type: string associatedType: type: string + APIDefinitionSearchResult: + title: API Definition Search Result + allOf: + - $ref: '#/components/schemas/SearchResult' + - properties: + apiName: + type: string + description: The name of the associated API + example: TestAPI + apiVersion: + type: string + description: The version of the associated API + example: 1.0.0 + apiContext: + type: string + description: The context of the associated API + example: /test + apiUUID: + type: string + description: The UUID of the associated API + apiProvider: + type: string + description: The provider name of the associated API + example: publisher + apiType: + type: string + description: The type of the associated API + example: REST + associatedType: + type: string + description: API or APIProduct + example: API MockResponsePayloadList: title: Mock Response Payload list type: object diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/APIDefinitionSearchResultAllOfDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/APIDefinitionSearchResultAllOfDTO.java new file mode 100644 index 000000000000..16b275098de6 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/APIDefinitionSearchResultAllOfDTO.java @@ -0,0 +1,208 @@ +package org.wso2.carbon.apimgt.rest.api.publisher.v1.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; + +import javax.xml.bind.annotation.*; +import org.wso2.carbon.apimgt.rest.api.common.annotations.Scope; +import com.fasterxml.jackson.annotation.JsonCreator; + +import javax.validation.Valid; + + + +public class APIDefinitionSearchResultAllOfDTO { + + private String apiName = null; + private String apiVersion = null; + private String apiContext = null; + private String apiUUID = null; + private String apiProvider = null; + private String apiType = null; + private String associatedType = null; + + /** + * The name of the associated API + **/ + public APIDefinitionSearchResultAllOfDTO apiName(String apiName) { + this.apiName = apiName; + return this; + } + + + @ApiModelProperty(example = "TestAPI", value = "The name of the associated API") + @JsonProperty("apiName") + public String getApiName() { + return apiName; + } + public void setApiName(String apiName) { + this.apiName = apiName; + } + + /** + * The version of the associated API + **/ + public APIDefinitionSearchResultAllOfDTO apiVersion(String apiVersion) { + this.apiVersion = apiVersion; + return this; + } + + + @ApiModelProperty(example = "1.0.0", value = "The version of the associated API") + @JsonProperty("apiVersion") + public String getApiVersion() { + return apiVersion; + } + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + /** + * The context of the associated API + **/ + public APIDefinitionSearchResultAllOfDTO apiContext(String apiContext) { + this.apiContext = apiContext; + return this; + } + + + @ApiModelProperty(example = "/test", value = "The context of the associated API") + @JsonProperty("apiContext") + public String getApiContext() { + return apiContext; + } + public void setApiContext(String apiContext) { + this.apiContext = apiContext; + } + + /** + * The UUID of the associated API + **/ + public APIDefinitionSearchResultAllOfDTO apiUUID(String apiUUID) { + this.apiUUID = apiUUID; + return this; + } + + + @ApiModelProperty(value = "The UUID of the associated API") + @JsonProperty("apiUUID") + public String getApiUUID() { + return apiUUID; + } + public void setApiUUID(String apiUUID) { + this.apiUUID = apiUUID; + } + + /** + * The provider name of the associated API + **/ + public APIDefinitionSearchResultAllOfDTO apiProvider(String apiProvider) { + this.apiProvider = apiProvider; + return this; + } + + + @ApiModelProperty(example = "publisher", value = "The provider name of the associated API") + @JsonProperty("apiProvider") + public String getApiProvider() { + return apiProvider; + } + public void setApiProvider(String apiProvider) { + this.apiProvider = apiProvider; + } + + /** + * The type of the associated API + **/ + public APIDefinitionSearchResultAllOfDTO apiType(String apiType) { + this.apiType = apiType; + return this; + } + + + @ApiModelProperty(example = "REST", value = "The type of the associated API") + @JsonProperty("apiType") + public String getApiType() { + return apiType; + } + public void setApiType(String apiType) { + this.apiType = apiType; + } + + /** + * API or APIProduct + **/ + public APIDefinitionSearchResultAllOfDTO associatedType(String associatedType) { + this.associatedType = associatedType; + return this; + } + + + @ApiModelProperty(example = "API", value = "API or APIProduct") + @JsonProperty("associatedType") + public String getAssociatedType() { + return associatedType; + } + public void setAssociatedType(String associatedType) { + this.associatedType = associatedType; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + APIDefinitionSearchResultAllOfDTO apIDefinitionSearchResultAllOf = (APIDefinitionSearchResultAllOfDTO) o; + return Objects.equals(apiName, apIDefinitionSearchResultAllOf.apiName) && + Objects.equals(apiVersion, apIDefinitionSearchResultAllOf.apiVersion) && + Objects.equals(apiContext, apIDefinitionSearchResultAllOf.apiContext) && + Objects.equals(apiUUID, apIDefinitionSearchResultAllOf.apiUUID) && + Objects.equals(apiProvider, apIDefinitionSearchResultAllOf.apiProvider) && + Objects.equals(apiType, apIDefinitionSearchResultAllOf.apiType) && + Objects.equals(associatedType, apIDefinitionSearchResultAllOf.associatedType); + } + + @Override + public int hashCode() { + return Objects.hash(apiName, apiVersion, apiContext, apiUUID, apiProvider, apiType, associatedType); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class APIDefinitionSearchResultAllOfDTO {\n"); + + sb.append(" apiName: ").append(toIndentedString(apiName)).append("\n"); + sb.append(" apiVersion: ").append(toIndentedString(apiVersion)).append("\n"); + sb.append(" apiContext: ").append(toIndentedString(apiContext)).append("\n"); + sb.append(" apiUUID: ").append(toIndentedString(apiUUID)).append("\n"); + sb.append(" apiProvider: ").append(toIndentedString(apiProvider)).append("\n"); + sb.append(" apiType: ").append(toIndentedString(apiType)).append("\n"); + sb.append(" associatedType: ").append(toIndentedString(associatedType)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/APIDefinitionSearchResultDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/APIDefinitionSearchResultDTO.java new file mode 100644 index 000000000000..00bf73e57c5b --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/APIDefinitionSearchResultDTO.java @@ -0,0 +1,210 @@ +package org.wso2.carbon.apimgt.rest.api.publisher.v1.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.APIDefinitionSearchResultAllOfDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.SearchResultDTO; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; + +import javax.xml.bind.annotation.*; +import org.wso2.carbon.apimgt.rest.api.common.annotations.Scope; +import com.fasterxml.jackson.annotation.JsonCreator; + +import javax.validation.Valid; + + + +public class APIDefinitionSearchResultDTO extends SearchResultDTO { + + private String apiName = null; + private String apiVersion = null; + private String apiContext = null; + private String apiUUID = null; + private String apiProvider = null; + private String apiType = null; + private String associatedType = null; + + /** + * The name of the associated API + **/ + public APIDefinitionSearchResultDTO apiName(String apiName) { + this.apiName = apiName; + return this; + } + + + @ApiModelProperty(example = "TestAPI", value = "The name of the associated API") + @JsonProperty("apiName") + public String getApiName() { + return apiName; + } + public void setApiName(String apiName) { + this.apiName = apiName; + } + + /** + * The version of the associated API + **/ + public APIDefinitionSearchResultDTO apiVersion(String apiVersion) { + this.apiVersion = apiVersion; + return this; + } + + + @ApiModelProperty(example = "1.0.0", value = "The version of the associated API") + @JsonProperty("apiVersion") + public String getApiVersion() { + return apiVersion; + } + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + /** + * The context of the associated API + **/ + public APIDefinitionSearchResultDTO apiContext(String apiContext) { + this.apiContext = apiContext; + return this; + } + + + @ApiModelProperty(example = "/test", value = "The context of the associated API") + @JsonProperty("apiContext") + public String getApiContext() { + return apiContext; + } + public void setApiContext(String apiContext) { + this.apiContext = apiContext; + } + + /** + * The UUID of the associated API + **/ + public APIDefinitionSearchResultDTO apiUUID(String apiUUID) { + this.apiUUID = apiUUID; + return this; + } + + + @ApiModelProperty(value = "The UUID of the associated API") + @JsonProperty("apiUUID") + public String getApiUUID() { + return apiUUID; + } + public void setApiUUID(String apiUUID) { + this.apiUUID = apiUUID; + } + + /** + * The provider name of the associated API + **/ + public APIDefinitionSearchResultDTO apiProvider(String apiProvider) { + this.apiProvider = apiProvider; + return this; + } + + + @ApiModelProperty(example = "publisher", value = "The provider name of the associated API") + @JsonProperty("apiProvider") + public String getApiProvider() { + return apiProvider; + } + public void setApiProvider(String apiProvider) { + this.apiProvider = apiProvider; + } + + /** + * The type of the associated API + **/ + public APIDefinitionSearchResultDTO apiType(String apiType) { + this.apiType = apiType; + return this; + } + + + @ApiModelProperty(example = "REST", value = "The type of the associated API") + @JsonProperty("apiType") + public String getApiType() { + return apiType; + } + public void setApiType(String apiType) { + this.apiType = apiType; + } + + /** + * API or APIProduct + **/ + public APIDefinitionSearchResultDTO associatedType(String associatedType) { + this.associatedType = associatedType; + return this; + } + + + @ApiModelProperty(example = "API", value = "API or APIProduct") + @JsonProperty("associatedType") + public String getAssociatedType() { + return associatedType; + } + public void setAssociatedType(String associatedType) { + this.associatedType = associatedType; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + APIDefinitionSearchResultDTO apIDefinitionSearchResult = (APIDefinitionSearchResultDTO) o; + return Objects.equals(apiName, apIDefinitionSearchResult.apiName) && + Objects.equals(apiVersion, apIDefinitionSearchResult.apiVersion) && + Objects.equals(apiContext, apIDefinitionSearchResult.apiContext) && + Objects.equals(apiUUID, apIDefinitionSearchResult.apiUUID) && + Objects.equals(apiProvider, apIDefinitionSearchResult.apiProvider) && + Objects.equals(apiType, apIDefinitionSearchResult.apiType) && + Objects.equals(associatedType, apIDefinitionSearchResult.associatedType); + } + + @Override + public int hashCode() { + return Objects.hash(apiName, apiVersion, apiContext, apiUUID, apiProvider, apiType, associatedType); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class APIDefinitionSearchResultDTO {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); + sb.append(" apiName: ").append(toIndentedString(apiName)).append("\n"); + sb.append(" apiVersion: ").append(toIndentedString(apiVersion)).append("\n"); + sb.append(" apiContext: ").append(toIndentedString(apiContext)).append("\n"); + sb.append(" apiUUID: ").append(toIndentedString(apiUUID)).append("\n"); + sb.append(" apiProvider: ").append(toIndentedString(apiProvider)).append("\n"); + sb.append(" apiType: ").append(toIndentedString(apiType)).append("\n"); + sb.append(" associatedType: ").append(toIndentedString(associatedType)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/SearchResultDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/SearchResultDTO.java index 5c057ee3f5cd..595a12016561 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/SearchResultDTO.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/SearchResultDTO.java @@ -30,7 +30,8 @@ public class SearchResultDTO { public enum TypeEnum { DOC("DOC"), API("API"), - APIPRODUCT("APIProduct"); + APIPRODUCT("APIProduct"), + DEFINITION("DEFINITION"); private String value; TypeEnum (String v) { 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/SearchResultMappingUtil.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/SearchResultMappingUtil.java index 67ef529209cf..accf4fab4554 100644 --- 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/SearchResultMappingUtil.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/SearchResultMappingUtil.java @@ -24,6 +24,7 @@ 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.api.model.APIDefinitionContentSearchResult; import org.wso2.carbon.apimgt.api.model.APIIdentifier; import org.wso2.carbon.apimgt.api.model.APIProduct; import org.wso2.carbon.apimgt.api.model.APIProductIdentifier; @@ -32,6 +33,7 @@ import org.wso2.carbon.apimgt.impl.utils.APIUtil; import org.wso2.carbon.apimgt.rest.api.common.RestApiCommonUtil; import org.wso2.carbon.apimgt.rest.api.common.RestApiConstants; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.APIDefinitionSearchResultDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.APIProductSearchResultDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.APISearchResultDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.DocumentSearchResultDTO; @@ -234,4 +236,33 @@ public static void setPaginationParams(SearchResultListDTO resultListDTO, String visibility.toString())); } } + + /** + * Get APIDefinitionSearchResultDTO representation for APi Definition Content Search Result. + * + * @param apiDefResult APIDefinitionContentSearchResult obj + * @return APIDefinitionSearchResultDTO obj + */ + + public static APIDefinitionSearchResultDTO + fromAPIDefSearchResultToAPIDefSearchResultDTO(APIDefinitionContentSearchResult apiDefResult) { + APIDefinitionSearchResultDTO apiDefSearchResultDTO = new APIDefinitionSearchResultDTO(); + apiDefSearchResultDTO.setId(apiDefResult.getId()); + apiDefSearchResultDTO.setType(SearchResultDTO.TypeEnum.DEFINITION); + apiDefSearchResultDTO.setApiUUID(apiDefResult.getApiUuid()); + apiDefSearchResultDTO.setApiName(apiDefResult.getApiName()); + apiDefSearchResultDTO.setApiContext(apiDefResult.getApiContext()); + apiDefSearchResultDTO.setApiVersion(apiDefResult.getApiVersion()); + apiDefSearchResultDTO.setApiProvider(apiDefResult.getApiProvider()); + apiDefSearchResultDTO.setApiType(apiDefResult.getApiType()); + apiDefSearchResultDTO.setAssociatedType(apiDefResult.getAssociatedType()); + if (apiDefResult.getName().contains("swagger")) { + apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " Swagger Definition"); + } else if (apiDefResult.getName().contains("graphql")) { + apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " GraphQL Definition"); + } else if (apiDefResult.getName().contains("async")) { + apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " Async Definition"); + } + return apiDefSearchResultDTO; + } } 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 517d176d35ed..6ab36696dc85 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 @@ -25,6 +25,7 @@ import org.wso2.carbon.apimgt.api.APIManagementException; import org.wso2.carbon.apimgt.api.APIProvider; import org.wso2.carbon.apimgt.api.model.API; +import org.wso2.carbon.apimgt.api.model.APIDefinitionContentSearchResult; import org.wso2.carbon.apimgt.api.model.APIProduct; import org.wso2.carbon.apimgt.api.model.Documentation; import org.wso2.carbon.apimgt.impl.APIConstants; @@ -118,6 +119,11 @@ private List getAllMatchedResults(List apis) throws API (Documentation) pair.getKey(), (APIProduct) pair.getValue()); } allMatchedResults.add(docResult); + } else if (searchResult instanceof APIDefinitionContentSearchResult) { + APIDefinitionContentSearchResult apiDefResult = (APIDefinitionContentSearchResult) searchResult; + SearchResultDTO definitionResultDTO = + SearchResultMappingUtil.fromAPIDefSearchResultToAPIDefSearchResultDTO(apiDefResult); + allMatchedResults.add(definitionResultDTO); } } return allMatchedResults; diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml index 7fa0e5807561..ace0086ff9e7 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml @@ -12266,6 +12266,7 @@ components: - DOC - API - APIProduct + - DEFINITION transportType: type: string description: Accepted values are HTTP, WS, SOAPTOREST, GRAPHQL @@ -12446,6 +12447,38 @@ components: type: string associatedType: type: string + APIDefinitionSearchResult: + title: API Definition Search Result + allOf: + - $ref: '#/components/schemas/SearchResult' + - properties: + apiName: + type: string + description: The name of the associated API + example: TestAPI + apiVersion: + type: string + description: The version of the associated API + example: 1.0.0 + apiContext: + type: string + description: The context of the associated API + example: /test + apiUUID: + type: string + description: The UUID of the associated API + apiProvider: + type: string + description: The provider name of the associated API + example: publisher + apiType: + type: string + description: The type of the associated API + example: REST + associatedType: + type: string + description: API or APIProduct + example: API MockResponsePayloadList: title: Mock Response Payload list type: object diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/APIDefinitionSearchResultAllOfDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/APIDefinitionSearchResultAllOfDTO.java new file mode 100644 index 000000000000..ca6b1116baf7 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/APIDefinitionSearchResultAllOfDTO.java @@ -0,0 +1,208 @@ +package org.wso2.carbon.apimgt.rest.api.store.v1.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; + +import javax.xml.bind.annotation.*; +import org.wso2.carbon.apimgt.rest.api.common.annotations.Scope; +import com.fasterxml.jackson.annotation.JsonCreator; + +import javax.validation.Valid; + + + +public class APIDefinitionSearchResultAllOfDTO { + + private String apiName = null; + private String apiVersion = null; + private String apiContext = null; + private String apiUUID = null; + private String apiProvider = null; + private String apiType = null; + private String associatedType = null; + + /** + * The name of the associated API + **/ + public APIDefinitionSearchResultAllOfDTO apiName(String apiName) { + this.apiName = apiName; + return this; + } + + + @ApiModelProperty(example = "TestAPI", value = "The name of the associated API") + @JsonProperty("apiName") + public String getApiName() { + return apiName; + } + public void setApiName(String apiName) { + this.apiName = apiName; + } + + /** + * The version of the associated API + **/ + public APIDefinitionSearchResultAllOfDTO apiVersion(String apiVersion) { + this.apiVersion = apiVersion; + return this; + } + + + @ApiModelProperty(example = "1.0.0", value = "The version of the associated API") + @JsonProperty("apiVersion") + public String getApiVersion() { + return apiVersion; + } + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + /** + * The context of the associated API + **/ + public APIDefinitionSearchResultAllOfDTO apiContext(String apiContext) { + this.apiContext = apiContext; + return this; + } + + + @ApiModelProperty(example = "/test", value = "The context of the associated API") + @JsonProperty("apiContext") + public String getApiContext() { + return apiContext; + } + public void setApiContext(String apiContext) { + this.apiContext = apiContext; + } + + /** + * The UUID of the associated API + **/ + public APIDefinitionSearchResultAllOfDTO apiUUID(String apiUUID) { + this.apiUUID = apiUUID; + return this; + } + + + @ApiModelProperty(value = "The UUID of the associated API") + @JsonProperty("apiUUID") + public String getApiUUID() { + return apiUUID; + } + public void setApiUUID(String apiUUID) { + this.apiUUID = apiUUID; + } + + /** + * The provider name of the associated API + **/ + public APIDefinitionSearchResultAllOfDTO apiProvider(String apiProvider) { + this.apiProvider = apiProvider; + return this; + } + + + @ApiModelProperty(example = "publisher", value = "The provider name of the associated API") + @JsonProperty("apiProvider") + public String getApiProvider() { + return apiProvider; + } + public void setApiProvider(String apiProvider) { + this.apiProvider = apiProvider; + } + + /** + * The type of the associated API + **/ + public APIDefinitionSearchResultAllOfDTO apiType(String apiType) { + this.apiType = apiType; + return this; + } + + + @ApiModelProperty(example = "REST", value = "The type of the associated API") + @JsonProperty("apiType") + public String getApiType() { + return apiType; + } + public void setApiType(String apiType) { + this.apiType = apiType; + } + + /** + * API or APIProduct + **/ + public APIDefinitionSearchResultAllOfDTO associatedType(String associatedType) { + this.associatedType = associatedType; + return this; + } + + + @ApiModelProperty(example = "API", value = "API or APIProduct") + @JsonProperty("associatedType") + public String getAssociatedType() { + return associatedType; + } + public void setAssociatedType(String associatedType) { + this.associatedType = associatedType; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + APIDefinitionSearchResultAllOfDTO apIDefinitionSearchResultAllOf = (APIDefinitionSearchResultAllOfDTO) o; + return Objects.equals(apiName, apIDefinitionSearchResultAllOf.apiName) && + Objects.equals(apiVersion, apIDefinitionSearchResultAllOf.apiVersion) && + Objects.equals(apiContext, apIDefinitionSearchResultAllOf.apiContext) && + Objects.equals(apiUUID, apIDefinitionSearchResultAllOf.apiUUID) && + Objects.equals(apiProvider, apIDefinitionSearchResultAllOf.apiProvider) && + Objects.equals(apiType, apIDefinitionSearchResultAllOf.apiType) && + Objects.equals(associatedType, apIDefinitionSearchResultAllOf.associatedType); + } + + @Override + public int hashCode() { + return Objects.hash(apiName, apiVersion, apiContext, apiUUID, apiProvider, apiType, associatedType); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class APIDefinitionSearchResultAllOfDTO {\n"); + + sb.append(" apiName: ").append(toIndentedString(apiName)).append("\n"); + sb.append(" apiVersion: ").append(toIndentedString(apiVersion)).append("\n"); + sb.append(" apiContext: ").append(toIndentedString(apiContext)).append("\n"); + sb.append(" apiUUID: ").append(toIndentedString(apiUUID)).append("\n"); + sb.append(" apiProvider: ").append(toIndentedString(apiProvider)).append("\n"); + sb.append(" apiType: ").append(toIndentedString(apiType)).append("\n"); + sb.append(" associatedType: ").append(toIndentedString(associatedType)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/APIDefinitionSearchResultDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/APIDefinitionSearchResultDTO.java new file mode 100644 index 000000000000..b79b2e159f13 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/APIDefinitionSearchResultDTO.java @@ -0,0 +1,210 @@ +package org.wso2.carbon.apimgt.rest.api.store.v1.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.APIDefinitionSearchResultAllOfDTO; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.SearchResultDTO; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; + +import javax.xml.bind.annotation.*; +import org.wso2.carbon.apimgt.rest.api.common.annotations.Scope; +import com.fasterxml.jackson.annotation.JsonCreator; + +import javax.validation.Valid; + + + +public class APIDefinitionSearchResultDTO extends SearchResultDTO { + + private String apiName = null; + private String apiVersion = null; + private String apiContext = null; + private String apiUUID = null; + private String apiProvider = null; + private String apiType = null; + private String associatedType = null; + + /** + * The name of the associated API + **/ + public APIDefinitionSearchResultDTO apiName(String apiName) { + this.apiName = apiName; + return this; + } + + + @ApiModelProperty(example = "TestAPI", value = "The name of the associated API") + @JsonProperty("apiName") + public String getApiName() { + return apiName; + } + public void setApiName(String apiName) { + this.apiName = apiName; + } + + /** + * The version of the associated API + **/ + public APIDefinitionSearchResultDTO apiVersion(String apiVersion) { + this.apiVersion = apiVersion; + return this; + } + + + @ApiModelProperty(example = "1.0.0", value = "The version of the associated API") + @JsonProperty("apiVersion") + public String getApiVersion() { + return apiVersion; + } + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + /** + * The context of the associated API + **/ + public APIDefinitionSearchResultDTO apiContext(String apiContext) { + this.apiContext = apiContext; + return this; + } + + + @ApiModelProperty(example = "/test", value = "The context of the associated API") + @JsonProperty("apiContext") + public String getApiContext() { + return apiContext; + } + public void setApiContext(String apiContext) { + this.apiContext = apiContext; + } + + /** + * The UUID of the associated API + **/ + public APIDefinitionSearchResultDTO apiUUID(String apiUUID) { + this.apiUUID = apiUUID; + return this; + } + + + @ApiModelProperty(value = "The UUID of the associated API") + @JsonProperty("apiUUID") + public String getApiUUID() { + return apiUUID; + } + public void setApiUUID(String apiUUID) { + this.apiUUID = apiUUID; + } + + /** + * The provider name of the associated API + **/ + public APIDefinitionSearchResultDTO apiProvider(String apiProvider) { + this.apiProvider = apiProvider; + return this; + } + + + @ApiModelProperty(example = "publisher", value = "The provider name of the associated API") + @JsonProperty("apiProvider") + public String getApiProvider() { + return apiProvider; + } + public void setApiProvider(String apiProvider) { + this.apiProvider = apiProvider; + } + + /** + * The type of the associated API + **/ + public APIDefinitionSearchResultDTO apiType(String apiType) { + this.apiType = apiType; + return this; + } + + + @ApiModelProperty(example = "REST", value = "The type of the associated API") + @JsonProperty("apiType") + public String getApiType() { + return apiType; + } + public void setApiType(String apiType) { + this.apiType = apiType; + } + + /** + * API or APIProduct + **/ + public APIDefinitionSearchResultDTO associatedType(String associatedType) { + this.associatedType = associatedType; + return this; + } + + + @ApiModelProperty(example = "API", value = "API or APIProduct") + @JsonProperty("associatedType") + public String getAssociatedType() { + return associatedType; + } + public void setAssociatedType(String associatedType) { + this.associatedType = associatedType; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + APIDefinitionSearchResultDTO apIDefinitionSearchResult = (APIDefinitionSearchResultDTO) o; + return Objects.equals(apiName, apIDefinitionSearchResult.apiName) && + Objects.equals(apiVersion, apIDefinitionSearchResult.apiVersion) && + Objects.equals(apiContext, apIDefinitionSearchResult.apiContext) && + Objects.equals(apiUUID, apIDefinitionSearchResult.apiUUID) && + Objects.equals(apiProvider, apIDefinitionSearchResult.apiProvider) && + Objects.equals(apiType, apIDefinitionSearchResult.apiType) && + Objects.equals(associatedType, apIDefinitionSearchResult.associatedType); + } + + @Override + public int hashCode() { + return Objects.hash(apiName, apiVersion, apiContext, apiUUID, apiProvider, apiType, associatedType); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class APIDefinitionSearchResultDTO {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); + sb.append(" apiName: ").append(toIndentedString(apiName)).append("\n"); + sb.append(" apiVersion: ").append(toIndentedString(apiVersion)).append("\n"); + sb.append(" apiContext: ").append(toIndentedString(apiContext)).append("\n"); + sb.append(" apiUUID: ").append(toIndentedString(apiUUID)).append("\n"); + sb.append(" apiProvider: ").append(toIndentedString(apiProvider)).append("\n"); + sb.append(" apiType: ").append(toIndentedString(apiType)).append("\n"); + sb.append(" associatedType: ").append(toIndentedString(associatedType)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/SearchResultDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/SearchResultDTO.java index 5be105aa045a..729775b25c9f 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/SearchResultDTO.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/store/v1/dto/SearchResultDTO.java @@ -30,7 +30,8 @@ public class SearchResultDTO { public enum TypeEnum { DOC("DOC"), API("API"), - APIPRODUCT("APIProduct"); + APIPRODUCT("APIProduct"), + DEFINITION("DEFINITION"); private String value; TypeEnum (String v) { diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/SearchApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/SearchApiServiceImpl.java index 3ce86cf6cf4f..8707ee31f411 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/SearchApiServiceImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/SearchApiServiceImpl.java @@ -24,6 +24,7 @@ import org.wso2.carbon.apimgt.api.APIConsumer; import org.wso2.carbon.apimgt.api.APIManagementException; import org.wso2.carbon.apimgt.api.model.API; +import org.wso2.carbon.apimgt.api.model.APIDefinitionContentSearchResult; import org.wso2.carbon.apimgt.api.model.APIProduct; import org.wso2.carbon.apimgt.api.model.Documentation; import org.wso2.carbon.apimgt.impl.APIConstants; @@ -104,6 +105,11 @@ public Response searchGet(Integer limit, Integer offset, String xWSO2Tenant, Str APIProduct apiProduct = (APIProduct) searchResult; SearchResultDTO apiResult = SearchResultMappingUtil.fromAPIToAPIResultDTO(apiProduct); allmatchedResults.add(apiResult); + } else if (searchResult instanceof APIDefinitionContentSearchResult) { + APIDefinitionContentSearchResult apiDefResult = (APIDefinitionContentSearchResult) searchResult; + SearchResultDTO definitionResultDTO = + SearchResultMappingUtil.fromAPIDefSearchResultToAPIDefSearchResultDTO(apiDefResult); + allmatchedResults.add(definitionResultDTO); } } diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/mappings/SearchResultMappingUtil.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/mappings/SearchResultMappingUtil.java index 31b08f6654dd..8da593272e27 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/mappings/SearchResultMappingUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/mappings/SearchResultMappingUtil.java @@ -19,12 +19,14 @@ package org.wso2.carbon.apimgt.rest.api.store.v1.mappings; import org.wso2.carbon.apimgt.api.model.API; +import org.wso2.carbon.apimgt.api.model.APIDefinitionContentSearchResult; import org.wso2.carbon.apimgt.api.model.APIProduct; import org.wso2.carbon.apimgt.api.model.APIIdentifier; import org.wso2.carbon.apimgt.api.model.APIProductIdentifier; import org.wso2.carbon.apimgt.api.model.Documentation; import org.wso2.carbon.apimgt.impl.utils.APIUtil; import org.wso2.carbon.apimgt.rest.api.common.RestApiCommonUtil; +import org.wso2.carbon.apimgt.rest.api.store.v1.dto.APIDefinitionSearchResultDTO; import org.wso2.carbon.apimgt.rest.api.store.v1.dto.DocumentSearchResultDTO; import org.wso2.carbon.apimgt.rest.api.store.v1.dto.PaginationDTO; import org.wso2.carbon.apimgt.rest.api.store.v1.dto.SearchResultDTO; @@ -178,4 +180,33 @@ public static void setPaginationParams(SearchResultListDTO resultListDTO, String resultListDTO.setPagination(paginationDTO); } + /** + * Get APIDefinitionSearchResultDTO representation for APi Definition Content Search Result. + * + * @param apiDefResult APIDefinitionContentSearchResult obj + * @return APIDefinitionSearchResultDTO obj + */ + + public static APIDefinitionSearchResultDTO + fromAPIDefSearchResultToAPIDefSearchResultDTO(APIDefinitionContentSearchResult apiDefResult) { + APIDefinitionSearchResultDTO apiDefSearchResultDTO = new APIDefinitionSearchResultDTO(); + apiDefSearchResultDTO.setId(apiDefResult.getId()); + apiDefSearchResultDTO.setType(SearchResultDTO.TypeEnum.DEFINITION); + apiDefSearchResultDTO.setApiUUID(apiDefResult.getApiUuid()); + apiDefSearchResultDTO.setApiName(apiDefResult.getApiName()); + apiDefSearchResultDTO.setApiContext(apiDefResult.getApiContext()); + apiDefSearchResultDTO.setApiVersion(apiDefResult.getApiVersion()); + apiDefSearchResultDTO.setApiProvider(apiDefResult.getApiProvider()); + apiDefSearchResultDTO.setApiType(apiDefResult.getApiType()); + apiDefSearchResultDTO.setAssociatedType(apiDefResult.getAssociatedType()); + if (apiDefResult.getName().contains("swagger")) { + apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " Swagger Definition"); + } else if (apiDefResult.getName().contains("graphql")) { + apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " GraphQL Definition"); + } else if (apiDefResult.getName().contains("async")) { + apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " Async Definition"); + } + return apiDefSearchResultDTO; + } + } diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/resources/devportal-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/resources/devportal-api.yaml index b99d43642ed8..90a501b43f27 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/resources/devportal-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/resources/devportal-api.yaml @@ -5320,6 +5320,7 @@ components: - DOC - API - APIProduct + - DEFINITION transportType: type: string description: Accepted values are HTTP, WS, SOAPTOREST, GRAPHQL @@ -5420,6 +5421,38 @@ components: example: admin apiUUID: type: string + APIDefinitionSearchResult: + title: API Definition Search Result + allOf: + - $ref: '#/components/schemas/SearchResult' + - properties: + apiName: + type: string + description: The name of the associated API + example: TestAPI + apiVersion: + type: string + description: The version of the associated API + example: 1.0.0 + apiContext: + type: string + description: The context of the associated API + example: /test + apiUUID: + type: string + description: The UUID of the associated API + apiProvider: + type: string + description: The provider name of the associated API + example: publisher + apiType: + type: string + description: The type of the associated API + example: REST + associatedType: + type: string + description: API or APIProduct + example: API CommenterInfo: type: object properties: From f3bc13b4bec7160de8a5a00eba377029c8facf83 Mon Sep 17 00:00:00 2001 From: Avishka-Shamendra Date: Thu, 4 Jul 2024 14:03:00 +0530 Subject: [PATCH 2/4] Add API Definition content search for WSDL files --- .../indexer/SOAPAPIDefinitionIndexer.java | 91 +++++++++++++++++++ .../apimgt/persistence/APIConstants.java | 5 +- .../persistence/RegistryPersistenceImpl.java | 26 +++++- .../persistence/utils/RegistrySearchUtil.java | 21 +++++ .../mappings/SearchResultMappingUtil.java | 2 + .../v1/mappings/SearchResultMappingUtil.java | 2 + 6 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/SOAPAPIDefinitionIndexer.java diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/SOAPAPIDefinitionIndexer.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/SOAPAPIDefinitionIndexer.java new file mode 100644 index 000000000000..a43f6c8d3c10 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/SOAPAPIDefinitionIndexer.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * 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.impl.indexing.indexer; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.solr.common.SolrException; +import org.wso2.carbon.apimgt.api.APIManagementException; +import org.wso2.carbon.apimgt.impl.APIConstants; +import org.wso2.carbon.apimgt.impl.utils.IndexerUtil; +import org.wso2.carbon.governance.api.util.GovernanceUtils; +import org.wso2.carbon.registry.core.Registry; +import org.wso2.carbon.registry.core.RegistryConstants; +import org.wso2.carbon.registry.core.Resource; +import org.wso2.carbon.registry.core.exceptions.RegistryException; +import org.wso2.carbon.registry.indexing.AsyncIndexer; +import org.wso2.carbon.registry.indexing.IndexingManager; +import org.wso2.carbon.registry.indexing.solr.IndexDocument; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * This indexer is introduced to index .wsdl files for unified content search. + */ +public class SOAPAPIDefinitionIndexer extends XMLIndexer { + + public static final Log log = LogFactory.getLog(SOAPAPIDefinitionIndexer.class); + + @Override + public IndexDocument getIndexedDocument(AsyncIndexer.File2Index fileData) throws SolrException, RegistryException { + Registry registry = GovernanceUtils + .getGovernanceSystemRegistry(IndexingManager.getInstance().getRegistry(fileData.tenantId)); + String definitionResourcePath = + fileData.path.substring(RegistryConstants.GOVERNANCE_REGISTRY_BASE_PATH.length()); + + + // If the file is not a wsdl definition file in provider path do not index + if (!definitionResourcePath.contains(APIConstants.APPLICATION_DATA_RESOURCE_URL_PREFIX) + || !definitionResourcePath.contains(APIConstants.WSDL_EXTENSION)) { + return null; + } + + IndexDocument indexDocument = super.getIndexedDocument(fileData); + IndexDocument newIndexDocument = indexDocument; + + if (log.isDebugEnabled()) { + log.debug("Executing WSDL file indexer for resource at " + definitionResourcePath); + } + + Resource definitionResource = null; + Map> fields = indexDocument.getFields(); + + if (registry.resourceExists(definitionResourcePath)) { + definitionResource = registry.get(definitionResourcePath); + } + + if (definitionResource != null) { + try { + IndexerUtil.fetchRequiredDetailsFromAssociatedAPI(registry, definitionResource, fields); + newIndexDocument = + new IndexDocument(fileData.path, null, + indexDocument.getRawContent(), indexDocument.getTenantId()); + fields.put(APIConstants.DOCUMENT_INDEXER_INDICATOR, Arrays.asList("true")); + newIndexDocument.setFields(fields); + } catch (APIManagementException e) { + //error occurred while fetching details from API, but continuing indexing + log.error("Error while updating indexed document.", e); + } + } + + return newIndexDocument; + } +} 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 69f6a8745d8a..2165229dcbe6 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 @@ -218,6 +218,8 @@ public static class Monetization { public static final String WSDL_FILE_EXTENSION = ".wsdl"; public static final String WSDL_PROVIDER_SEPERATOR = "--"; public static final String API_WSDL_ARCHIVE_LOCATION = "archives/"; + public static final String WSDL_XML_MEDIA_TYPE = "application/wsdl+xml"; + public static final String WSDL_FILE_MEDIA_TYPE = "application/octet-stream"; public static final String ZIP_FILE_EXTENSION = ".zip"; @@ -371,7 +373,6 @@ public enum APITransportType { public static final String GRAPHQL_SCHEMA_FILE_EXTENSION = ".graphql"; public static final String GRAPHQL_LOCAL_ENTRY_EXTENSION = "_graphQL"; public static final String GRAPHQL_SCHEMA_PROVIDER_SEPERATOR = "--"; - public static final String GRAPHQL_API_TYPE = "GRAPHQL"; public static final String GRAPHQL_DEFINITION_MEDIA_TYPE = "text/plain; charset=ISO-8859-1"; public static final String ALLOW_MULTIPLE_STATUS = "allowMultipleStatus"; public static final String ALLOW_MULTIPLE_VERSIONS = "allowMultipleVersions"; @@ -381,6 +382,8 @@ public enum APITransportType { public static final String API_TYPE_SSE = "SSE"; public static final String API_TYPE_WEBHOOK = "WEBHOOK"; public static final String API_TYPE_WS = "WS"; + public static final String API_TYPE_GRAPHQL = "GRAPHQL"; + public static final String API_TYPE_SOAP = "SOAP"; public static final String API_ASYNC_API_DEFINITION_RESOURCE_NAME = "asyncapi.json"; 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 bc75c23d536c..0c445d615bf4 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 @@ -594,8 +594,8 @@ public PublisherAPI updateAPI(Organization org, PublisherAPI publisherAPI) throw visibleRoles, resourcePath, registry); } - // Update .graphgl and .asyncapi file permissions, required for API definition content search functionality - if (APIConstants.GRAPHQL_API_TYPE.equals(api.getType())) { + // Update api def file permissions, required for API definition content search functionality + if (APIConstants.API_TYPE_GRAPHQL.equals(api.getType())) { String resourcePath = RegistryPersistenceUtil.getOpenAPIDefinitionFilePath(api.getId().getName(), api.getId().getVersion(), api.getId().getProviderName()); resourcePath += api.getId().getProviderName() + APIConstants.GRAPHQL_SCHEMA_PROVIDER_SEPERATOR + @@ -616,6 +616,17 @@ public PublisherAPI updateAPI(Organization org, PublisherAPI publisherAPI) throw RegistryPersistenceUtil.setResourcePermissions(api.getId().getProviderName(), api.getVisibility(), visibleRoles, resourcePath, registry); } + } else if (APIConstants.API_TYPE_SOAP.equals(api.getType())) { + String resourcePath = RegistryPersistenceUtil.getOpenAPIDefinitionFilePath(api.getId().getName(), + api.getId().getVersion(), api.getId().getProviderName()); + resourcePath += api.getId().getProviderName() + APIConstants.WSDL_PROVIDER_SEPERATOR + + api.getId().getName() + api.getId().getVersion() + APIConstants.WSDL_FILE_EXTENSION; + if (registry.resourceExists(resourcePath)) { + RegistryPersistenceUtil.clearResourcePermissions(resourcePath, api.getId(), + ((UserRegistry) registry).getTenantId()); + RegistryPersistenceUtil.setResourcePermissions(api.getId().getProviderName(), api.getVisibility(), + visibleRoles, resourcePath, registry); + } } // doc visibility change @@ -1596,7 +1607,9 @@ public PublisherContentSearchResult searchContentForPublisher(Organization org, } } else if (APIConstants.APPLICATION_JSON_MEDIA_TYPE.equals(resource.getMediaType()) || - APIConstants.GRAPHQL_DEFINITION_MEDIA_TYPE.equals(resource.getMediaType())) { + APIConstants.GRAPHQL_DEFINITION_MEDIA_TYPE.equals(resource.getMediaType()) || + APIConstants.WSDL_XML_MEDIA_TYPE.equals(resource.getMediaType()) || + APIConstants.WSDL_FILE_MEDIA_TYPE.equals(resource.getMediaType())) { addAPIDefinitionSearchContent(resourcePath, registry, apiArtifactManager, contentData); } else { @@ -1756,7 +1769,9 @@ public DevPortalContentSearchResult searchContentForDevPortal(Organization org, } } else if (APIConstants.APPLICATION_JSON_MEDIA_TYPE.equals(resource.getMediaType()) || - APIConstants.GRAPHQL_DEFINITION_MEDIA_TYPE.equals(resource.getMediaType())) { + APIConstants.GRAPHQL_DEFINITION_MEDIA_TYPE.equals(resource.getMediaType()) || + APIConstants.WSDL_XML_MEDIA_TYPE.equals(resource.getMediaType()) || + APIConstants.WSDL_FILE_MEDIA_TYPE.equals(resource.getMediaType())) { addAPIDefinitionSearchContent(resourcePath, registry, apiArtifactManager, contentData); } else { @@ -4092,6 +4107,9 @@ private void addAPIDefinitionSearchContent(String resourcePath, Registry registr } else if (resourcePath.contains(APIConstants.GRAPHQL_SCHEMA_FILE_EXTENSION)) { index = resourcePath.lastIndexOf('/') + 1; content.setApiType(APIDefSearchContent.ApiType.GRAPHQL); + } else if (resourcePath.contains(APIConstants.WSDL_FILE_EXTENSION)) { + index = resourcePath.lastIndexOf('/') + 1; + content.setApiType(APIDefSearchContent.ApiType.SOAP); } else { index = resourcePath.indexOf(APIConstants.API_OAS_DEFINITION_RESOURCE_NAME); content.setApiType(APIDefSearchContent.ApiType.REST); diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistrySearchUtil.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistrySearchUtil.java index e7e0ba0f06a4..7b8f51bb7a09 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistrySearchUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistrySearchUtil.java @@ -58,17 +58,23 @@ public class RegistrySearchUtil { ".RESTAsyncAPIDefinitionIndexer"; public static final String GRAPHQL_DEFINITION_INDEXER = "org.wso2.carbon.apimgt.impl.indexing.indexer" + ".GraphQLAPIDefinitionIndexer"; + public static final String SOAP_DEFINITION_INDEXER = "org.wso2.carbon.apimgt.impl.indexing.indexer" + + ".SOAPAPIDefinitionIndexer"; public static final String STORE_VIEW_ROLES = "store_view_roles"; public static final String PUBLISHER_ROLES = "publisher_roles"; public static final String DOCUMENT_MEDIA_TYPE_KEY = "application/vnd.wso2-document\\+xml"; public static final String API_DEF_MEDIA_TYPE_KEY = "application/json"; public static final String GRAPHQL_DEF_MEDIA_TYPE_KEY = "text/plain(.)+charset=ISO-8859-1"; + public static final String SOAP_DEF_MEDIA_TYPE_KEY = "application/wsdl\\+xml|application/octet-stream"; public static final String SEARCH_MEDIA_TYPE_FIELD = "mediaType"; public static final String DOCUMENTATION_INLINE_CONTENT_TYPE = "text/plain"; public static final String API_RXT_MEDIA_TYPE = "application/vnd.wso2-api+xml"; public static final String LCSTATE_SEARCH_KEY = "lcState"; public static final String DOCUMENT_RXT_MEDIA_TYPE = "application/vnd.wso2-document+xml"; public static final String GRAPHQL_DEFINITION_MEDIA_TYPE = "text/plain; charset=ISO-8859-1"; + public static final String SOAP_DEFINITION_WSDL_XML_MEDIA_TYPE = "application/wsdl+xml"; + public static final String SOAP_DEFINITION_WSDL_FILE_MEDIA_TYPE = "application/octet-stream"; + public static final String API_OVERVIEW_STATUS = "overview_status"; public static final String API_RELATED_CUSTOM_PROPERTIES_PREFIX = "api_meta."; public static final String API_RELATED_CUSTOM_PROPERTIES_DISPLAY_DEV = "__display"; @@ -259,6 +265,7 @@ private static Map getSearchAttributes(String searchQuery) { Indexer documentIndexer = indexerMap.get(DOCUMENT_MEDIA_TYPE_KEY); Indexer jsonIndexer = indexerMap.get(API_DEF_MEDIA_TYPE_KEY); Indexer graphqlIndexer = indexerMap.get(GRAPHQL_DEF_MEDIA_TYPE_KEY); + Indexer soapIndexer = indexerMap.get(SOAP_DEF_MEDIA_TYPE_KEY); String complexAttribute = ClientUtils.escapeQueryChars(API_RXT_MEDIA_TYPE); if (!StringUtils.isEmpty(publisherRoles)) { complexAttribute = @@ -296,6 +303,20 @@ private static Map getSearchAttributes(String searchQuery) { } } + if (soapIndexer != null && SOAP_DEFINITION_INDEXER.equals(soapIndexer.getClass().getName())) { + if (!StringUtils.isEmpty(publisherRoles)) { + complexAttribute += " OR mediaType_s:((" + ClientUtils + .escapeQueryChars(SOAP_DEFINITION_WSDL_XML_MEDIA_TYPE) + + " OR " + ClientUtils.escapeQueryChars(SOAP_DEFINITION_WSDL_FILE_MEDIA_TYPE) + + " ) AND publisher_roles_s:" + publisherRoles + ")"; + } else { + complexAttribute += " OR mediaType_s:((" + ClientUtils + .escapeQueryChars(SOAP_DEFINITION_WSDL_XML_MEDIA_TYPE) + + " OR " + ClientUtils.escapeQueryChars(SOAP_DEFINITION_WSDL_FILE_MEDIA_TYPE) + + " ) AND document_indexed_s:true)"; + } + } + attributes.put(SEARCH_MEDIA_TYPE_FIELD, complexAttribute); attributes.put(API_OVERVIEW_STATUS, apiState); return attributes; 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/SearchResultMappingUtil.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/SearchResultMappingUtil.java index accf4fab4554..e655ad7cb71f 100644 --- 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/SearchResultMappingUtil.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/SearchResultMappingUtil.java @@ -262,6 +262,8 @@ public static void setPaginationParams(SearchResultListDTO resultListDTO, String apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " GraphQL Definition"); } else if (apiDefResult.getName().contains("async")) { apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " Async Definition"); + } else if (apiDefResult.getName().contains("wsdl")) { + apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " WSDL Definition"); } return apiDefSearchResultDTO; } diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/mappings/SearchResultMappingUtil.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/mappings/SearchResultMappingUtil.java index 8da593272e27..ff0530f40d5a 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/mappings/SearchResultMappingUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/mappings/SearchResultMappingUtil.java @@ -205,6 +205,8 @@ public static void setPaginationParams(SearchResultListDTO resultListDTO, String apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " GraphQL Definition"); } else if (apiDefResult.getName().contains("async")) { apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " Async Definition"); + } else if (apiDefResult.getName().contains("wsdl")) { + apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " WSDL Definition"); } return apiDefSearchResultDTO; } From bb03db3dfe37193efa4ebaeb1dd8bf817c69aba3 Mon Sep 17 00:00:00 2001 From: Avishka-Shamendra Date: Tue, 10 Sep 2024 20:21:50 +0530 Subject: [PATCH 3/4] Improve API definition content search code --- .../indexer/GraphQLAPIDefinitionIndexer.java | 6 + .../RESTAsyncAPIDefinitionIndexer.java | 11 +- .../indexer/SOAPAPIDefinitionIndexer.java | 6 +- .../carbon/apimgt/impl/utils/IndexerUtil.java | 122 ++++++++++++++++++ .../persistence/RegistryPersistenceImpl.java | 84 +++++++++--- .../persistence/dto/APIDefSearchContent.java | 3 + 6 files changed, 211 insertions(+), 21 deletions(-) diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/GraphQLAPIDefinitionIndexer.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/GraphQLAPIDefinitionIndexer.java index b8d975e4aef6..784fac2f56a3 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/GraphQLAPIDefinitionIndexer.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/GraphQLAPIDefinitionIndexer.java @@ -29,6 +29,7 @@ import org.wso2.carbon.registry.core.RegistryConstants; import org.wso2.carbon.registry.core.Resource; import org.wso2.carbon.registry.core.exceptions.RegistryException; +import org.wso2.carbon.registry.core.utils.RegistryUtils; import org.wso2.carbon.registry.indexing.AsyncIndexer; import org.wso2.carbon.registry.indexing.IndexingManager; import org.wso2.carbon.registry.indexing.solr.IndexDocument; @@ -56,6 +57,11 @@ public IndexDocument getIndexedDocument(AsyncIndexer.File2Index fileData) throws return null; } + // Extract only required info (types) from graphql definition to index + String schemaString = RegistryUtils.decodeBytes(fileData.data); + String definitionString = IndexerUtil.getTypesFromGraphQLSchemaString(schemaString); + fileData.data = definitionString.getBytes(); + IndexDocument indexDocument = super.getIndexedDocument(fileData); IndexDocument newIndexDocument = indexDocument; diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/RESTAsyncAPIDefinitionIndexer.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/RESTAsyncAPIDefinitionIndexer.java index af6742c97b05..4208fb6a4c6b 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/RESTAsyncAPIDefinitionIndexer.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/RESTAsyncAPIDefinitionIndexer.java @@ -29,9 +29,9 @@ import org.wso2.carbon.registry.core.RegistryConstants; import org.wso2.carbon.registry.core.Resource; import org.wso2.carbon.registry.core.exceptions.RegistryException; +import org.wso2.carbon.registry.core.utils.RegistryUtils; import org.wso2.carbon.registry.indexing.AsyncIndexer.File2Index; import org.wso2.carbon.registry.indexing.IndexingManager; -import org.wso2.carbon.registry.indexing.indexer.JSONIndexer; import org.wso2.carbon.registry.indexing.solr.IndexDocument; import java.util.Arrays; @@ -41,7 +41,7 @@ /** * This is indexer introduced to index swagger,async api artifacts for unified content search. */ -public class RESTAsyncAPIDefinitionIndexer extends JSONIndexer { +public class RESTAsyncAPIDefinitionIndexer extends PlainTextIndexer { public static final Log log = LogFactory.getLog(RESTAsyncAPIDefinitionIndexer.class); @Override @@ -58,6 +58,12 @@ public IndexDocument getIndexedDocument(File2Index fileData) throws SolrExceptio return null; } + // Filter out only values from the swagger, async json files for indexing + String jsonAsString = RegistryUtils.decodeBytes(fileData.data); + String valuesString = IndexerUtil.getValuesFromJsonString(jsonAsString); + fileData.data = valuesString.getBytes(); + fileData.mediaType = "application/json"; + IndexDocument indexDocument = super.getIndexedDocument(fileData); IndexDocument newIndexDocument = indexDocument; @@ -89,5 +95,4 @@ public IndexDocument getIndexedDocument(File2Index fileData) throws SolrExceptio return newIndexDocument; } - } diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/SOAPAPIDefinitionIndexer.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/SOAPAPIDefinitionIndexer.java index a43f6c8d3c10..343f356eacba 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/SOAPAPIDefinitionIndexer.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/indexing/indexer/SOAPAPIDefinitionIndexer.java @@ -40,7 +40,7 @@ /** * This indexer is introduced to index .wsdl files for unified content search. */ -public class SOAPAPIDefinitionIndexer extends XMLIndexer { +public class SOAPAPIDefinitionIndexer extends PlainTextIndexer { public static final Log log = LogFactory.getLog(SOAPAPIDefinitionIndexer.class); @@ -58,6 +58,10 @@ public IndexDocument getIndexedDocument(AsyncIndexer.File2Index fileData) throws return null; } + // Extract only required data before indexing + String contentString = IndexerUtil.getContentFromXMLData(fileData.data); + fileData.data = contentString.getBytes(); + IndexDocument indexDocument = super.getIndexedDocument(fileData); IndexDocument newIndexDocument = indexDocument; diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/IndexerUtil.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/IndexerUtil.java index eca6b3e55171..d6aeced1f1e6 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/IndexerUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/IndexerUtil.java @@ -20,15 +20,28 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONObject; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import org.wso2.carbon.apimgt.api.APIManagementException; +import org.wso2.carbon.apimgt.api.model.graphql.queryanalysis.GraphqlSchemaType; import org.wso2.carbon.apimgt.impl.APIConstants; +import org.wso2.carbon.apimgt.impl.definitions.GraphQLSchemaDefinition; import org.wso2.carbon.governance.api.generic.GenericArtifactManager; import org.wso2.carbon.governance.api.generic.dataobjects.GenericArtifact; import org.wso2.carbon.registry.core.Registry; import org.wso2.carbon.registry.core.Resource; import org.wso2.carbon.registry.core.exceptions.RegistryException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.ByteArrayInputStream; import java.util.Arrays; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -65,4 +78,113 @@ public static void fetchRequiredDetailsFromAssociatedAPI(Registry registry, Reso log.warn("API does not exist at " + apiPath); } } + + + /** + * This method can be used to filter out the json values, excluding keys from the json files + * + * @param jsonAsString JSON string + * @return values string + */ + public static String getValuesFromJsonString(String jsonAsString) { + JSONObject jsonObject = new JSONObject(jsonAsString); + StringBuilder values = new StringBuilder(); + extractJsonValues(jsonObject, values); + return values.toString().trim(); + } + + /** + * Extract json values as a string from JSON object recursively + * + * @param json json object + * @param values StringBuilder values + */ + private static void extractJsonValues(Object json, StringBuilder values) { + if (json instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) json; + Iterator keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + extractJsonValues(jsonObject.get(key), values); + } + } else if (json instanceof JSONArray) { + JSONArray jsonArray = (JSONArray) json; + for (int i = 0; i < jsonArray.length(); i++) { + extractJsonValues(jsonArray.get(i), values); + } + } else { + values.append(json.toString()).append(" "); + } + } + + /** + * This method is used to extract types from graphql schema + * + * @param schemaString Schema String + * @return definition string + */ + public static String getTypesFromGraphQLSchemaString(String schemaString) { + List types = new GraphQLSchemaDefinition() + .extractGraphQLTypeList(schemaString); + StringBuilder definitionString = new StringBuilder(); + + for (GraphqlSchemaType type : types) { + definitionString.append(type.getType()).append(" "); + definitionString.append(String.join(" ", type.getFieldList())); + } + return definitionString.toString(); + } + + /** + * This method returns string content extracted from XML data + * + * @param xmlData xml data bytes + * @return string content + */ + public static String getContentFromXMLData(byte[] xmlData) { + try { + DocumentBuilderFactory factory = APIUtil.getSecuredDocumentBuilder(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new ByteArrayInputStream(xmlData)); + doc.getDocumentElement().normalize(); + return extractTextAndAttributesFromElement(doc.getDocumentElement()); + } catch (Exception e) { + log.error("Error while parsing XML data", e); + return ""; + } + } + + /** + * This method is used to recursively extract text and attributes as a string + * + * @param element Doc element + * @return text and attributes string + */ + + private static String extractTextAndAttributesFromElement(Element element) { + StringBuilder text = new StringBuilder(); + + // Extract attributes from the element + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) { + Node attr = attributes.item(i); + text.append(attr.getNodeName()).append("=").append(attr.getNodeValue()).append(" "); + } + + // Extract text from child nodes + NodeList nodeList = element.getChildNodes(); + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + // Recursively extract text and attributes + text.append(extractTextAndAttributesFromElement((Element) node)).append(" "); + } else if (node.getNodeType() == Node.TEXT_NODE) { + // Append text directly + text.append(node.getTextContent().trim()).append(" "); + } + } + return text.toString().trim(); + } + } + 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 0c445d615bf4..0f0a95a58d56 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 @@ -1712,7 +1712,8 @@ public DevPortalContentSearchResult searchContentForDevPortal(Organization org, .getRegistry(CarbonConstants.REGISTRY_SYSTEM_USERNAME, tenantId); ContentBasedSearchService contentBasedSearchService = new ContentBasedSearchService(); - SearchResultsBean resultsBean = contentBasedSearchService.searchByAttribute(attributes, systemUserRegistry); + SearchResultsBean resultsBean = contentBasedSearchService.searchByAttribute(attributes, + systemUserRegistry); String errorMsg = resultsBean.getErrorMessage(); if (errorMsg != null) { throw new APIPersistenceException("Error while searching " + errorMsg); @@ -4112,7 +4113,8 @@ private void addAPIDefinitionSearchContent(String resourcePath, Registry registr content.setApiType(APIDefSearchContent.ApiType.SOAP); } else { index = resourcePath.indexOf(APIConstants.API_OAS_DEFINITION_RESOURCE_NAME); - content.setApiType(APIDefSearchContent.ApiType.REST); + // swagger.json is included in all types of APIs, hence we need to properly set the API type + setAPITypeForSwagger(resourcePath, index, content, registry); } String apiPath = resourcePath.substring(0, index) + APIConstants.API_KEY; @@ -4123,26 +4125,74 @@ private void addAPIDefinitionSearchContent(String resourcePath, Registry registr String defResourceName = defResource.getId().substring(defResource.getId().lastIndexOf('/') + 1); DevPortalAPI devAPI; + /* Ignore internal swagger.json content search results of non REST APIs + as most of the data is duplicated in WSDL, GraphQL, AsyncAPI def. */ + boolean ignoreDuplicateSwaggerContent = + (!APIDefSearchContent.ApiType.REST.toString().equals(content.getApiType()) + && defResourceName.contains("swagger")); + if (apiArtifactId != null) { - GenericArtifact apiArtifact = apiArtifactManager.getGenericArtifact(apiArtifactId); - devAPI = RegistryPersistenceUtil.getDevPortalAPIForSearch(apiArtifact); - content.setId(defResourceId); - content.setName(defResourceName); - content.setApiUUID(devAPI.getId()); - content.setApiName(devAPI.getApiName()); - content.setApiContext(devAPI.getContext()); - content.setApiProvider(devAPI.getProviderName()); - content.setApiVersion(devAPI.getVersion()); - if (apiArtifact.getAttribute(APIConstants.API_OVERVIEW_TYPE) - .equals(APIConstants.AuditLogConstants.API_PRODUCT)) { - content.setAssociatedType(APIConstants.API_PRODUCT); - } else { - content.setAssociatedType(APIConstants.API); + if (!ignoreDuplicateSwaggerContent) { + GenericArtifact apiArtifact = apiArtifactManager.getGenericArtifact(apiArtifactId); + devAPI = RegistryPersistenceUtil.getDevPortalAPIForSearch(apiArtifact); + content.setId(defResourceId); + content.setName(defResourceName); + content.setApiUUID(devAPI.getId()); + content.setApiName(devAPI.getApiName()); + content.setApiContext(devAPI.getContext()); + content.setApiProvider(devAPI.getProviderName()); + content.setApiVersion(devAPI.getVersion()); + if (apiArtifact.getAttribute(APIConstants.API_OVERVIEW_TYPE) + .equals(APIConstants.AuditLogConstants.API_PRODUCT)) { + content.setAssociatedType(APIConstants.API_PRODUCT); + } else { + content.setAssociatedType(APIConstants.API); + } + contentData.add(content); } - contentData.add(content); + } else { throw new GovernanceException("artifact id is null of " + apiPath); } + } + + /** + * This method is used to set the correct API Type for swagger.json as all API types have a swagger.json + * file in registry + * + * @param resourcePath registry resourcePath + * @param index int index + * @param content APIDefSearchContent obj. + * @param registry registry obj. + * @throws RegistryException on failure + */ + private void setAPITypeForSwagger(String resourcePath, int index, + APIDefSearchContent content, Registry registry) throws RegistryException { + String directoryPath = resourcePath.substring(0, index); + Resource directoryResource = registry.get(directoryPath); + + if (directoryResource instanceof Collection) { + Collection collection = (Collection) directoryResource; + int childrenCount = collection.getChildCount(); + String[] childPaths = collection.getChildren(); + if (childrenCount > 2) { + for (String childPath : childPaths) { + if (childPath.contains(APIConstants.API_ASYNC_API_DEFINITION_RESOURCE_NAME)) { + content.setApiType(APIDefSearchContent.ApiType.ASYNC); + break; + } else if (childPath.contains(APIConstants.GRAPHQL_SCHEMA_FILE_EXTENSION)) { + content.setApiType(APIDefSearchContent.ApiType.GRAPHQL); + break; + } else if (childPath.contains(APIConstants.WSDL_FILE_EXTENSION)) { + content.setApiType(APIDefSearchContent.ApiType.SOAP); + break; + } + } + } + if (content.getApiType() == null) { + content.setApiType(APIDefSearchContent.ApiType.REST); + } + } } } diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/APIDefSearchContent.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/APIDefSearchContent.java index ba3bfee763e8..02baa8917e55 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/APIDefSearchContent.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/APIDefSearchContent.java @@ -112,6 +112,9 @@ public void setAssociatedType(String associatedType) { } public String getApiType() { + if (apiType == null) { + return null; + } return apiType.toString(); } From 3fa71ef7387b38cfad03dcbd0da424f9297dee2d Mon Sep 17 00:00:00 2001 From: Avishka-Shamendra Date: Wed, 18 Sep 2024 12:20:03 +0530 Subject: [PATCH 4/4] Fix API definition content search with anonymous user --- .../persistence/RegistryPersistenceImpl.java | 56 +++++++++---------- .../mappings/SearchResultMappingUtil.java | 2 +- .../v1/mappings/SearchResultMappingUtil.java | 2 +- 3 files changed, 29 insertions(+), 31 deletions(-) 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 0f0a95a58d56..dbd0900f19b6 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 @@ -4114,7 +4114,7 @@ private void addAPIDefinitionSearchContent(String resourcePath, Registry registr } else { index = resourcePath.indexOf(APIConstants.API_OAS_DEFINITION_RESOURCE_NAME); // swagger.json is included in all types of APIs, hence we need to properly set the API type - setAPITypeForSwagger(resourcePath, index, content, registry); + setAPITypeForSwagger(resourcePath, index, content, registry, apiArtifactManager); } String apiPath = resourcePath.substring(0, index) + APIConstants.API_KEY; @@ -4160,39 +4160,37 @@ private void addAPIDefinitionSearchContent(String resourcePath, Registry registr * This method is used to set the correct API Type for swagger.json as all API types have a swagger.json * file in registry * - * @param resourcePath registry resourcePath - * @param index int index - * @param content APIDefSearchContent obj. - * @param registry registry obj. + * @param resourcePath registry resourcePath + * @param index int index + * @param content APIDefSearchContent obj. + * @param registry registry obj. + * @param artifactManager artifact manager * @throws RegistryException on failure */ private void setAPITypeForSwagger(String resourcePath, int index, - APIDefSearchContent content, Registry registry) throws RegistryException { - String directoryPath = resourcePath.substring(0, index); - Resource directoryResource = registry.get(directoryPath); - - if (directoryResource instanceof Collection) { - Collection collection = (Collection) directoryResource; - int childrenCount = collection.getChildCount(); - String[] childPaths = collection.getChildren(); - if (childrenCount > 2) { - for (String childPath : childPaths) { - if (childPath.contains(APIConstants.API_ASYNC_API_DEFINITION_RESOURCE_NAME)) { - content.setApiType(APIDefSearchContent.ApiType.ASYNC); - break; - } else if (childPath.contains(APIConstants.GRAPHQL_SCHEMA_FILE_EXTENSION)) { - content.setApiType(APIDefSearchContent.ApiType.GRAPHQL); - break; - } else if (childPath.contains(APIConstants.WSDL_FILE_EXTENSION)) { - content.setApiType(APIDefSearchContent.ApiType.SOAP); - break; - } - } - } - if (content.getApiType() == null) { + APIDefSearchContent content, Registry registry, + GenericArtifactManager + artifactManager) throws RegistryException { + + String apiPath = resourcePath.substring(0, index) + APIConstants.API_KEY; + Resource apiResource = registry.get(apiPath); + String apiArtifactId = apiResource.getUUID(); + if (apiArtifactId != null) { + GenericArtifact artifact = artifactManager.getGenericArtifact(apiArtifactId); + String type = artifact.getAttribute(APIConstants.API_OVERVIEW_TYPE); + if (APIConstants.API_TYPE_SOAP.equals(type) || + APIConstants.API_TYPE_SOAPTOREST.equals(type)) { + content.setApiType(APIDefSearchContent.ApiType.SOAP); + } else if (APIConstants.API_TYPE_GRAPHQL.equals(type)) { + content.setApiType(APIDefSearchContent.ApiType.GRAPHQL); + } else if (APIConstants.API_TYPE_WS.equals(type) || + APIConstants.API_TYPE_WEBHOOK.equals(type) || + APIConstants.API_TYPE_SSE.equals(type) || + APIConstants.API_TYPE_WEBSUB.equals(type)) { + content.setApiType(APIDefSearchContent.ApiType.ASYNC); + } else { content.setApiType(APIDefSearchContent.ApiType.REST); } } - } } 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/SearchResultMappingUtil.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/SearchResultMappingUtil.java index e655ad7cb71f..6bddea50a028 100644 --- 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/SearchResultMappingUtil.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/SearchResultMappingUtil.java @@ -257,7 +257,7 @@ public static void setPaginationParams(SearchResultListDTO resultListDTO, String apiDefSearchResultDTO.setApiType(apiDefResult.getApiType()); apiDefSearchResultDTO.setAssociatedType(apiDefResult.getAssociatedType()); if (apiDefResult.getName().contains("swagger")) { - apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " Swagger Definition"); + apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " REST API Definition"); } else if (apiDefResult.getName().contains("graphql")) { apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " GraphQL Definition"); } else if (apiDefResult.getName().contains("async")) { diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/mappings/SearchResultMappingUtil.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/mappings/SearchResultMappingUtil.java index ff0530f40d5a..6a137f84fefb 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/mappings/SearchResultMappingUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/mappings/SearchResultMappingUtil.java @@ -200,7 +200,7 @@ public static void setPaginationParams(SearchResultListDTO resultListDTO, String apiDefSearchResultDTO.setApiType(apiDefResult.getApiType()); apiDefSearchResultDTO.setAssociatedType(apiDefResult.getAssociatedType()); if (apiDefResult.getName().contains("swagger")) { - apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " Swagger Definition"); + apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " REST API Definition"); } else if (apiDefResult.getName().contains("graphql")) { apiDefSearchResultDTO.setName(apiDefResult.getApiName() + " GraphQL Definition"); } else if (apiDefResult.getName().contains("async")) {