diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/APIProvider.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/APIProvider.java index 028fd054b660..fcd13d13c6c4 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/APIProvider.java +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/APIProvider.java @@ -33,6 +33,7 @@ import org.wso2.carbon.apimgt.api.model.policy.SubscriptionPolicy; import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.util.List; import java.util.Map; import java.util.Set; @@ -65,6 +66,31 @@ public interface APIProvider extends APIManager { Comment getComment(ApiTypeWrapper apiTypeWrapper, String commentId, Integer replyLimit, Integer replyOffset) throws APIManagementException; + /** + * This method is to delete Sequence Backend by type + * + * @param apiUUID API Id + * @param type Key type + * @throws APIManagementException If failed to delete Sequence Backend + */ + void deleteCustomBackendByID(String apiUUID, String type) throws APIManagementException; + + /** + * This method is to delete all Sequence Backends by APIID + * + * @param apiUUID API ID + * @throws APIManagementException If failed to delete Sequence Backend + */ + void deleteCustomBackendByAPIID(String apiUUID) throws APIManagementException; + + /** + * This method is to delete Sequence Backends of a specific revision + * @param apiUUID API Id + * @param revisionId Revision Id + * @throws APIManagementException If failed to delete Sequence Backend + */ + void deleteSequenceBackendByRevision(String apiUUID, String revisionId) throws APIManagementException; + /** * @param apiTypeWrapper Api type wrapper * @param parentCommentID @@ -315,6 +341,39 @@ List getSubscriptionsOfAPI(String apiName, String apiVersion, Str */ API updateAPI(API api, API existingAPI) throws APIManagementException, FaultGatewaysException; + /** + * This method is to update Sequence Backend + * + * @param api API + * @param type Key Type + * @param sequence Sequence Content + * @param seqName Sequence Name + * @param customBackendUUID Sequence Id + * @throws APIManagementException If not updated + */ + void updateCustomBackend(String api, String type, String sequence, String seqName, String customBackendUUID) + throws APIManagementException; + + /** + * THis method is to retrieve Sequence Backend data + * + * @param apiUUID API Id + * @param type Key Type + * @return SequenceBackendData object + * @throws APIManagementException If data is not properly retrieved + */ + SequenceBackendData getCustomBackendByAPIUUID(String apiUUID, String type) throws APIManagementException; + + /** + * This method is to retrieve all Sequence Backends of an API + * + * @param apiUUID API Id + * @return List of Sequence Backends + * @throws APIManagementException If not found + */ + + List getAllSequenceBackendsByAPIUUID(String apiUUID) throws APIManagementException; + /** * Create a new version of the api, with version newVersion * diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/ExceptionCodes.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/ExceptionCodes.java index a5033939ff01..26d6bf917fa9 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/ExceptionCodes.java +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/ExceptionCodes.java @@ -482,6 +482,9 @@ public enum ExceptionCodes implements ErrorHandler { // API import/export related codes ERROR_READING_META_DATA(900907, "Error while reading meta information from the definition", 400, "Error while reading meta information from the definition"), + + ERROR_READING_CUSTOM_SEQUENCE(900908, "Error while reading Custom Sequence from the API Endpoint Configuration", + 400, "Error while reading Custom Sequence from the API Endpoint Configuration"), ERROR_READING_PARAMS_FILE(900908, "Error while reading meta information from the params file", 400, "Error while reading meta information from the params file"), ERROR_FETCHING_DEFINITION_FILE(900909, "Cannot find the definition file of the project", 400, @@ -531,6 +534,8 @@ public enum ExceptionCodes implements ErrorHandler { "Required attributes(s) %s for api policy specification %s are either missing or empty"), OPERATION_POLICY_NOT_FOUND(902010, "API Policy Not Found", 404, "Requested api policy with id '%s' not found"), + CUSTOM_BACKEND_NOT_FOUND(903250, "Sequence Backend not found", + 404, "Requested Sequence Backend of API '%s' not found"), OPERATION_POLICY_ALREADY_EXISTS(903001, "The API Policy already exists.", 409, "An Operation Policy with name '%s' and version '%s' already exists"), diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/API.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/API.java index cc2489b9b635..a65e8e9aff6e 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/API.java +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/API.java @@ -86,6 +86,7 @@ public class API implements Serializable { private boolean apiResourcePatternsChanged; private String status; + private String sequence; private String technicalOwner; private String technicalOwnerEmail; @@ -786,6 +787,14 @@ public void setRating(float rating) { this.rating = rating; } + public void setSequence(String sequence) { + this.sequence = sequence; + } + + public String getSequence() { + return sequence; + } + public void setLatest(boolean latest) { isLatest = latest; } diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/SequenceBackendData.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/SequenceBackendData.java new file mode 100644 index 000000000000..a2774dd021e1 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/SequenceBackendData.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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; + +public class SequenceBackendData { + private String Id; + private String sequence; + private String type; + private String name; + private String apiUUID; + private String revisionUUID; + + public String getSequence() { + return sequence; + } + + public void setSequence(String sequence) { + this.sequence = sequence; + } + + public String getId() { + return Id; + } + + public void setId(String id) { + Id = id; + } + + 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 getApiUUID() { + return apiUUID; + } + + public void setApiUUID(String apiUUID) { + this.apiUUID = apiUUID; + } + + public String getRevisionUUID() { + return revisionUUID; + } + + public void setRevisionUUID(String revisionUUID) { + this.revisionUUID = revisionUUID; + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/InMemoryAPIDeployer.java b/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/InMemoryAPIDeployer.java index dbb9d0ecac21..768ee1bee2db 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/InMemoryAPIDeployer.java +++ b/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/InMemoryAPIDeployer.java @@ -33,7 +33,6 @@ import org.apache.synapse.transport.dynamicconfigurations.DynamicProfileReloaderHolder; import org.wso2.carbon.apimgt.api.APIManagementException; import org.wso2.carbon.apimgt.api.ExceptionCodes; -import org.wso2.carbon.apimgt.api.APIManagementException; import org.wso2.carbon.apimgt.api.gateway.GatewayAPIDTO; import org.wso2.carbon.apimgt.api.gateway.GatewayContentDTO; import org.wso2.carbon.apimgt.api.gateway.GraphQLSchemaDTO; @@ -330,6 +329,8 @@ private void unDeployAPI(APIGatewayAdmin apiGatewayAdmin, DeployAPIInGatewayEven gatewayAPIDTO); GatewayUtils.setEndpointsToBeRemoved(apiProductIdentifier, associatedApi.getUuid(), gatewayAPIDTO); + GatewayUtils.setCustomBackendToBeRemoved(apiProductIdentifier, associatedApi.getUuid(), + gatewayAPIDTO); } } else { API api = new API(new APIIdentifier(gatewayEvent.getProvider(), gatewayEvent.getName(), @@ -350,6 +351,8 @@ private void unDeployAPI(APIGatewayAdmin apiGatewayAdmin, DeployAPIInGatewayEven } GatewayUtils.setCustomSequencesToBeRemoved(api, gatewayAPIDTO); + GatewayUtils.setCustomBackendToBeRemoved(gatewayAPIDTO); + } gatewayAPIDTO.setLocalEntriesToBeRemove( GatewayUtils diff --git a/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/common/APIMgtLatencyStatsHandler.java b/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/common/APIMgtLatencyStatsHandler.java index 2d14790fca61..4b06dc73f491 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/common/APIMgtLatencyStatsHandler.java +++ b/components/apimgt/org.wso2.carbon.apimgt.gateway/src/main/java/org/wso2/carbon/apimgt/gateway/handlers/common/APIMgtLatencyStatsHandler.java @@ -109,11 +109,12 @@ public boolean handleResponse(MessageContext messageContext) { org.apache.axis2.context.MessageContext.setCurrentMessageContext(axis2MC); if (messageContext.getProperty(APIMgtGatewayConstants.BACKEND_REQUEST_END_TIME) == null) { messageContext.setProperty(APIMgtGatewayConstants.BACKEND_REQUEST_END_TIME, System.currentTimeMillis()); - if (APIUtil.isAnalyticsEnabled()) { - long executionStartTime = Long.parseLong((String) messageContext.getProperty(APIMgtGatewayConstants - .BACKEND_REQUEST_START_TIME)); - messageContext.setProperty(APIMgtGatewayConstants.BACKEND_LATENCY, System.currentTimeMillis() - - executionStartTime); + if (APIUtil.isAnalyticsEnabled() + && messageContext.getProperty(APIMgtGatewayConstants.BACKEND_REQUEST_START_TIME) != null) { + long executionStartTime = Long.parseLong( + (String) messageContext.getProperty(APIMgtGatewayConstants.BACKEND_REQUEST_START_TIME)); + messageContext.setProperty(APIMgtGatewayConstants.BACKEND_LATENCY, + System.currentTimeMillis() - executionStartTime); } } return true; 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 d37671ad8fc2..ce4318f69714 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 @@ -1783,6 +1783,8 @@ private ConfigParameters() { public static final String ENDPOINT_TYPE_SERVICE = "service"; public static final String ENDPOINT_TYPE_ADDRESS = "address"; public static final String ENDPOINT_TYPE_AWSLAMBDA = "awslambda"; + public static final String ENDPOINT_TYPE_SEQUENCE = "sequence_backend"; + public static final String SEQUENCE_DATA = "sequence"; public static final String ENDPOINT_PRODUCTION_FAILOVERS = "production_failovers"; public static final String ENDPOINT_SANDBOX_FAILOVERS = "sandbox_failovers"; public static final String ENDPOINT_PRODUCTION_ENDPOINTS = "production_endpoints"; 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 c0292daa29cd..2b5b30d75d69 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 @@ -72,6 +72,7 @@ import org.wso2.carbon.apimgt.api.model.BlockConditionsDTO; import org.wso2.carbon.apimgt.api.model.Comment; import org.wso2.carbon.apimgt.api.model.CommentList; +import org.wso2.carbon.apimgt.api.model.SequenceBackendData; import org.wso2.carbon.apimgt.api.model.DeployedAPIRevision; import org.wso2.carbon.apimgt.api.model.Documentation; import org.wso2.carbon.apimgt.api.model.Documentation.DocumentSourceType; @@ -1167,6 +1168,30 @@ private void updateAPIPolicies(API api, String tenantDomain) throws APIManagemen apiMgtDAO.updateAPIPoliciesMapping(api.getUuid(), api.getUriTemplates(), api.getApiPolicies(), tenantDomain); } + @Override + public void updateCustomBackend(String apiUUID, String type, String sequence, String seqName, + String customBackendUUID) throws APIManagementException { + apiMgtDAO.updateCustomBackend(apiUUID, seqName, sequence, type, customBackendUUID); + } + + @Override + public SequenceBackendData getCustomBackendByAPIUUID(String apiUUID, String type) throws APIManagementException { + return apiMgtDAO.getCustomBackendByAPIUUID(apiUUID, type); + } + + public List getAllSequenceBackendsByAPIUUID(String apiUUID) throws APIManagementException { + return apiMgtDAO.getSequenceBackendsByAPIUUID(apiUUID); + } + + @Override + public void deleteCustomBackendByAPIID(String apiUUID) throws APIManagementException { + apiMgtDAO.deleteCustomBackendByAPIID(apiUUID); + } + @Override + public void deleteCustomBackendByID(String apiUUID, String type) throws APIManagementException { + apiMgtDAO.deleteCustomBackend(apiUUID, type); + } + private void validateKeyManagers(API api) throws APIManagementException { Map tenantKeyManagers = KeyManagerHolder.getGlobalAndTenantKeyManagers(tenantDomain); @@ -2561,6 +2586,8 @@ public void deleteAPI(String apiUuid, String organization) throws APIManagementE // DB delete operations if (!isError && api != null) { try { + // Remove Custom Backend entries of the API + deleteCustomBackendByAPIID(apiUuid); deleteAPIRevisions(apiUuid, organization); deleteAPIFromDB(api); if (log.isDebugEnabled()) { @@ -5361,6 +5388,12 @@ public boolean isSubscriptionValidationDisabled(String uuid) throws APIManagemen return !"ENABLED".equalsIgnoreCase(status); } + @Override + public void deleteSequenceBackendByRevision(String apiUUID, String revisionId) + throws APIManagementException { + apiMgtDAO.deleteCustomBackendByRevision(apiUUID, "0"); + } + @Override public API getAPIbyUUID(String uuid, String organization) throws APIManagementException { Organization org = new Organization(organization); @@ -5383,6 +5416,7 @@ public API getAPIbyUUID(String uuid, String organization) throws APIManagementEx if (APIConstants.API_SUBTYPE_AI_API.equals(api.getSubtype())) { populateAiConfiguration(api); } + if (APIUtil.isSequenceDefined(api.getInSequence()) || APIUtil.isSequenceDefined(api.getOutSequence()) || APIUtil.isSequenceDefined(api.getFaultSequence())) { if (migrationEnabled == null) { @@ -6099,6 +6133,7 @@ public String addAPIRevision(APIRevision apiRevision, String organization) throw ExceptionCodes.from(ExceptionCodes.API_REVISION_UUID_NOT_FOUND)); } apiRevision.setRevisionUUID(revisionUUID); + try { apiMgtDAO.addAPIRevision(apiRevision); AIConfiguration configuration = apiMgtDAO.getAIConfiguration(apiRevision.getApiUUID(), null, organization); @@ -6302,6 +6337,7 @@ public void deployAPIRevision(String apiId, String apiRevisionUUID, } } apiMgtDAO.addAPIRevisionDeployment(apiRevisionUUID, apiRevisionDeployments); + WorkflowExecutor revisionDeploymentWFExecutor = getWorkflowExecutor( WorkflowConstants.WF_TYPE_AM_REVISION_DEPLOYMENT); diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/ApiMgtDAO.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/ApiMgtDAO.java index ae8849691258..2775c930cb3a 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/ApiMgtDAO.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/ApiMgtDAO.java @@ -61,6 +61,7 @@ import org.wso2.carbon.apimgt.api.model.BlockConditionsDTO; import org.wso2.carbon.apimgt.api.model.Comment; import org.wso2.carbon.apimgt.api.model.CommentList; +import org.wso2.carbon.apimgt.api.model.SequenceBackendData; import org.wso2.carbon.apimgt.api.model.DeployedAPIRevision; import org.wso2.carbon.apimgt.api.model.Environment; import org.wso2.carbon.apimgt.api.model.GatewayPolicyData; @@ -2796,9 +2797,8 @@ public List getSubscriptionsOfAPI(String apiName, String apiVersi } /** - * @param apiName Name of the API - * @param apiVersion Version of the API - * @param provider Name of API creator + * @param apiUUID UUID of the API + * @param organization Organization of the API * @return All subscriptions of a given API * @throws org.wso2.carbon.apimgt.api.APIManagementException */ @@ -11213,6 +11213,167 @@ public List retrieveSavedEmailList(String userName, String stakeHolder) } + /** + * + * @param apiUUID UUID of API + * @param revisionUUID Revision ID of the API + * @return A HashMap with Custom Backend data + * @throws APIManagementException + */ + public Map retrieveCustomBackendOfAPIRevision(String apiUUID, String revisionUUID) throws APIManagementException { + String sqlQuery = SQLConstants.CustomBackendConstants.GET_CUSTOM_BACKEND_OF_API_REVISION; + Map map = new HashMap<>(); + ResultSet resultSet = null; + try (Connection connection = APIMgtDBUtil.getConnection(); + PreparedStatement ps = connection.prepareStatement(sqlQuery)) { + ps.setString(1, apiUUID); + ps.setString(2, revisionUUID); + resultSet = ps.executeQuery(); + while (resultSet.next()) { + map.put("sequence", resultSet.getString("SEQUENCE")); + map.put("endpoint_type", resultSet.getString("TYPE")); + map.put("sequence_name", resultSet.getString("NAME")); + } + } catch (SQLException ex) { + handleException("Error retrieving Custom Backend of an API: " + apiUUID, ex); + } + return map; + } + + public SequenceBackendData getCustomBackendByAPIUUID(String apiUUID, String type) throws APIManagementException { + String sqlQuery = SQLConstants.CustomBackendConstants.GET_API_SPECIFIC_CUSTOM_BACKEND_FROM_SEQUENCE_ID; + SequenceBackendData sequenceBackendData = null; + try (Connection con = APIMgtDBUtil.getConnection(); PreparedStatement ps = con.prepareStatement(sqlQuery)) { + ps.setString(1, apiUUID); + ps.setString(2, type); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + sequenceBackendData = new SequenceBackendData(); + sequenceBackendData.setApiUUID(apiUUID); + sequenceBackendData.setRevisionUUID("0"); + sequenceBackendData.setSequence(IOUtils.toString(rs.getBinaryStream("SEQUENCE"))); + sequenceBackendData.setId(rs.getString("ID")); + sequenceBackendData.setName(rs.getString("NAME")); + sequenceBackendData.setType(type); + } + } + } catch (SQLException | IOException ex) { + handleException("Error when fetching Custom Backend data for API: " + apiUUID, ex); + } + return sequenceBackendData; + } + + public List getSequenceBackendsByAPIUUID(String apiUUID) throws APIManagementException { + String sqlQuery = SQLConstants.CustomBackendConstants.GET_ALL_API_SPECIFIC_CUSTOM_BACKENDS; + List backendDataList = new ArrayList<>(); + try (Connection con = APIMgtDBUtil.getConnection(); PreparedStatement ps = con.prepareStatement(sqlQuery)) { + ps.setString(1, apiUUID); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + SequenceBackendData sqBackend = new SequenceBackendData(); + sqBackend.setApiUUID(apiUUID); + sqBackend.setId(rs.getString("ID")); + sqBackend.setName(rs.getString("NAME")); + sqBackend.setType(rs.getString("TYPE")); + backendDataList.add(sqBackend); + } + } + return backendDataList; + } catch (SQLException ex) { + handleException("Error when retrieving Sequence Backends of API: " + apiUUID, ex); + } + return null; + } + + public String getCustomBackendSequenceOfAPIByUUID(String apiUUID, String type) throws APIManagementException { + String sqlQuery = SQLConstants.CustomBackendConstants.GET_API_SPECIFIC_CUSTOM_BACKEND_FROM_SEQUENCE_ID; + String sequence = null; + try (Connection con = APIMgtDBUtil.getConnection(); PreparedStatement ps = con.prepareStatement(sqlQuery)) { + ps.setString(1, apiUUID); + ps.setString(2, type); + + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + try (InputStream in = rs.getBinaryStream("SEQUENCE")) { + sequence = IOUtils.toString(in); + } catch (IOException ex) { + handleException("Error reading the sequence of Custom Backend API: " + apiUUID, ex); + } + } + } + } catch (SQLException ex) { + handleException("Error when fetching Custom Backend data of API: " + apiUUID, ex); + } + + if (sequence == null) { + throw new APIManagementException("Custom Backend Content cannot be empty"); + } + return sequence; + } + + public Map getCustomBackendOfAPIByUUID(String backendUUID, String apiUUID, String type, + boolean isInfo) throws APIManagementException { + String sqlQuery; + Map endpointConfig = new HashMap<>(); + boolean isRevisioned = checkAPIUUIDIsARevisionUUID(apiUUID) != null; + if (isRevisioned) { + sqlQuery = SQLConstants.CustomBackendConstants.GET_REVISION_SPECIFIC_CUSTOM_BACKEND_FROM_SEQUENCE_ID; + } else { + sqlQuery = SQLConstants.CustomBackendConstants.GET_API_SPECIFIC_CUSTOM_BACKEND_FROM_SEQUENCE_ID; + } + ResultSet resultSet = null; + try (Connection connection = APIMgtDBUtil.getConnection(); + PreparedStatement ps = connection.prepareStatement(sqlQuery)) { + ps.setString(1, backendUUID); + ps.setString(2, apiUUID); + ps.setString(3, type); + resultSet = ps.executeQuery(); + while (resultSet.next()) { + if (!isInfo) { + try (InputStream in = resultSet.getBinaryStream("SEQUENCE")) { + endpointConfig.put("sequence", in); + } catch (IOException ex) { + handleException( + "Error reading Sequence Content of Custom Backend: " + backendUUID + " API: " + apiUUID, + ex); + } + } + endpointConfig.put("type", resultSet.getString("TYPE")); + endpointConfig.put("sequence_name", resultSet.getString("NAME")); + endpointConfig.put("endpoint_type", "custom_backend"); + endpointConfig.put("sequence_id", resultSet.getString("ID")); + } + } catch (SQLException ex) { + handleException("Error when retrieving Custom Backend of API: " + apiUUID, ex); + } + return endpointConfig; + } + + /** + * + * @param apiUUID API UUID + * @return HashMap with Custom Backend data + * @throws APIManagementException + */ + public Map retrieveCustomBackendOfAPI(String apiUUID) throws APIManagementException { + String sqlQuery = SQLConstants.CustomBackendConstants.GET_CUSTOM_BACKEND_OF_API_DEFAULT_REVISION; + Map map = new HashMap<>(); + ResultSet resultSet = null; + try (Connection connection = APIMgtDBUtil.getConnection(); + PreparedStatement ps = connection.prepareStatement(sqlQuery)) { + ps.setString(1, apiUUID); + resultSet = ps.executeQuery(); + while (resultSet.next()) { + map.put("type", resultSet.getString("TYPE")); + map.put("sequence_name", resultSet.getString("NAME")); + map.put("endpoint_type", "custom_backend"); + } + } catch (SQLException ex) { + handleException("Error retrieving Custom Backend of an API: " + apiUUID, ex); + } + return map; + } + /** * This method will delete all email alert subscriptions details from tables * @@ -17403,6 +17564,9 @@ public void addAPIRevision(APIRevision apiRevision) throws APIManagementExceptio // Retrieve API ID APIIdentifier apiIdentifier = APIUtil.getAPIIdentifierFromUUID(apiRevision.getApiUUID()); + + // Insert Custom Backend if Provided + int apiId = getAPIID(apiRevision.getApiUUID(), connection); int tenantId = APIUtil.getTenantId(APIUtil.replaceEmailDomainBack(apiIdentifier.getProviderName())); String tenantDomain = APIUtil.getTenantDomainFromTenantId(tenantId); @@ -17602,6 +17766,8 @@ public void addAPIRevision(APIRevision apiRevision) throws APIManagementExceptio insertGraphQLComplexityStatement.executeBatch(); updateLatestRevisionNumber(connection, apiRevision.getApiUUID(), apiRevision.getId()); addAPIRevisionMetaData(connection, apiRevision.getApiUUID(), apiRevision.getRevisionUUID()); + // Add Custom Backend + revisionCustomBackend(apiRevision, connection); connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -18453,6 +18619,7 @@ public void restoreAPIRevision(APIRevision apiRevision) throws APIManagementExce } restoreAPIPolicies(apiRevision, tenantDomain, uriTemplateMap, connection); + restoreCustomBackend(apiRevision, connection); insertScopeResourceMappingStatement.executeBatch(); insertProductResourceMappingStatement.executeBatch(); @@ -18590,6 +18757,9 @@ public void deleteAPIRevision(APIRevision apiRevision) throws APIManagementExcep // Removing related revision entries from operation policies deleteAllAPISpecificOperationPoliciesByAPIUUID(connection, apiRevision.getApiUUID(), apiRevision.getRevisionUUID()); + // Removing related Custom Backend entries + deleteAllCustomBackendsOfAPIRevision(apiRevision.getApiUUID(), apiRevision.getRevisionUUID(), connection); + connection.commit(); } catch (SQLException e) { connection.rollback(); @@ -18602,6 +18772,18 @@ public void deleteAPIRevision(APIRevision apiRevision) throws APIManagementExcep } } + private void deleteAllCustomBackendsOfAPIRevision(String apiUUID, String revisionUUID, Connection connection) throws APIManagementException { + String deleteSqlQuery = SQLConstants.CustomBackendConstants.DELETE_CUSTOM_BACKEND_BY_REVISION; + try (PreparedStatement pstmt = connection.prepareStatement(deleteSqlQuery)) { + connection.setAutoCommit(false); + pstmt.setString(1, apiUUID); + pstmt.setString(2, revisionUUID); + pstmt.executeUpdate(); + } catch (SQLException ex) { + handleException("Error when deleting Custom Backend of API: " + apiUUID, ex); + } + } + /** * Adds an API Product revision record to the database * @@ -21329,6 +21511,115 @@ public void updateAPIPoliciesMapping(String apiUUID, Set uriTemplat } } + /** + * This method is to update Sequence Backend data + * + * @param apiUUID API Id + * @param sequenceName Sequence Name + * @param sequence Sequence Content + * @param type Key type + * @param backendUUID Sequence Id + * @throws APIManagementException If not properly updated + */ + public void updateCustomBackend(String apiUUID, String sequenceName, String sequence, String type, + String backendUUID) throws APIManagementException { + // delete current working copy + String deleteCustomBackedQuery = SQLConstants.CustomBackendConstants.DELETE_CUSTOM_BACKEND_BY_API_AND_TYPE; + try (Connection connection = APIMgtDBUtil.getConnection(); + PreparedStatement prepStmt = connection.prepareStatement(deleteCustomBackedQuery)) { + try { + connection.setAutoCommit(false); + prepStmt.setString(1, apiUUID); + prepStmt.setString(2, type); + prepStmt.executeUpdate(); + addCustomBackend(apiUUID, sequenceName, null, sequence, type, connection, backendUUID); + connection.commit(); + } catch (SQLException ex) { + connection.rollback(); + handleException("Error while adding Custom Backend for API : " + apiUUID, ex); + } + } catch (SQLException e) { + handleException("Error while adding Custom Backend for API : " + apiUUID, e); + } + } + + public void deleteCustomBackend(String apiUUID, String type) throws APIManagementException { + String deleteCustomBackedQuery = SQLConstants.CustomBackendConstants.DELETE_CUSTOM_BACKEND; + try (Connection connection = APIMgtDBUtil.getConnection(); + PreparedStatement prepStmt = connection.prepareStatement(deleteCustomBackedQuery)) { + try { + connection.setAutoCommit(false); + prepStmt.setString(1, apiUUID); + prepStmt.setString(2, type); + prepStmt.executeUpdate(); + connection.commit(); + } catch (SQLException ex) { + connection.rollback(); + handleException("Error while deleting Custom Backend for API : " + apiUUID, ex); + } + } catch (SQLException e) { + handleException("Error while deleting Custom Backend for API : " + apiUUID, e); + } + } + + public void deleteCustomBackendByAPIID(String apiUUID) throws APIManagementException { + String deleteCustomBackendSql = SQLConstants.CustomBackendConstants.DELETE_CUSTOM_BACKEND_BY_API; + try (Connection connection = APIMgtDBUtil.getConnection(); + PreparedStatement prepStmt = connection.prepareStatement(deleteCustomBackendSql)) { + try { + connection.setAutoCommit(false); + prepStmt.setString(1, apiUUID); + connection.commit(); + } catch (SQLException ex) { + connection.rollback(); + handleException("Error while deleting Custom Backend for API: " + apiUUID, ex); + } + } catch (SQLException ex) { + handleException("Error while deleting Custom Backend for API: " + apiUUID, ex); + } + } + + public void deleteCustomBackendByRevision(String apiUUID, String revisionUUID) throws APIManagementException { + String deleteSqlQuery = SQLConstants.CustomBackendConstants.DELETE_CUSTOM_BACKEND_BY_REVISION; + try (Connection con = APIMgtDBUtil.getConnection(); + PreparedStatement ps = con.prepareStatement(deleteSqlQuery)) { + try { + con.setAutoCommit(false); + ps.setString(1, apiUUID); + ps.setString(2, revisionUUID); + ps.executeUpdate(); + con.commit(); + } catch (SQLException ex) { + con.rollback(); + handleException("Error deleting Custom Backend for Revision: " + apiUUID, ex); + } + } catch (SQLException ex) { + handleException("Error deleting Custom Backend for Revision: " + apiUUID, ex); + } + } + + public void addCustomBackend(String apiUUID, String sequenceName, String revision, String sequence, + String type, Connection connection, String backendUUID) throws APIManagementException { + String insertCustomBackendQuery = SQLConstants.CustomBackendConstants.ADD_CUSTOM_BACKEND; + try (PreparedStatement prepStmt = connection.prepareStatement(insertCustomBackendQuery)) { + connection.setAutoCommit(false); + prepStmt.setString(1, backendUUID); + prepStmt.setString(2, apiUUID); + try (InputStream seqStream = new ByteArrayInputStream(sequence.getBytes())) { + prepStmt.setBinaryStream(3, seqStream); + } + prepStmt.setString(4, type); + if (revision == null) { + revision = "0"; + } + prepStmt.setString(5, revision); + prepStmt.setString(6, sequenceName); + prepStmt.executeUpdate(); + } catch (SQLException | IOException e) { + handleException("Error while adding Custom Backend for API : " + apiUUID, e); + } + } + /** * This method will add API level policy mappings to the database. * @@ -21513,6 +21804,39 @@ private List getAPIPolicyMapping(String apiUUID, String revisio return policyList; } + private void revisionCustomBackend(APIRevision apiRevision, Connection connection) + throws SQLException, APIManagementException { + String addCBSqlQuery = SQLConstants.CustomBackendConstants.ADD_CUSTOM_BACKEND; + String getCBSQLQuery = SQLConstants.CustomBackendConstants.GET_ALL_API_SPECIFIC_CUSTOM_BACKENDS; + try (PreparedStatement getPstmt = connection.prepareStatement(getCBSQLQuery); + PreparedStatement addPstmt = connection.prepareStatement(addCBSqlQuery)) { + connection.setAutoCommit(false); + getPstmt.setString(1, apiRevision.getApiUUID()); + List sequenceBackendDataList = new ArrayList<>(); + int count = 0; + + try (ResultSet rs = getPstmt.executeQuery()) { + while (rs.next()) { + addPstmt.setString(1, rs.getString("ID")); + addPstmt.setString(2, apiRevision.getApiUUID()); + addPstmt.setBinaryStream(3, rs.getBinaryStream("SEQUENCE")); + addPstmt.setString(4, rs.getString("TYPE")); + addPstmt.setString(5, apiRevision.getRevisionUUID()); + addPstmt.setString(6, rs.getString("NAME")); + addPstmt.addBatch(); + count++; + } + } + + if (count > 0) { + addPstmt.executeBatch(); + } + } catch (SQLException ex) { + handleException("Error while adding Custom Backends to the database of API: " + apiRevision.getApiUUID(), + ex); + } + } + /** * Create a revision of API policis. This will clone the policy and policy mapping with each revision * @@ -21620,6 +21944,35 @@ private void handlePolicyCloningWhenRevisioning(OperationPolicy policy, String a } } + private void restoreCustomBackend(APIRevision apiRevision, Connection connection) throws SQLException { + String deleteSql = SQLConstants.CustomBackendConstants.DELETE_WORKING_COPY_OF_CUSTOM_BACKEND; + String getSql = SQLConstants.CustomBackendConstants.GET_CUSTOM_BACKEND_OF_API_REVISION; + String addSql = SQLConstants.CustomBackendConstants.ADD_CUSTOM_BACKEND; + try (PreparedStatement pstmt = connection.prepareStatement(deleteSql); + PreparedStatement pstmtGet = connection.prepareStatement(getSql); + PreparedStatement pstmtAdd = connection.prepareStatement(addSql)) { + connection.setAutoCommit(false); + pstmt.setString(1, apiRevision.getApiUUID()); + pstmt.executeUpdate(); + + pstmtGet.setString(1, apiRevision.getApiUUID()); + pstmtGet.setString(2, apiRevision.getRevisionUUID()); + + try(ResultSet rs = pstmtGet.executeQuery()) { + while(rs.next()) { + pstmtAdd.setString(1, rs.getString("ID")); + pstmtAdd.setString(2, apiRevision.getApiUUID()); + pstmtAdd.setBinaryStream(3, rs.getBinaryStream("SEQUENCE")); + pstmtAdd.setString(4, rs.getString("TYPE")); + pstmtAdd.setString(5, "0"); + pstmtAdd.setString(6, rs.getString("NAME")); + pstmtAdd.addBatch(); + } + } + pstmtAdd.executeBatch(); + } + } + /** * Restore a revision of API policies. This will copy the policy and policy mapping to working copy. * diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java index 5c2be2aa2e72..daf7622bed74 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/dao/constants/SQLConstants.java @@ -4471,8 +4471,27 @@ public static class SystemConfigsConstants { * Static class to hold database queries related to AM_TRANSACTION_RECORDS table */ public static class TransactionCountConstants { - public static final String INSERT_TRANSACTION_COUNT = "INSERT INTO AM_TRANSACTION_RECORDS " + "(ID, HOST, SERVER_ID, SERVER_TYPE, COUNT, RECORDED_TIME) " + "VALUES (?,?,?,?,?,?)"; - public static final String GET_TRANSACTION_COUNT = "SELECT SUM(COUNT) FROM AM_TRANSACTION_RECORDS " + "WHERE RECORDED_TIME >= ? AND RECORDED_TIME <= ?"; + public static final String INSERT_TRANSACTION_COUNT = + "INSERT INTO AM_TRANSACTION_RECORDS " + "(ID, HOST, SERVER_ID, SERVER_TYPE, COUNT, RECORDED_TIME) " + + "VALUES (?,?,?,?,?,?)"; + public static final String GET_TRANSACTION_COUNT = + "SELECT SUM(COUNT) FROM AM_TRANSACTION_RECORDS " + "WHERE RECORDED_TIME >= ? AND RECORDED_TIME <= ?"; + } + + public static class CustomBackendConstants { + public static final String ADD_CUSTOM_BACKEND = + "INSERT INTO AM_API_SEQUENCE_BACKEND (ID,API_UUID,SEQUENCE,TYPE,REVISION_UUID,NAME) " + + "VALUES (?,?,?,?,?,?)"; + public static final String DELETE_WORKING_COPY_OF_CUSTOM_BACKEND = "DELETE FROM AM_API_SEQUENCE_BACKEND WHERE API_UUID = ? AND REVISION_UUID = '0'"; + public static final String DELETE_CUSTOM_BACKEND = "DELETE FROM AM_API_SEQUENCE_BACKEND WHERE API_UUID = ? AND TYPE = ? AND REVISION_UUID = '0'"; + public static final String DELETE_CUSTOM_BACKEND_BY_API_AND_TYPE = "DELETE FROM AM_API_SEQUENCE_BACKEND WHERE API_UUID = ? AND TYPE = ? AND REVISION_UUID = '0'"; + public static final String DELETE_CUSTOM_BACKEND_BY_REVISION = "DELETE FROM AM_API_SEQUENCE_BACKEND WHERE API_UUID = ? AND REVISION_UUID = ?"; + public static final String DELETE_CUSTOM_BACKEND_BY_API = "DELETE FROM AM_API_SEQUENCE_BACKEND WHERE API_UUID = ?"; + public static final String GET_CUSTOM_BACKEND_OF_API_REVISION = "SELECT ID, NAME, SEQUENCE, TYPE FROM AM_API_SEQUENCE_BACKEND WHERE API_UUID = ? AND REVISION_UUID = ?"; + public static final String GET_CUSTOM_BACKEND_OF_API_DEFAULT_REVISION = "SELECT ACB.NAME, ACB.TYPE FROM AM_API_SEQUENCE_BACKEND WHERE API_UUID = ? AND REVISION_UUID = '0'"; + public static final String GET_REVISION_SPECIFIC_CUSTOM_BACKEND_FROM_SEQUENCE_ID = "SELECT ACB.ID, ACB.NAME, ACB.SEQUENCE, ACB.TYPE FROM AM_API_SEQUENCE_BACKEND ACB WHERE ACB.ID = ? AND ACB.REVISION_UUID = ? AND ACB.TYPE = ?"; + public static final String GET_API_SPECIFIC_CUSTOM_BACKEND_FROM_SEQUENCE_ID = "SELECT ACB.ID, ACB.NAME, ACB.SEQUENCE, ACB.TYPE FROM AM_API_SEQUENCE_BACKEND ACB WHERE ACB.API_UUID = ? AND ACB.REVISION_UUID = '0' AND ACB.TYPE = ?"; + public static final String GET_ALL_API_SPECIFIC_CUSTOM_BACKENDS = "SELECT ACB.ID, ACB.NAME, ACB.SEQUENCE, ACB.TYPE FROM AM_API_SEQUENCE_BACKEND ACB WHERE ACB.API_UUID = ? AND ACB.REVISION_UUID = '0'"; } } diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/importexport/ImportExportConstants.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/importexport/ImportExportConstants.java index 908592a483fc..6aca3a4a3696 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/importexport/ImportExportConstants.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/importexport/ImportExportConstants.java @@ -309,6 +309,7 @@ public final class ImportExportConstants { public static final String DISPLAY_ON_DEVPORTAL_OPTION = "displayOnDevportal"; public static final String POLICIES_DIRECTORY = "Policies"; + public static final String CUSTOM_BACKEND_DIRECTORY = "Sequence-Backend"; public static final String SWAGGER_X_WSO2_APICTL_INIT = "x-wso2-apictl-init"; public static final String EXPORT_POLICY_TYPE_YAML = "YAML"; diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java index 4af0ebda42c3..04ceb097da10 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java @@ -2921,6 +2921,27 @@ public static void validateAPIContext(String context, String apiName) throws API } } + /** + * This method is used to validate the endpoint configuration for API + * + * @param endpointConfigObject Endpoint Configuratioj of the API + * @param apiType API Type + * @param apiName Name of the API + * @throws APIManagementException Throws an error if endpoint configuration is not valid + */ + public static void validateAPIEndpointConfig(Object endpointConfigObject, String apiType, String apiName) + throws APIManagementException { + if (endpointConfigObject != null) { + Map endpointConfigMap = (Map) endpointConfigObject; + if (endpointConfigMap != null && endpointConfigMap.containsKey("endpoint_type") + && APIConstants.ENDPOINT_TYPE_SEQUENCE.equals( + endpointConfigMap.get(APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE)) + && !APIConstants.API_TYPE_HTTP.equalsIgnoreCase(apiType)) { + throw new APIManagementException("Invalid endpoint configuration provided for the API " + apiName); + } + } + } + /** * Check whether the parentheses are balanced * @@ -4288,6 +4309,17 @@ public static String getSequenceExtensionName(API api) { return api.getId().getApiName() + ":v" + api.getId().getVersion(); } + /** + * Return the Custom Backend name + * + * @param apiUUID API Id + * @param type Type of the custom backend used for (SANDBOX, PRODUCTION) + * @return The name of the Custom Backend + */ + public static String getCustomBackendName(String apiUUID, String type) { + return apiUUID + "-" + type; + } + /** * Return the sequence extension name. * eg: admin--testAPi--v1.00 @@ -10011,6 +10043,33 @@ public static void loadCommonOperationPolicies(String organization) { } } + /** + * Method is used to retrieve the Custom Backend sequence + * + * @param extractedFolderPath Extracted folder path of the APICTL project + * @param customBackendFileName Custom Backend name + * @param fileExtension .xml + * @return The Sequence of the Custom Backend as an Input Stream + * @throws APIManagementException If an error occurs while reading, throws an error + */ + public static String getCustomBackendSequence(String extractedFolderPath, String customBackendFileName, + String fileExtension) throws APIManagementException { + if (!StringUtils.isEmpty(customBackendFileName) && !customBackendFileName.contains(fileExtension)) { + customBackendFileName = customBackendFileName + fileExtension; + } + String fileName = extractedFolderPath + File.separator + customBackendFileName; + if (checkFileExistence(fileName)) { + try { + try (InputStream inputStream = new FileInputStream(fileName)) { + return IOUtils.toString(inputStream); + } + } catch (IOException ex) { + handleException("Error reading Custom Backend " + customBackendFileName); + } + } + return null; + } + /** * Read the operation policy definition from the provided path and return the definition object * @@ -10047,6 +10106,37 @@ public static OperationPolicyDefinition getOperationPolicyDefinitionFromFile(Str return policyDefinition; } + /** + * Method is used to get Custom Backend Sequence from the APICTL project + * + * @param extractedFolderPath Extracted Folder path + * @param sequenceName Sequence File name + * @param fileExtension File extension of the Custom Backend + * @return Custom Backend sequence as a String + * @throws APIManagementException Throws if an error occurs when reading the file + */ + public static String getCustomBackendSequenceFromFile(String extractedFolderPath, String sequenceName, + String fileExtension) throws APIManagementException { + + String customBackendContent = null; + try { + if(!StringUtils.isEmpty(sequenceName) && !sequenceName.contains(".xml")) { + sequenceName = sequenceName + fileExtension; + } + String fileName = extractedFolderPath + File.separator + sequenceName; + if (checkFileExistence(fileName)) { + if (log.isDebugEnabled()) { + log.debug("Found Sequence Backend file " + fileName); + } + customBackendContent = FileUtils.readFileToString(new File(fileName)); + } + } catch (IOException e) { + throw new APIManagementException("Error while reading Custom Backend from path: " + extractedFolderPath, e, + ExceptionCodes.ERROR_READING_META_DATA); + } + return customBackendContent; + } + /** * Check whether there exists a file for the provided location * @@ -10246,6 +10336,10 @@ public static String getOperationPolicyFileName(String policyName, String policy return policyName + "_" + policyVersion; } + public static String getCustomBackendFileName(String apiUUID, String endpointType) { + return apiUUID + "-" + endpointType; + } + public static void initializeVelocityContext(VelocityEngine velocityEngine){ velocityEngine.setProperty(RuntimeConstants.OLD_CHECK_EMPTY_OBJECTS, false); velocityEngine.setProperty(DeprecatedRuntimeConstants.OLD_SPACE_GOBBLING,"bc"); diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/GatewayUtils.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/GatewayUtils.java index 633844789fc2..bb1f8537a997 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/GatewayUtils.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/GatewayUtils.java @@ -46,6 +46,27 @@ public static void setCustomSequencesToBeRemoved(API api, GatewayAPIDTO gatewayA gatewayAPIDTO.setSequencesToBeRemove(addStringToList(faultSequence, gatewayAPIDTO.getSequencesToBeRemove())); } + public static void setCustomBackendToBeRemoved(GatewayAPIDTO gatewayAPIDTO) { + String sandBoxBackend = APIUtil.getCustomBackendName(gatewayAPIDTO.getApiId(), + APIConstants.API_KEY_TYPE_SANDBOX); + gatewayAPIDTO.setSequencesToBeRemove(addStringToList(sandBoxBackend, gatewayAPIDTO.getSequencesToBeRemove())); + String productionBackend = APIUtil.getCustomBackendName(gatewayAPIDTO.getApiId(), + APIConstants.API_KEY_TYPE_PRODUCTION); + gatewayAPIDTO.setSequencesToBeRemove( + addStringToList(productionBackend, gatewayAPIDTO.getSequencesToBeRemove())); + } + + public static void setCustomBackendToBeRemoved(APIProductIdentifier apiProductIdentifier, String apiUUID, + GatewayAPIDTO gatewayAPIDTO) { + String sandBoxBackend = APIUtil.getCustomBackendName(apiProductIdentifier.getUUID().concat("-" + apiUUID), + APIConstants.API_KEY_TYPE_SANDBOX); + gatewayAPIDTO.setSequencesToBeRemove(addStringToList(sandBoxBackend, gatewayAPIDTO.getSequencesToBeRemove())); + String productionBackend = APIUtil.getCustomBackendName(apiProductIdentifier.getUUID().concat("-" + apiUUID), + APIConstants.API_KEY_TYPE_PRODUCTION); + gatewayAPIDTO.setSequencesToBeRemove( + addStringToList(productionBackend, gatewayAPIDTO.getSequencesToBeRemove())); + } + public static String[] addStringToList(String key, String[] keys) { if (keys == null) { diff --git a/components/apimgt/org.wso2.carbon.apimgt.keymgt/src/test/resources/dbscripts/h2.sql b/components/apimgt/org.wso2.carbon.apimgt.keymgt/src/test/resources/dbscripts/h2.sql index 834115bd1cb4..ba3921bd77e6 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.keymgt/src/test/resources/dbscripts/h2.sql +++ b/components/apimgt/org.wso2.carbon.apimgt.keymgt/src/test/resources/dbscripts/h2.sql @@ -1310,6 +1310,17 @@ CREATE TABLE IF NOT EXISTS AM_API ( UNIQUE (API_PROVIDER,API_NAME,API_VERSION) ); +CREATE TABLE IF NOT EXISTS AM_API_SEQUENCE_BACKEND ( + ID VARCHAR(60) NOT NULL, + API_UUID VARCHAR(256) NOT NULL, + REVISION_UUID VARCHAR(256) DEFAULT '0', + SEQUENCE LONGBLOB, + NAME VARCHAR(256) NOT NULL, + TYPE VARCHAR(120) NOT NULL, + PRIMARY KEY(ID, API_UUID, REVISION_UUID, TYPE), + FOREIGN KEY(API_UUID) REFERENCES AM_API(API_UUID) ON DELETE CASCADE +); + CREATE TABLE IF NOT EXISTS AM_API_URL_MAPPING ( URL_MAPPING_ID INTEGER AUTO_INCREMENT, API_ID INTEGER NOT NULL, 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 dbd0900f19b6..4bbea252cbf8 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 @@ -392,6 +392,7 @@ public void restoreAPIRevision(Organization org, String apiUUID, String revision registry.put(apiPath, newAPIArtifact); GenericArtifact artifact = getAPIArtifact(apiUUID, registry); artifact.setAttribute(APIConstants.API_OVERVIEW_STATUS, lifecycleStatus); + // Update with the modified artifact artifactManager.updateGenericArtifact(apiArtifact); RegistryPersistenceUtil.clearResourcePermissions(apiPath, api.getId(), ((UserRegistry) registry).getTenantId()); 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 d25b133d4465..b5e358788c95 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 @@ -487,6 +487,196 @@ paths: -H "Content-Type: application/json" -d @data.json "https://127.0.0.1:9443/api/am/publisher/v4/apis/7a2298c4-c905-403f-8fac-38c73301631f/topics"' + /apis/{apiId}/sequence-backend/{type}/content: + get: + tags: + - APIs + summary: Get Sequence of Custom Backend + description: This operation can be used to get Sequence of the Custom Backend + operationId: getSequenceBackendContent + parameters: + - name: type + in: path + description: | + Type of the Endpoint. + SANDBOX or PRODUCTION + required: true + schema: + maxLength: 15 + type: string + - $ref: '#/components/parameters/apiId' + responses: + 200: + description: | + OK. + Requested API Custom Backend is returned + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/xml: + schema: + type: string + format: binary + 404: + $ref: '#/components/responses/NotFound' + 406: + $ref: '#/components/responses/NotAcceptable' + security: + - OAuth2Security: + - apim:api_view + - apim:api_manage + - apim:api_import_export + - apim:api_product_import_export + x-code-samples: + - lang: Curl + source: 'curl -k -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://127.0.0.1:9443/api/am/publisher/v4/apis/7a2298c4-c905-403f-8fac-38c73301631f/custom-backend/7a2298c4-c915-483f-8fac-38c73301631f/content"' + + /apis/{apiId}/sequence-backend/{type}: + delete: + tags: + - APIs + summary: Delete Sequence Backend of the API + description: This operation can be used to remove the Sequence Backend of the API + operationId: sequenceBackendDelete + parameters: + - name: type + in: path + description: | + Type of the Endpoint. + SANDBOX or PRODUCTION + required: true + schema: + maxLength: 15 + type: string + - $ref: '#/components/parameters/apiId' + responses: + 200: + description: | + OK. + Resource successfully deleted. + content: { } + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 409: + $ref: '#/components/responses/Conflict' + 412: + $ref: '#/components/responses/PreconditionFailed' + security: + - OAuth2Security: + - apim:api_delete + - apim:api_manage + - apim:api_import_export + x-code-samples: + - lang: Curl + source: 'curl -k -X DELETE -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://127.0.0.1:9443/api/am/publisher/v4/apis/7a2298c4-c905-403f-8fac-38c73301631f/custom-backend/7a2298c4-c915-483f-8fac-38c73301631f"' + + /apis/{apiId}/sequence-backend: + get: + tags: + - APIs + summary: Get Sequence Backends of the API + description: This operation can be used to get Sequence Backend data of the API + operationId: getSequenceBackendData + parameters: + - $ref: '#/components/parameters/apiId' + responses: + 200: + description: | + OK. + Requested API Sequence Backend is returned + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/SequenceBackendList' + 404: + $ref: '#/components/responses/NotFound' + 406: + $ref: '#/components/responses/NotAcceptable' + security: + - OAuth2Security: + - apim:api_view + - apim:api_manage + - apim:api_import_export + - apim:api_product_import_export + x-code-samples: + - lang: Curl + source: 'curl -k -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://127.0.0.1:9443/api/am/publisher/v4/apis/7a2298c4-c905-403f-8fac-38c73301631f/custom-backend/7a2298c4-c915-483f-8fac-38c73301631f"' + put: + tags: + - APIs + summary: Upload Sequence Sequence as the Endpoint of the API + description: This operation can be used to change the endpoint of the API to Sequence Backend + operationId: sequenceBackendUpdate + parameters: + - $ref: '#/components/parameters/apiId' + requestBody: + content: + multipart/form-data: + schema: + properties: + sequence: + type: string + description: The sequence that needs to be uploaded. + format: binary + type: + type: string + description: Type of the Endpoint + responses: + 200: + description: | + OK. + Successful response with updated API object + headers: + Location: + description: | + The URL of the newly created resource. + schema: + type: string + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/API' + 400: + $ref: '#/components/responses/BadRequest' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 409: + $ref: '#/components/responses/Conflict' + 412: + $ref: '#/components/responses/PreconditionFailed' + security: + - OAuth2Security: + - apim:api_create + - apim:api_manage + - apim:api_publish + x-code-samples: + - lang: Curl + source: 'curl -k -X PUT -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + -H "Content-Type: application/json" -d @data.json "https://127.0.0.1:9443/api/am/publisher/v4/apis/7a2298c4-c905-403f-8fac-38c73301631f/custom-backend"' + + /apis/{apiId}/reimport-service: put: tags: @@ -9358,6 +9548,39 @@ paths: -H "Content-Type: application/json" -d @data.json "https://127.0.0.1:9443/api/am/admin/v4/workflows/update-workflow-status?workflowReferenceId=56e3a170-a7a7-45f8-b051-7e43a58a67e1"' components: schemas: + SequenceBackendList: + title: Sequence Backend List + type: object + properties: + count: + type: integer + description: | + Number of Sequence Backends returned. + example: 1 + list: + type: array + items: + $ref: '#/components/schemas/SequenceBackend' + SequenceBackend: + title: Sequence Backend + type: object + properties: + sequenceId: + type: string + readOnly: true + example: 943d3002-000c-42d3-a1b9-d6559f8a4d49 + sequenceName: + type: string + readOnly: true + example: 943d3002-000c-42d3-a1b9-d6559f8a4d49-SANDBOX + sequenceType: + type: string + readOnly: true + example: SANDBOX + sequence: + type: string + format: binary + readOnly: true Comment: title: Comment required: 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/SequenceBackendDTO.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/SequenceBackendDTO.java new file mode 100644 index 000000000000..39b78888d961 --- /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/SequenceBackendDTO.java @@ -0,0 +1,142 @@ +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 java.io.File; +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 SequenceBackendDTO { + + private String sequenceId = null; + private String sequenceName = null; + private String sequenceType = null; + private File sequence = null; + + /** + **/ + public SequenceBackendDTO sequenceId(String sequenceId) { + this.sequenceId = sequenceId; + return this; + } + + + @ApiModelProperty(example = "943d3002-000c-42d3-a1b9-d6559f8a4d49", value = "") + @JsonProperty("sequenceId") + public String getSequenceId() { + return sequenceId; + } + public void setSequenceId(String sequenceId) { + this.sequenceId = sequenceId; + } + + /** + **/ + public SequenceBackendDTO sequenceName(String sequenceName) { + this.sequenceName = sequenceName; + return this; + } + + + @ApiModelProperty(example = "943d3002-000c-42d3-a1b9-d6559f8a4d49-SANDBOX", value = "") + @JsonProperty("sequenceName") + public String getSequenceName() { + return sequenceName; + } + public void setSequenceName(String sequenceName) { + this.sequenceName = sequenceName; + } + + /** + **/ + public SequenceBackendDTO sequenceType(String sequenceType) { + this.sequenceType = sequenceType; + return this; + } + + + @ApiModelProperty(example = "SANDBOX", value = "") + @JsonProperty("sequenceType") + public String getSequenceType() { + return sequenceType; + } + public void setSequenceType(String sequenceType) { + this.sequenceType = sequenceType; + } + + /** + **/ + public SequenceBackendDTO sequence(File sequence) { + this.sequence = sequence; + return this; + } + + + @ApiModelProperty(value = "") + @JsonProperty("sequence") + public File getSequence() { + return sequence; + } + public void setSequence(File sequence) { + this.sequence = sequence; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SequenceBackendDTO sequenceBackend = (SequenceBackendDTO) o; + return Objects.equals(sequenceId, sequenceBackend.sequenceId) && + Objects.equals(sequenceName, sequenceBackend.sequenceName) && + Objects.equals(sequenceType, sequenceBackend.sequenceType) && + Objects.equals(sequence, sequenceBackend.sequence); + } + + @Override + public int hashCode() { + return Objects.hash(sequenceId, sequenceName, sequenceType, sequence); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class SequenceBackendDTO {\n"); + + sb.append(" sequenceId: ").append(toIndentedString(sequenceId)).append("\n"); + sb.append(" sequenceName: ").append(toIndentedString(sequenceName)).append("\n"); + sb.append(" sequenceType: ").append(toIndentedString(sequenceType)).append("\n"); + sb.append(" sequence: ").append(toIndentedString(sequence)).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/SequenceBackendListDTO.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/SequenceBackendListDTO.java new file mode 100644 index 000000000000..ef33c302c048 --- /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/SequenceBackendListDTO.java @@ -0,0 +1,106 @@ +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 java.util.ArrayList; +import java.util.List; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.SequenceBackendDTO; +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 SequenceBackendListDTO { + + private Integer count = null; + private List list = new ArrayList(); + + /** + * Number of Sequence Backends returned. + **/ + public SequenceBackendListDTO count(Integer count) { + this.count = count; + return this; + } + + + @ApiModelProperty(example = "1", value = "Number of Sequence Backends returned. ") + @JsonProperty("count") + public Integer getCount() { + return count; + } + public void setCount(Integer count) { + this.count = count; + } + + /** + **/ + public SequenceBackendListDTO list(List list) { + this.list = list; + return this; + } + + + @ApiModelProperty(value = "") + @Valid + @JsonProperty("list") + public List getList() { + return list; + } + public void setList(List list) { + this.list = list; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SequenceBackendListDTO sequenceBackendList = (SequenceBackendListDTO) o; + return Objects.equals(count, sequenceBackendList.count) && + Objects.equals(list, sequenceBackendList.list); + } + + @Override + public int hashCode() { + return Objects.hash(count, list); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class SequenceBackendListDTO {\n"); + + sb.append(" count: ").append(toIndentedString(count)).append("\n"); + sb.append(" list: ").append(toIndentedString(list)).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/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/SynapsePolicyAggregator.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/SynapsePolicyAggregator.java index 3936c0170a02..c204989287fb 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/SynapsePolicyAggregator.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/SynapsePolicyAggregator.java @@ -57,6 +57,9 @@ public class SynapsePolicyAggregator { private static final String POLICY_SEQUENCE_TEMPLATE_LOCATION = CarbonUtils.getCarbonHome() + File.separator + "repository" + File.separator + "resources" + File.separator + "api_templates" + File.separator + "operation_policy_template.j2"; + private static final String CUSTOM_BACKEND_SEQUENCE_TEMPLATE_LOCATION = + CarbonUtils.getCarbonHome() + File.separator + "repository" + File.separator + "resources" + File.separator + + "api_templates" + File.separator + "custom_backend_sequence_template.j2"; private static final String GATEWAY_POLICY_SEQUENCE_TEMPLATE_LOCATION = CarbonUtils.getCarbonHome() + File.separator + "repository" + File.separator + "resources" + File.separator + "templates" + File.separator + "gateway_policy_template.j2"; @@ -104,6 +107,38 @@ public static String generatePolicySequenceForUriTemplateSet(Set ur } } + public static String generateSequenceBackendForAPIProducts(String seqName, String prodSeq, String pathToArchive, + String endpointType) throws APIManagementException, IOException { + Map configMap = new HashMap<>(); + String customBackendTemplate = FileUtil.readFileToString(CUSTOM_BACKEND_SEQUENCE_TEMPLATE_LOCATION) + .replace("\\", ""); + // change sequence name from the upper function + configMap.put("sequence_name", prodSeq); + String sanitizedSequence = renderCustomBackendSequence(seqName, pathToArchive); + if (sanitizedSequence == null) { + return null; + } + configMap.put("custom_sequence", sanitizedSequence); + configMap.put("endpoint_type", endpointType); + return renderPolicyTemplate(customBackendTemplate, configMap); + } + + public static String generateBackendSequenceForCustomSequence(String fileName, String pathToArchive, + String endpointType, String apiSeqName) throws APIManagementException, IOException { + Map configMap = new HashMap<>(); + String customBackendTemplate = FileUtil.readFileToString(CUSTOM_BACKEND_SEQUENCE_TEMPLATE_LOCATION) + .replace("\\", ""); + // change sequence name from the upper function + configMap.put("sequence_name", apiSeqName); + String sanitizedSequence = renderCustomBackendSequence(fileName, pathToArchive); + if (sanitizedSequence == null) { + return null; + } + configMap.put("custom_sequence", sanitizedSequence); + configMap.put("endpoint_type", endpointType); + return renderPolicyTemplate(customBackendTemplate, configMap); + } + /** * This method will populate the operation policy case list. * @@ -202,6 +237,17 @@ private static List renderPolicyMapping(List policyList return renderedPolicyMappingList; } + private static String renderCustomBackendSequence(String sequenceName, String pathToArchive) + throws APIManagementException { + String policyDirectory = pathToArchive + File.separator + ImportExportConstants.CUSTOM_BACKEND_DIRECTORY; + String sequence = APIUtil.getCustomBackendSequenceFromFile(policyDirectory, sequenceName, + APIConstants.SYNAPSE_POLICY_DEFINITION_EXTENSION_XML); + if (sequence == null) { + return null; + } + return renderPolicyTemplate(sequence, new HashMap<>()); + } + /** * As there can be multiple child elements without a root element, for sanitization, we will * first wrap the template with a dummy root element and build the OM element. 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/TemplateBuilderUtil.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/TemplateBuilderUtil.java index bd35cd8fae60..05b31f15b96e 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/TemplateBuilderUtil.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/TemplateBuilderUtil.java @@ -18,6 +18,9 @@ package org.wso2.carbon.apimgt.rest.api.publisher.v1.common; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import org.apache.axiom.om.OMAttribute; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.util.AXIOMUtil; @@ -43,11 +46,13 @@ import org.wso2.carbon.apimgt.api.model.APIProductResource; import org.wso2.carbon.apimgt.api.model.CORSConfiguration; import org.wso2.carbon.apimgt.api.model.Environment; +import org.wso2.carbon.apimgt.api.model.SequenceBackendData; import org.wso2.carbon.apimgt.api.model.URITemplate; import org.wso2.carbon.apimgt.api.model.WebSocketTopicMappingConfiguration; import org.wso2.carbon.apimgt.common.gateway.graphql.GraphQLSchemaDefinitionUtil; import org.wso2.carbon.apimgt.impl.APIConstants; import org.wso2.carbon.apimgt.impl.certificatemgt.exceptions.CertificateManagementException; +import org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO; import org.wso2.carbon.apimgt.impl.definitions.GraphQLSchemaDefinition; import org.wso2.carbon.apimgt.impl.dto.SoapToRestMediationDto; import org.wso2.carbon.apimgt.impl.importexport.ImportExportConstants; @@ -80,6 +85,8 @@ import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; +import static org.wso2.carbon.apimgt.impl.APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE; + /** * This class used to utility for Template. */ @@ -556,6 +563,60 @@ public static GatewayAPIDTO retrieveGatewayAPIDto(API api, Environment environme // add new property for entires that has a __display suffix JSONObject modifiedProperties = getModifiedProperties(originalProperties); api.setAdditionalProperties(modifiedProperties); + + String endpointConfigString = api.getEndpointConfig(); + if (StringUtils.isNotBlank(endpointConfigString)) { + try { + // Avoid number format issues in Endpoint Configuration + JsonObject endpointConf = JsonParser.parseString(api.getEndpointConfig()).getAsJsonObject(); + if (endpointConf != null && APIConstants.ENDPOINT_TYPE_SEQUENCE.equals( + endpointConf.get(API_ENDPOINT_CONFIG_PROTOCOL_TYPE).getAsString()) && StringUtils.equals( + api.getType().toLowerCase(), APIConstants.API_TYPE_HTTP.toLowerCase())) { + ApiMgtDAO apiMgtDAO = ApiMgtDAO.getInstance(); + // To modify the endpoint config string + JSONParser parser = new JSONParser(); + ObjectMapper objectMapper = new ObjectMapper(); + JSONObject endpointConfig = (JSONObject) parser.parse(endpointConfigString); + if (APIConstants.ENDPOINT_TYPE_SEQUENCE.equals( + endpointConfig.get(API_ENDPOINT_CONFIG_PROTOCOL_TYPE))) { + String policyDirectory = + extractedFolderPath + File.separator + ImportExportConstants.CUSTOM_BACKEND_DIRECTORY; + String seqName = APIUtil.getCustomBackendName(api.getUuid(), APIConstants.API_KEY_TYPE_SANDBOX); + SequenceBackendData seqData = apiMgtDAO.getCustomBackendByAPIUUID(api.getUuid(), + APIConstants.API_KEY_TYPE_SANDBOX); + if (seqData != null) { + String name = seqData.getName(); + if (!StringUtils.isEmpty(name) && !name.contains( + APIConstants.SYNAPSE_POLICY_DEFINITION_EXTENSION_XML)) { + name = name + APIConstants.SYNAPSE_POLICY_DEFINITION_EXTENSION_XML; + } + if (APIUtil.checkFileExistence(policyDirectory + File.separator + name)) { + endpointConfig.put("sandbox", seqName); + } + } + + seqName = APIUtil.getCustomBackendName(api.getUuid(), APIConstants.API_KEY_TYPE_PRODUCTION); + seqData = apiMgtDAO.getCustomBackendByAPIUUID(api.getUuid(), + APIConstants.API_KEY_TYPE_PRODUCTION); + if (seqData != null) { + String name = seqData.getName(); + if (!StringUtils.isEmpty(name) && !name.contains( + APIConstants.SYNAPSE_POLICY_DEFINITION_EXTENSION_XML)) { + name = name + APIConstants.SYNAPSE_POLICY_DEFINITION_EXTENSION_XML; + } + if (APIUtil.checkFileExistence(policyDirectory + File.separator + name)) { + endpointConfig.put("production", seqName); + } + } + api.setEndpointConfig(objectMapper.writeValueAsString(endpointConfig)); + } + } + } catch (IOException | ParseException ex) { + throw new APIManagementException("Error when updating Endpoint Configuration of API: " + api.getUuid(), + ex); + } + } + APITemplateBuilder apiTemplateBuilder = TemplateBuilderUtil .getAPITemplateBuilder(api, tenantDomain, clientCertificatesDTOListProduction, clientCertificatesDTOListSandbox, soapToRestInMediationDtoList, soapToRestOutMediationDtoList); @@ -690,7 +751,16 @@ private static GatewayAPIDTO createAPIGatewayDTOtoPublishAPI(Environment environ api.setUuid(apidto.getId()); GatewayUtils.setCustomSequencesToBeRemoved(apiProduct.getId(), api.getUuid(), productAPIDto); APITemplateBuilder apiTemplateBuilder = new APITemplateBuilderImpl(api, apiProduct); - addEndpoints(api, apiTemplateBuilder, productAPIDto); + // check the endpoint type + if (!StringUtils.isEmpty(api.getEndpointConfig())) { + JsonObject endpointConfObj = JsonParser.parseString(api.getEndpointConfig()).getAsJsonObject(); + if (!APIConstants.ENDPOINT_TYPE_SEQUENCE.equals( + endpointConfObj.get(API_ENDPOINT_CONFIG_PROTOCOL_TYPE).getAsString())) { + addEndpoints(api, apiTemplateBuilder, productAPIDto); + } + } else { + addEndpoints(api, apiTemplateBuilder, productAPIDto); + } setCustomSequencesToBeAdded(apiProduct, api, productAPIDto, apiExtractedPath, apidto); setAPIFaultSequencesToBeAdded(api, productAPIDto, apiExtractedPath, apidto); String prefix = id.getName() + "--v" + id.getVersion(); @@ -735,6 +805,24 @@ private static void setCustomSequencesToBeAdded(APIProduct apiProduct, API api, gatewayAPIDTO.setSequenceToBeAdd( addGatewayContentToList(gatewayFaultContentDTO, gatewayAPIDTO.getSequenceToBeAdd())); } + + JsonObject endpointConfigMap = JsonParser.parseString(api.getEndpointConfig()).getAsJsonObject(); + if (endpointConfigMap != null && APIConstants.ENDPOINT_TYPE_SEQUENCE.equals( + endpointConfigMap.get(API_ENDPOINT_CONFIG_PROTOCOL_TYPE).getAsString()) + && APIConstants.API_TYPE_HTTP.equals(api.getType())) { + GatewayContentDTO gatewayCustomBackendSequenceDTO = retrieveSequenceBackendForAPIProduct(api, + apiProduct, APIConstants.API_KEY_TYPE_SANDBOX, extractedPath); + if (gatewayCustomBackendSequenceDTO != null) { + gatewayAPIDTO.setSequenceToBeAdd(addGatewayContentToList(gatewayCustomBackendSequenceDTO, + gatewayAPIDTO.getSequenceToBeAdd())); + } + gatewayCustomBackendSequenceDTO = retrieveSequenceBackendForAPIProduct(api, apiProduct, + APIConstants.API_KEY_TYPE_PRODUCTION, extractedPath); + if (gatewayCustomBackendSequenceDTO != null) { + gatewayAPIDTO.setSequenceToBeAdd(addGatewayContentToList(gatewayCustomBackendSequenceDTO, + gatewayAPIDTO.getSequenceToBeAdd())); + } + } } } @@ -829,7 +917,7 @@ private static GatewayAPIDTO createAPIGatewayDTOtoPublishAPI(Environment environ // specified Or if the Gateway type is 'sandbox' and a sandbox url has not been specified if (endpointConfig != null && !APIConstants.ENDPOINT_TYPE_AWSLAMBDA.equals( - endpointConfig.get(APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE)) && ( + endpointConfig.get(API_ENDPOINT_CONFIG_PROTOCOL_TYPE)) && ( (APIConstants.GATEWAY_ENV_TYPE_PRODUCTION.equals(environment.getType()) && !APIUtil.isProductionEndpointsExists(api.getEndpointConfig())) || ( APIConstants.GATEWAY_ENV_TYPE_SANDBOX.equals(environment.getType()) @@ -858,8 +946,9 @@ private static GatewayAPIDTO createAPIGatewayDTOtoPublishAPI(Environment environ } else if (APIConstants.IMPLEMENTATION_TYPE_ENDPOINT.equalsIgnoreCase(api.getImplementation())) { String apiConfig = builder.getConfigStringForTemplate(environment); gatewayAPIDTO.setApiDefinition(apiConfig); - if (endpointConfig != null && !endpointConfig.get(APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE) - .equals(APIConstants.ENDPOINT_TYPE_AWSLAMBDA)) { + if (endpointConfig != null && !endpointConfig.get(API_ENDPOINT_CONFIG_PROTOCOL_TYPE) + .equals(APIConstants.ENDPOINT_TYPE_AWSLAMBDA) && !endpointConfig.get( + API_ENDPOINT_CONFIG_PROTOCOL_TYPE).equals(APIConstants.ENDPOINT_TYPE_SEQUENCE)) { if (!isWsApi) { addEndpoints(api, builder, gatewayAPIDTO); } @@ -967,6 +1056,23 @@ private static void setCustomSequencesToBeAdded(API api, GatewayAPIDTO gatewayAP addGatewayContentToList(gatewayFaultContentDTO, gatewayAPIDTO.getSequenceToBeAdd())); } } + Map endpointConfigMap = (Map) apidto.getEndpointConfig(); + + if (endpointConfigMap != null && APIConstants.ENDPOINT_TYPE_SEQUENCE.equals( + endpointConfigMap.get(API_ENDPOINT_CONFIG_PROTOCOL_TYPE))) { + GatewayContentDTO gatewayCustomBackendSequenceDTO = retrieveCustomBackendSequence(api, + APIConstants.API_KEY_TYPE_SANDBOX, extractedPath); + if (gatewayCustomBackendSequenceDTO != null) { + gatewayAPIDTO.setSequenceToBeAdd( + addGatewayContentToList(gatewayCustomBackendSequenceDTO, gatewayAPIDTO.getSequenceToBeAdd())); + } + gatewayCustomBackendSequenceDTO = retrieveCustomBackendSequence(api, APIConstants.API_KEY_TYPE_PRODUCTION, + extractedPath); + if (gatewayCustomBackendSequenceDTO != null) { + gatewayAPIDTO.setSequenceToBeAdd( + addGatewayContentToList(gatewayCustomBackendSequenceDTO, gatewayAPIDTO.getSequenceToBeAdd())); + } + } } private static void setAPIFaultSequencesToBeAdded(API api, GatewayAPIDTO gatewayAPIDTO, String extractedPath, @@ -1194,7 +1300,7 @@ private static void setSecureVaultPropertyToBeAdded(String prefix, API api, Gate } } else if (APIConstants.ENDPOINT_TYPE_AWSLAMBDA - .equals(endpointConfig.get(APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE))) { + .equals(endpointConfig.get(API_ENDPOINT_CONFIG_PROTOCOL_TYPE))) { addAWSCredentialsToList(prefix, api, gatewayAPIDTO, endpointConfig); } } @@ -1395,15 +1501,15 @@ private static GatewayContentDTO retrieveOperationPolicySequence(String pathToAc operationPolicySequenceContentDto.setName(seqExt); operationPolicySequenceContentDto.setContent(APIUtil.convertOMtoString(omElement)); switch (flow) { - case APIConstants.OPERATION_SEQUENCE_TYPE_REQUEST: - api.setInSequence(seqExt); - break; - case APIConstants.OPERATION_SEQUENCE_TYPE_RESPONSE: - api.setOutSequence(seqExt); - break; - case APIConstants.OPERATION_SEQUENCE_TYPE_FAULT: - api.setFaultSequence(seqExt); - break; + case APIConstants.OPERATION_SEQUENCE_TYPE_REQUEST: + api.setInSequence(seqExt); + break; + case APIConstants.OPERATION_SEQUENCE_TYPE_RESPONSE: + api.setOutSequence(seqExt); + break; + case APIConstants.OPERATION_SEQUENCE_TYPE_FAULT: + api.setFaultSequence(seqExt); + break; } return operationPolicySequenceContentDto; } @@ -1414,6 +1520,81 @@ private static GatewayContentDTO retrieveOperationPolicySequence(String pathToAc return null; } + private static GatewayContentDTO retrieveSequenceBackendForAPIProduct(API api, APIProduct apiProduct, + String endpointType, String pathToAchieve) throws APIManagementException { + GatewayContentDTO customBackendSequenceContentDto = new GatewayContentDTO(); + String customSequence = null; + ApiMgtDAO apiMgtDAO = ApiMgtDAO.getInstance(); + SequenceBackendData data = apiMgtDAO.getCustomBackendByAPIUUID(api.getUuid(), endpointType); + if (data != null) { + String seqExt = data.getName(); + if (!StringUtils.isEmpty(seqExt) && seqExt.contains(".xml")) { + seqExt = seqExt + ".xml"; + } + String prodSeqExt = APIUtil.getCustomBackendName(apiProduct.getUuid().concat("-" + api.getUuid()), + endpointType); + try { + customSequence = SynapsePolicyAggregator.generateSequenceBackendForAPIProducts(seqExt, prodSeqExt, + pathToAchieve, endpointType); + } catch (IOException e) { + throw new APIManagementException(e); + } + + if (StringUtils.isNotEmpty(customSequence)) { + try { + OMElement omElement = APIUtil.buildOMElement(new ByteArrayInputStream(customSequence.getBytes())); + if (omElement != null) { + if (omElement.getAttribute(new QName("name")) != null) { + omElement.getAttribute(new QName("name")).setAttributeValue(prodSeqExt); + } + customBackendSequenceContentDto.setName(prodSeqExt); + customBackendSequenceContentDto.setContent(APIUtil.convertOMtoString(omElement)); + return customBackendSequenceContentDto; + } + } catch (Exception e) { + throw new APIManagementException(e); + } + } + } + + return null; + } + + private static GatewayContentDTO retrieveCustomBackendSequence(API api, String endpointType, String pathToAchieve) + throws APIManagementException { + ApiMgtDAO apiMgtDAO = ApiMgtDAO.getInstance(); + GatewayContentDTO customBackendSequenceContentDto = new GatewayContentDTO(); + String customSequence = null; + SequenceBackendData data = apiMgtDAO.getCustomBackendByAPIUUID(api.getUuid(), endpointType); + if (data != null) { + String seqExt = data.getName(); + String apiSeqName = APIUtil.getCustomBackendName(api.getUuid(), endpointType); + try { + customSequence = SynapsePolicyAggregator.generateBackendSequenceForCustomSequence(seqExt, pathToAchieve, + endpointType, apiSeqName); + } catch (IOException e) { + throw new APIManagementException(e); + } + + if (StringUtils.isNotEmpty(customSequence)) { + try { + OMElement omElement = APIUtil.buildOMElement(new ByteArrayInputStream(customSequence.getBytes())); + if (omElement != null) { + if (omElement.getAttribute(new QName("name")) != null) { + omElement.getAttribute(new QName("name")).setAttributeValue(apiSeqName); + } + customBackendSequenceContentDto.setName(apiSeqName); + customBackendSequenceContentDto.setContent(APIUtil.convertOMtoString(omElement)); + return customBackendSequenceContentDto; + } + } catch (Exception e) { + throw new APIManagementException(e); + } + } + } + return null; + } + private static GatewayContentDTO retrieveOperationPolicySequenceForProducts(APIProduct apiProduct, API api, String extractedLocation, String flow) throws APIManagementException { @@ -1638,19 +1819,19 @@ public static String populateSubscriptionEndpointConfig(String endpointConfig) t try { JSONObject newEndpointConfigJson = new JSONObject(); - newEndpointConfigJson.put(APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE, + newEndpointConfigJson.put(API_ENDPOINT_CONFIG_PROTOCOL_TYPE, APIConstants.ENDPOINT_TYPE_GRAPHQL); JSONObject oldEndpointConfigJson = (JSONObject) new JSONParser().parse(endpointConfig); newEndpointConfigJson.put(APIConstants.ENDPOINT_TYPE_HTTP, oldEndpointConfigJson); JSONObject wsEndpointConfig = new JSONObject(); - wsEndpointConfig.put(APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE, APIConstants.WS_PROTOCOL); + wsEndpointConfig.put(API_ENDPOINT_CONFIG_PROTOCOL_TYPE, APIConstants.WS_PROTOCOL); // If production_endpoints exists if (oldEndpointConfigJson.get(APIConstants.ENDPOINT_PRODUCTION_ENDPOINTS) != null) { JSONObject prodWSEndpointConfig; String prodWsEndpoint = ""; // If load_balanced endpoints get the first prod endpoint url from the list if (APIConstants.ENDPOINT_TYPE_LOADBALANCE.equals( - oldEndpointConfigJson.get(APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE))) { + oldEndpointConfigJson.get(API_ENDPOINT_CONFIG_PROTOCOL_TYPE))) { // get first load balanced endpoint prodWsEndpoint = (String) ((JSONObject) ((org.json.simple.JSONArray) oldEndpointConfigJson .get(APIConstants.ENDPOINT_PRODUCTION_ENDPOINTS)).get(0)).get(APIConstants.ENDPOINT_URL); @@ -1683,7 +1864,7 @@ public static String populateSubscriptionEndpointConfig(String endpointConfig) t String sandboxWsEndpoint = ""; // If load_balanced endpoints get the first sandbox endpoint url from the list if (APIConstants.ENDPOINT_TYPE_LOADBALANCE.equals( - oldEndpointConfigJson.get(APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE))) { + oldEndpointConfigJson.get(API_ENDPOINT_CONFIG_PROTOCOL_TYPE))) { // get first load balanced endpoint sandboxWsEndpoint = (String) ((JSONObject) ((org.json.simple.JSONArray) oldEndpointConfigJson .get(APIConstants.ENDPOINT_SANDBOX_ENDPOINTS)).get(0)).get(APIConstants.ENDPOINT_URL); 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/APIMappingUtil.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/APIMappingUtil.java index 5804fb64cb86..21b6056bb04c 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/APIMappingUtil.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/APIMappingUtil.java @@ -54,6 +54,7 @@ import org.wso2.carbon.apimgt.api.model.OperationPolicy; import org.wso2.carbon.apimgt.api.model.ResourcePath; import org.wso2.carbon.apimgt.api.model.Scope; +import org.wso2.carbon.apimgt.api.model.SequenceBackendData; import org.wso2.carbon.apimgt.api.model.ServiceEntry; import org.wso2.carbon.apimgt.api.model.Tier; import org.wso2.carbon.apimgt.api.model.URITemplate; @@ -116,6 +117,8 @@ import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ResourcePolicyInfoDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ResourcePolicyListDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ScopeDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.SequenceBackendDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.SequenceBackendListDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.WSDLInfoDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.WSDLValidationResponseDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.WSDLValidationResponseWsdlInfoDTO; @@ -509,6 +512,22 @@ public static MockResponsePayloadInfoDTO fromMockPayloadToDTO(APIResourceMediati return mockResponsePayloadInfoDTO; } + public static SequenceBackendListDTO fromSequenceDataToDTO(List list) { + SequenceBackendListDTO res = new SequenceBackendListDTO(); + res.setCount(list.size()); + List backends = new ArrayList<>(); + + for (SequenceBackendData backend : list) { + SequenceBackendDTO dto = new SequenceBackendDTO(); + dto.sequenceId(backend.getId()); + dto.setSequenceName(backend.getName()); + dto.setSequenceType(backend.getType()); + backends.add(dto); + } + res.setList(backends); + return res; + } + /** * This method creates the API monetization information DTO. * 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/ExportUtils.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/ExportUtils.java index 1c89b95b2cc1..6abb8e24a416 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/ExportUtils.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/ExportUtils.java @@ -53,6 +53,7 @@ import org.wso2.carbon.apimgt.api.model.OperationPolicy; import org.wso2.carbon.apimgt.api.model.OperationPolicyData; import org.wso2.carbon.apimgt.api.model.ResourceFile; +import org.wso2.carbon.apimgt.api.model.SequenceBackendData; import org.wso2.carbon.apimgt.api.model.URITemplate; import org.wso2.carbon.apimgt.api.model.graphql.queryanalysis.GraphqlComplexityInfo; import org.wso2.carbon.apimgt.impl.APIConstants; @@ -86,6 +87,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import static org.wso2.carbon.apimgt.impl.APIConstants.API_DATA_PRODUCTION_ENDPOINTS; @@ -216,6 +218,33 @@ public static File exportApi(APIProvider apiProvider, APIIdentifier apiIdentifie String tenantDomain = APIUtil.getTenantDomainFromTenantId(tenantId); addOperationPoliciesToArchive(archivePath, tenantDomain, exportFormat, apiProvider, api, currentApiUuid); + + if (api != null && !StringUtils.isEmpty(api.getEndpointConfig())) { + JsonObject endpointConfig = JsonParser.parseString(api.getEndpointConfig()).getAsJsonObject(); + if (endpointConfig != null && APIConstants.ENDPOINT_TYPE_SEQUENCE.equals( + endpointConfig.get(API_ENDPOINT_CONFIG_PROTOCOL_TYPE).getAsString()) && StringUtils.equals( + apiDtoToReturn.getType().toString().toLowerCase(), APIConstants.API_TYPE_HTTP.toLowerCase())) { + if (apiDtoToReturn.getEndpointConfig() != null) { + Map endpointConf = (Map) apiDtoToReturn.getEndpointConfig(); + if (endpointConf != null && APIConstants.ENDPOINT_TYPE_SEQUENCE.equals( + endpointConf.get(API_ENDPOINT_CONFIG_PROTOCOL_TYPE))) { + SequenceBackendData sqData = apiProvider.getCustomBackendByAPIUUID(currentApiUuid, + APIConstants.API_KEY_TYPE_SANDBOX); + if (sqData != null) { + endpointConf.put("sandbox", sqData.getName()); + } + sqData = apiProvider.getCustomBackendByAPIUUID(currentApiUuid, + APIConstants.API_KEY_TYPE_PRODUCTION); + if (sqData != null) { + endpointConf.put("production", sqData.getName()); + } + apiDtoToReturn.setEndpointConfig(endpointConf); + } + } + addCustomBackendToArchive(archivePath, apiProvider, currentApiUuid); + } + } + addGatewayEnvironmentsToArchive(archivePath, apiDtoToReturn.getId(), exportFormat, apiProvider); if (migrationEnabled != null) { @@ -628,6 +657,31 @@ public static void addEndpointCertificatesToArchive(String archivePath, APIDTO a } } + public static void addCustomBackendToArchive(String archivePath, APIProvider apiProvider, String apiUUID) + throws APIManagementException { + try { + CommonUtil.createDirectory(archivePath + File.separator + ImportExportConstants.CUSTOM_BACKEND_DIRECTORY); + + // Add production Backend Sequences + SequenceBackendData data = apiProvider.getCustomBackendByAPIUUID(apiUUID, + APIConstants.API_KEY_TYPE_PRODUCTION); + if (data != null) { + String seqName = data.getName(); + exportCustomBackend(seqName, data.getSequence(), archivePath); + } + + // Add sandbox Backend Sequences + data = apiProvider.getCustomBackendByAPIUUID(apiUUID, APIConstants.API_KEY_TYPE_SANDBOX); + if (data != null) { + String seqName = data.getName(); + exportCustomBackend(seqName, data.getSequence(), archivePath); + } + + } catch (IOException | APIImportExportException ex) { + throw new APIManagementException("Error adding Custom Backends to the API Directory: " + apiUUID, ex); + } + } + /** * Retrieve the operation policies and store those in the archive directory. * @@ -736,6 +790,27 @@ public static void exportPolicyData(String policyFileName, OperationPolicyData p } } + /** + * Method is used to write Custom Backend file to the Directory + * + * @param customBackendFileName Custom Backend file name + * @param sequence Content of the Custom Backend + * @param archivePath Archived path + * @throws APIImportExportException Import/Export error if exists + * @throws IOException IO Error when reading/writing to the file + */ + public static void exportCustomBackend(String customBackendFileName, String sequence, String archivePath) + throws APIImportExportException, IOException { + if (!StringUtils.isEmpty(customBackendFileName) && !customBackendFileName.contains( + APIConstants.SYNAPSE_POLICY_DEFINITION_EXTENSION_XML)) { + customBackendFileName = customBackendFileName + APIConstants.SYNAPSE_POLICY_DEFINITION_EXTENSION_XML; + } + String customBackendName = + archivePath + File.separator + ImportExportConstants.CUSTOM_BACKEND_DIRECTORY + File.separator + + customBackendFileName; + CommonUtil.writeFile(customBackendName, sequence); + } + /** * Retrieve the deployed gateway environments and store those in the archive directory. * 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/ImportUtils.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/ImportUtils.java index 71646c2243fc..d4f5b0891ed8 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/ImportUtils.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/ImportUtils.java @@ -123,6 +123,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import javax.validation.constraints.NotNull; /** @@ -263,6 +264,10 @@ public static ImportedAPIDTO importApi(String extractedFolderPath, APIDTO import // validate the API context APIUtil.validateAPIContext(importedApiDTO.getContext(), importedApiDTO.getName()); + // Get the endpoint config object updated + APIUtil.validateAPIEndpointConfig(importedApiDTO.getEndpointConfig(), importedApiDTO.getType().toString(), + importedApiDTO.getName()); + API targetApi = retrieveApiToOverwrite(importedApiDTO.getName(), importedApiDTO.getVersion(), currentTenantDomain, apiProvider, Boolean.TRUE, organization); @@ -383,6 +388,13 @@ public static ImportedAPIDTO importApi(String extractedFolderPath, APIDTO import populateAPIWithPolicies(importedApi, apiProvider, extractedFolderPath, extractedPoliciesMap, extractedAPIPolicies, currentTenantDomain); + // Update Custom Backend Data if endpoint type is selected to "custom_backend" + Map endpointConf = (Map) importedApiDTO.getEndpointConfig(); + if (endpointConf != null && APIConstants.ENDPOINT_TYPE_SEQUENCE.equals( + endpointConf.get(APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE))) { + updateAPIWithCustomBackend(importedApi, extractedFolderPath, apiProvider); + } + API oldAPI = apiProvider.getAPIbyUUID(importedApi.getUuid(), importedApi.getOrganization()); apiProvider.updateAPI(importedApi, oldAPI); @@ -671,6 +683,51 @@ public static void validateAppliedPolicy(OperationPolicy appliedPolicy, } } + /** + * Method is used to Update the Custom Backend of the API + * + * @param api API Object + * @param extractedFolderPath Extracted folder path + * @param apiProvider API Provider + * @throws APIManagementException Throws an error occurs while reading the sequence as an Input Stream + */ + public static void updateAPIWithCustomBackend(API api, String extractedFolderPath, APIProvider apiProvider) + throws APIManagementException { + String customBackendDir = extractedFolderPath + File.separator + ImportExportConstants.CUSTOM_BACKEND_DIRECTORY; + if (api != null && !StringUtils.isEmpty(api.getEndpointConfig())) { + JsonObject endpointConfig = JsonParser.parseString(api.getEndpointConfig()).getAsJsonObject(); + + if (endpointConfig != null) { + if (endpointConfig.get("sandbox") != null) { + String seqFile = endpointConfig.get("sandbox").getAsString(); + String seqId = UUID.randomUUID().toString(); + String seq = APIUtil.getCustomBackendSequence(customBackendDir, seqFile, ".xml"); + if (!StringUtils.isEmpty(seqFile) && !seqFile.contains( + APIConstants.SYNAPSE_POLICY_DEFINITION_EXTENSION_XML)) { + seqFile = seqFile + APIConstants.SYNAPSE_POLICY_DEFINITION_EXTENSION_XML; + } + if (seq != null) { + apiProvider.updateCustomBackend(api.getUuid(), APIConstants.API_KEY_TYPE_SANDBOX, seq, seqFile, + seqId); + } + } + if (endpointConfig.get("production") != null) { + String seqFile = endpointConfig.get("production").getAsString(); + String seqId = UUID.randomUUID().toString(); + String seq = APIUtil.getCustomBackendSequence(customBackendDir, seqFile, ".xml"); + if (!StringUtils.isEmpty(seqFile) && !seqFile.contains( + APIConstants.SYNAPSE_POLICY_DEFINITION_EXTENSION_XML)) { + seqFile = seqFile + APIConstants.SYNAPSE_POLICY_DEFINITION_EXTENSION_XML; + } + if (seq != null) { + apiProvider.updateCustomBackend(api.getUuid(), APIConstants.API_KEY_TYPE_PRODUCTION, seq, + seqFile, seqId); + } + } + } + } + } + /** * This method is used to populate uri template of the API with API Level Policies and Operation Level Policies. * diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/PublisherCommonUtils.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/PublisherCommonUtils.java index 8931bc46fa37..d8635ad5bbba 100755 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/PublisherCommonUtils.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/PublisherCommonUtils.java @@ -21,6 +21,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import graphql.schema.GraphQLSchema; import graphql.schema.idl.SchemaParser; import graphql.schema.idl.TypeDefinitionRegistry; @@ -29,6 +31,8 @@ import graphql.schema.validation.SchemaValidationError; import graphql.schema.validation.SchemaValidator; import io.swagger.v3.parser.ObjectMapperFactory; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; @@ -98,9 +102,11 @@ import org.wso2.carbon.core.util.CryptoUtil; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -110,6 +116,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -189,11 +196,57 @@ public static API updateApi(API originalAPI, APIDTO apiDtoToUpdate, APIProvider API apiToUpdate = prepareForUpdateApi(originalAPI, apiDtoToUpdate, apiProvider, tokenScopes); apiProvider.updateAPI(apiToUpdate, originalAPI); + API apiUpdated = apiProvider.getAPIbyUUID(originalAPI.getUuid(), originalAPI.getOrganization()); + if (apiUpdated != null && !StringUtils.isEmpty(apiUpdated.getEndpointConfig())) { + JsonObject endpointConfig = JsonParser.parseString(apiUpdated.getEndpointConfig()).getAsJsonObject(); + if (!APIConstants.ENDPOINT_TYPE_SEQUENCE.equals( + endpointConfig.get(APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE).getAsString()) && ( + APIConstants.API_TYPE_HTTP.equals(apiUpdated.getType()) || APIConstants.API_TYPE_SOAPTOREST.equals( + apiUpdated.getType()))) { + apiProvider.deleteSequenceBackendByRevision(apiUpdated.getUuid(), "0"); + } + } + return apiUpdated; + } - return apiProvider.getAPIbyUUID(originalAPI.getUuid(), originalAPI.getOrganization()); - // TODO use returend api + /** + * @param api API of the Custom Backend + * @param apiProvider API Provider + * @param endpointType Endpoint Type of the Custom Backend (SANDBOX, PRODUCTION) + * @param customBackend Custom Backend + * @param contentDecomp Header Content of the Request + * @throws APIManagementException If an error occurs while updating the API and API definition + */ + public static void updateCustomBackend(API api, APIProvider apiProvider, String endpointType, + InputStream customBackend, String contentDecomp) throws APIManagementException { + String fileName = getFileNameFromContentDisposition(contentDecomp); + if (fileName == null) { + throw new APIManagementException( + "Error when retrieving Custom Backend file name of API: " + api.getId().getApiName()); + } + String customBackendUUID = UUID.randomUUID().toString(); + try { + String customBackendStr = IOUtils.toString(customBackend); + apiProvider.updateCustomBackend(api.getUuid(), endpointType, customBackendStr, fileName, customBackendUUID); + } catch (IOException ex) { + throw new APIManagementException("Error retrieving sequence backend of API: " + api.getUuid(), ex); + } + } + + private static String getFileNameFromContentDisposition(String contentDisposition) { + // Split the Content-Disposition header to get the file name + String[] parts = contentDisposition.split(";"); + for (String part : parts) { + if (part.trim().startsWith("filename")) { + // Extract the file name value + return part.split("=")[1].trim().replace("\"", ""); + } + } + return null; } + + /** * Prepare for API object before updating the API. * @@ -216,6 +269,8 @@ private static API prepareForUpdateApi(API originalAPI, APIDTO apiDtoToUpdate, A + " as the token information hasn't been correctly set internally", ExceptionCodes.TOKEN_SCOPES_NOT_SET); } + APIUtil.validateAPIEndpointConfig(apiDtoToUpdate.getEndpointConfig(), apiDtoToUpdate.getType().toString(), + apiDtoToUpdate.getName()); if (apiDtoToUpdate.getVisibility() == APIDTO.VisibilityEnum.RESTRICTED && apiDtoToUpdate.getVisibleRoles() .isEmpty()) { throw new APIManagementException("Access control roles cannot be empty when visibility is restricted", @@ -294,6 +349,25 @@ private static API prepareForUpdateApi(API originalAPI, APIDTO apiDtoToUpdate, A encryptEndpointSecurityApiKeyCredentials(endpointConfig, cryptoUtil, oldProductionApiKeyValue, oldSandboxApiKeyValue, apiDtoToUpdate); + // update endpointConfig with the provided custom sequence + if (endpointConfig != null) { + if (APIConstants.ENDPOINT_TYPE_SEQUENCE.equals( + endpointConfig.get(APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE))) { + try { + if (endpointConfig.get("sequence_path") != null) { + String pathToSequence = endpointConfig.get("sequence_path").toString(); + String sequence = FileUtils.readFileToString(new File(pathToSequence), + Charset.defaultCharset()); + endpointConfig.put("sequence", sequence); + apiDtoToUpdate.setEndpointConfig(endpointConfig); + } + } catch (IOException ex) { + throw new APIManagementException( + "Error while reading Custom Sequence of API: " + apiDtoToUpdate.getId(), ex, + ExceptionCodes.ERROR_READING_CUSTOM_SEQUENCE); + } + } + } // AWS Lambda: secret key encryption while updating the API if (apiDtoToUpdate.getEndpointConfig() != null) { @@ -1074,7 +1148,6 @@ public static API addAPIWithGeneratedSwaggerDefinition(APIDTO apiDto, String oas apiDto); // AWS Lambda: secret key encryption while creating the API - if (apiDto.getEndpointConfig() != null) { if (endpointConfig.containsKey(APIConstants.AMZN_SECRET_KEY)) { String secretKey = (String) endpointConfig.get(APIConstants.AMZN_SECRET_KEY); if (!StringUtils.isEmpty(secretKey)) { @@ -1083,7 +1156,6 @@ public static API addAPIWithGeneratedSwaggerDefinition(APIDTO apiDto, String oas apiDto.setEndpointConfig(endpointConfig); } } - } /* if (isWSAPI) { ArrayList websocketTransports = new ArrayList<>(); diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApi.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApi.java index 95ec977cfc81..7b687a1250a5 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApi.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApi.java @@ -40,6 +40,7 @@ import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ResourcePathListDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ResourcePolicyInfoDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ResourcePolicyListDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.SequenceBackendListDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ThrottlingPolicyDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.TopicListDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.WSDLInfoDTO; @@ -1388,6 +1389,46 @@ public Response getOperationPolicyForAPIByPolicyId(@ApiParam(value = "**API ID** return delegate.getRepliesOfComment(commentId, apiId, xWSO2Tenant, limit, offset, ifNoneMatch, includeCommenterInfo, securityContext); } + @GET + @Path("/{apiId}/sequence-backend/{type}/content") + + @Produces({ "application/xml", "application/json" }) + @ApiOperation(value = "Get Sequence of Custom Backend", notes = "This operation can be used to get Sequence of the Custom Backend", response = File.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:api_view", description = "View API"), + @AuthorizationScope(scope = "apim:api_manage", description = "Manage all API related operations"), + @AuthorizationScope(scope = "apim:api_import_export", description = "Import and export APIs related operations"), + @AuthorizationScope(scope = "apim:api_product_import_export", description = "Import and export API Products related operations") + }) + }, tags={ "APIs", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. Requested API Custom Backend is returned ", response = File.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 406, message = "Not Acceptable. The requested media type is not supported.", response = ErrorDTO.class) }) + public Response getSequenceBackendContent( @Size(max=15)@ApiParam(value = "Type of the Endpoint. SANDBOX or PRODUCTION ",required=true) @PathParam("type") String type, @ApiParam(value = "**API ID** consisting of the **UUID** of the API. ",required=true) @PathParam("apiId") String apiId) throws APIManagementException{ + return delegate.getSequenceBackendContent(type, apiId, securityContext); + } + + @GET + @Path("/{apiId}/sequence-backend") + + @Produces({ "application/json" }) + @ApiOperation(value = "Get Sequence Backends of the API", notes = "This operation can be used to get Sequence Backend data of the API", response = SequenceBackendListDTO.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:api_view", description = "View API"), + @AuthorizationScope(scope = "apim:api_manage", description = "Manage all API related operations"), + @AuthorizationScope(scope = "apim:api_import_export", description = "Import and export APIs related operations"), + @AuthorizationScope(scope = "apim:api_product_import_export", description = "Import and export API Products related operations") + }) + }, tags={ "APIs", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. Requested API Sequence Backend is returned ", response = SequenceBackendListDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 406, message = "Not Acceptable. The requested media type is not supported.", response = ErrorDTO.class) }) + public Response getSequenceBackendData(@ApiParam(value = "**API ID** consisting of the **UUID** of the API. ",required=true) @PathParam("apiId") String apiId) throws APIManagementException{ + return delegate.getSequenceBackendData(apiId, securityContext); + } + @GET @Path("/{apiId}/wsdl-info") @@ -1589,6 +1630,49 @@ public Response restoreAPIRevision(@ApiParam(value = "**API ID** consisting of t return delegate.restoreAPIRevision(apiId, revisionId, securityContext); } + @DELETE + @Path("/{apiId}/sequence-backend/{type}") + + @Produces({ "application/json" }) + @ApiOperation(value = "Delete Sequence Backend of the API", notes = "This operation can be used to remove the Sequence Backend of the API", response = Void.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:api_delete", description = "Delete API"), + @AuthorizationScope(scope = "apim:api_manage", description = "Manage all API related operations"), + @AuthorizationScope(scope = "apim:api_import_export", description = "Import and export APIs related operations") + }) + }, tags={ "APIs", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. Resource successfully deleted. ", response = Void.class), + @ApiResponse(code = 403, message = "Forbidden. The request must be conditional but no condition has been specified.", response = ErrorDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 409, message = "Conflict. Specified resource already exists.", response = ErrorDTO.class), + @ApiResponse(code = 412, message = "Precondition Failed. The request has not been performed because one of the preconditions is not met.", response = ErrorDTO.class) }) + public Response sequenceBackendDelete( @Size(max=15)@ApiParam(value = "Type of the Endpoint. SANDBOX or PRODUCTION ",required=true) @PathParam("type") String type, @ApiParam(value = "**API ID** consisting of the **UUID** of the API. ",required=true) @PathParam("apiId") String apiId) throws APIManagementException{ + return delegate.sequenceBackendDelete(type, apiId, securityContext); + } + + @PUT + @Path("/{apiId}/sequence-backend") + @Consumes({ "multipart/form-data" }) + @Produces({ "application/json" }) + @ApiOperation(value = "Upload Sequence Sequence as the Endpoint of the API", notes = "This operation can be used to change the endpoint of the API to Sequence Backend", response = APIDTO.class, authorizations = { + @Authorization(value = "OAuth2Security", scopes = { + @AuthorizationScope(scope = "apim:api_create", description = "Create API"), + @AuthorizationScope(scope = "apim:api_manage", description = "Manage all API related operations"), + @AuthorizationScope(scope = "apim:api_publish", description = "Publish API") + }) + }, tags={ "APIs", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. Successful response with updated API object ", response = APIDTO.class), + @ApiResponse(code = 400, message = "Bad Request. Invalid request or validation error.", response = ErrorDTO.class), + @ApiResponse(code = 403, message = "Forbidden. The request must be conditional but no condition has been specified.", response = ErrorDTO.class), + @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), + @ApiResponse(code = 409, message = "Conflict. Specified resource already exists.", response = ErrorDTO.class), + @ApiResponse(code = 412, message = "Precondition Failed. The request has not been performed because one of the preconditions is not met.", response = ErrorDTO.class) }) + public Response sequenceBackendUpdate(@ApiParam(value = "**API ID** consisting of the **UUID** of the API. ",required=true) @PathParam("apiId") String apiId, @Multipart(value = "sequence", required = false) InputStream sequenceInputStream, @Multipart(value = "sequence" , required = false) Attachment sequenceDetail, @Multipart(value = "type", required = false) String type) throws APIManagementException{ + return delegate.sequenceBackendUpdate(apiId, sequenceInputStream, sequenceDetail, type, securityContext); + } + @POST @Path("/{apiId}/undeploy-revision") @Consumes({ "application/json" }) diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApiService.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApiService.java index e6b1fd08e701..7caffe1a712f 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApiService.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApiService.java @@ -49,6 +49,7 @@ import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ResourcePathListDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ResourcePolicyInfoDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ResourcePolicyListDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.SequenceBackendListDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ThrottlingPolicyDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.TopicListDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.WSDLInfoDTO; @@ -130,6 +131,8 @@ public interface ApisApiService { public Response getGraphQLPolicyComplexityTypesOfAPI(String apiId, MessageContext messageContext) throws APIManagementException; public Response getOperationPolicyForAPIByPolicyId(String apiId, String operationPolicyId, MessageContext messageContext) throws APIManagementException; public Response getRepliesOfComment(String commentId, String apiId, String xWSO2Tenant, Integer limit, Integer offset, String ifNoneMatch, Boolean includeCommenterInfo, MessageContext messageContext) throws APIManagementException; + public Response getSequenceBackendContent(String type, String apiId, MessageContext messageContext) throws APIManagementException; + public Response getSequenceBackendData(String apiId, MessageContext messageContext) throws APIManagementException; public Response getWSDLInfoOfAPI(String apiId, MessageContext messageContext) throws APIManagementException; public Response getWSDLOfAPI(String apiId, String ifNoneMatch, MessageContext messageContext) throws APIManagementException; public Response importAPI(InputStream fileInputStream, Attachment fileDetail, Boolean preserveProvider, Boolean rotateRevision, Boolean overwrite, Boolean preservePortalConfigurations, String accept, MessageContext messageContext) throws APIManagementException; @@ -141,6 +144,8 @@ public interface ApisApiService { public Response publishAPIToExternalStores(String apiId, String externalStoreIds, String ifMatch, MessageContext messageContext) throws APIManagementException; public Response reimportServiceFromCatalog(String apiId, MessageContext messageContext) throws APIManagementException; public Response restoreAPIRevision(String apiId, String revisionId, MessageContext messageContext) throws APIManagementException; + public Response sequenceBackendDelete(String type, String apiId, MessageContext messageContext) throws APIManagementException; + public Response sequenceBackendUpdate(String apiId, InputStream sequenceInputStream, Attachment sequenceDetail, String type, MessageContext messageContext) throws APIManagementException; public Response undeployAPIRevision(String apiId, String revisionId, String revisionNumber, Boolean allEnvironments, List apIRevisionDeploymentDTO, MessageContext messageContext) throws APIManagementException; public Response updateAPI(String apiId, APIDTO APIDTO, String ifMatch, MessageContext messageContext) throws APIManagementException; public Response updateAPIClientCertificateByAlias(String alias, String apiId, InputStream certificateInputStream, Attachment certificateDetail, String tier, MessageContext messageContext) throws APIManagementException; 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/ApisApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/ApisApiServiceImpl.java index 90f3ca802aa2..b203f4b37286 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/ApisApiServiceImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/ApisApiServiceImpl.java @@ -82,6 +82,7 @@ import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import java.io.*; @@ -167,6 +168,48 @@ public Response getAllAPIs(Integer limit, Integer offset, String sortBy, String return null; } + @Override + public Response getSequenceBackendData(String apiId, MessageContext messageContext) throws APIManagementException { + APIProvider apiProvider = RestApiCommonUtil.getLoggedInUserProvider(); + + //validate if api exists + CommonUtils.validateAPIExistence(apiId); + List data = apiProvider.getAllSequenceBackendsByAPIUUID(apiId); + if (data == null) { + throw new APIMgtResourceNotFoundException( + "Couldn't retrieve an existing Sequence Backend for API " + apiId, + ExceptionCodes.from(ExceptionCodes.CUSTOM_BACKEND_NOT_FOUND, apiId)); + } + SequenceBackendListDTO respObj = APIMappingUtil.fromSequenceDataToDTO(data); + return Response.ok().entity(respObj).build(); + } + + @Override + public Response getSequenceBackendContent(String type, String apiId, MessageContext messageContext) throws APIManagementException { + APIProvider apiProvider = RestApiCommonUtil.getLoggedInUserProvider(); + CommonUtils.validateAPIExistence(apiId); + + SequenceBackendData data = apiProvider.getCustomBackendByAPIUUID(apiId, type); + if (data == null) { + throw new APIMgtResourceNotFoundException( + "Couldn't retrieve an existing Sequence Backend for API: " + apiId, + ExceptionCodes.from(ExceptionCodes.CUSTOM_BACKEND_NOT_FOUND, apiId)); + } + File file = RestApiPublisherUtils.exportCustomBackendData(data.getSequence(), data.getName()); + return Response.ok(file) + .header(RestApiConstants.HEADER_CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"") + .build(); + } + + @Override + public Response sequenceBackendDelete(String type, String apiId, MessageContext messageContext) throws APIManagementException { + APIProvider apiProvider = RestApiCommonUtil.getLoggedInUserProvider(); + //validate if api exists + CommonUtils.validateAPIExistence(apiId); + apiProvider.deleteCustomBackendByID(apiId, type); + return Response.ok().build(); + } + @Override public Response createAPI(APIDTO body, String oasVersion, MessageContext messageContext) throws APIManagementException{ @@ -202,6 +245,21 @@ public Response getAPI(String apiId, String xWSO2Tenant, String ifNoneMatch, return Response.ok().entity(apiToReturn).build(); } + @Override + public Response sequenceBackendUpdate(String apiId, InputStream sequenceInputStream, + Attachment sequenceDetail, String type, MessageContext messageContext) throws APIManagementException { + String username = RestApiCommonUtil.getLoggedInUsername(); + APIProvider apiProvider = RestApiCommonUtil.getProvider(username); + String organization = RestApiUtil.getValidatedOrganization(messageContext); + API api = apiProvider.getAPIbyUUID(apiId, organization); + api.setOrganization(organization); + MultivaluedMap headers = sequenceDetail.getHeaders(); + String contentDecomp = headers.getFirst("Content-Disposition"); + + PublisherCommonUtils.updateCustomBackend(api, apiProvider, type, sequenceInputStream, contentDecomp); + return Response.ok().build(); + } + @Override public Response addCommentToAPI(String apiId, PostRequestBodyDTO postRequestBodyDTO, String replyTo, MessageContext messageContext) throws APIManagementException { @@ -3892,6 +3950,15 @@ public Response deployAPIRevision(String apiId, String revisionId, //validate whether the API is advertise only APIDTO apiDto = getAPIByID(apiId, apiProvider, organization); + + // Cannot deploy an API with custom sequence to the APK gateway + Map endpointConfigMap = (Map) apiDto.getEndpointConfig(); + if (endpointConfigMap != null && !APIConstants.WSO2_SYNAPSE_GATEWAY.equals(apiDto.getGatewayType()) + && APIConstants.ENDPOINT_TYPE_SEQUENCE.equals( + endpointConfigMap.get(APIConstants.API_ENDPOINT_CONFIG_PROTOCOL_TYPE))) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Cannot Deploy an API with a Custom Sequence to APK Gateway: " + apiId).build(); + } // Reject the request if API lifecycle is 'RETIRED'. if (apiDto.getLifeCycleStatus().equals(APIConstants.RETIRED)) { String errorMessage = "Deploying API Revisions is not supported for retired APIs. ApiId: " + apiId; diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/utils/RestApiPublisherUtils.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/utils/RestApiPublisherUtils.java index ad580f43d337..cd7022132f65 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/utils/RestApiPublisherUtils.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/utils/RestApiPublisherUtils.java @@ -277,6 +277,20 @@ public static String getContentType(Attachment fileDetail) { return fileContentType; } + public static File exportCustomBackendData(String seq, String seqName) throws APIManagementException { + try { + // Provided Sequence Name by the user + String customBackendName = seqName; + if (!customBackendName.contains(".xml")) { + customBackendName = seqName + APIConstants.SYNAPSE_POLICY_DEFINITION_EXTENSION_XML; + } + CommonUtil.writeFile(customBackendName, seq); + return new File(customBackendName); + } catch (APIImportExportException ex) { + throw new APIManagementException("Error when exporting Custom Backend: " + seqName, ex); + } + } + public static File exportOperationPolicyData(OperationPolicyData policyData, String format) throws APIManagementException { 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 d25b133d4465..b5e358788c95 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 @@ -487,6 +487,196 @@ paths: -H "Content-Type: application/json" -d @data.json "https://127.0.0.1:9443/api/am/publisher/v4/apis/7a2298c4-c905-403f-8fac-38c73301631f/topics"' + /apis/{apiId}/sequence-backend/{type}/content: + get: + tags: + - APIs + summary: Get Sequence of Custom Backend + description: This operation can be used to get Sequence of the Custom Backend + operationId: getSequenceBackendContent + parameters: + - name: type + in: path + description: | + Type of the Endpoint. + SANDBOX or PRODUCTION + required: true + schema: + maxLength: 15 + type: string + - $ref: '#/components/parameters/apiId' + responses: + 200: + description: | + OK. + Requested API Custom Backend is returned + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/xml: + schema: + type: string + format: binary + 404: + $ref: '#/components/responses/NotFound' + 406: + $ref: '#/components/responses/NotAcceptable' + security: + - OAuth2Security: + - apim:api_view + - apim:api_manage + - apim:api_import_export + - apim:api_product_import_export + x-code-samples: + - lang: Curl + source: 'curl -k -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://127.0.0.1:9443/api/am/publisher/v4/apis/7a2298c4-c905-403f-8fac-38c73301631f/custom-backend/7a2298c4-c915-483f-8fac-38c73301631f/content"' + + /apis/{apiId}/sequence-backend/{type}: + delete: + tags: + - APIs + summary: Delete Sequence Backend of the API + description: This operation can be used to remove the Sequence Backend of the API + operationId: sequenceBackendDelete + parameters: + - name: type + in: path + description: | + Type of the Endpoint. + SANDBOX or PRODUCTION + required: true + schema: + maxLength: 15 + type: string + - $ref: '#/components/parameters/apiId' + responses: + 200: + description: | + OK. + Resource successfully deleted. + content: { } + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 409: + $ref: '#/components/responses/Conflict' + 412: + $ref: '#/components/responses/PreconditionFailed' + security: + - OAuth2Security: + - apim:api_delete + - apim:api_manage + - apim:api_import_export + x-code-samples: + - lang: Curl + source: 'curl -k -X DELETE -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://127.0.0.1:9443/api/am/publisher/v4/apis/7a2298c4-c905-403f-8fac-38c73301631f/custom-backend/7a2298c4-c915-483f-8fac-38c73301631f"' + + /apis/{apiId}/sequence-backend: + get: + tags: + - APIs + summary: Get Sequence Backends of the API + description: This operation can be used to get Sequence Backend data of the API + operationId: getSequenceBackendData + parameters: + - $ref: '#/components/parameters/apiId' + responses: + 200: + description: | + OK. + Requested API Sequence Backend is returned + headers: + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/SequenceBackendList' + 404: + $ref: '#/components/responses/NotFound' + 406: + $ref: '#/components/responses/NotAcceptable' + security: + - OAuth2Security: + - apim:api_view + - apim:api_manage + - apim:api_import_export + - apim:api_product_import_export + x-code-samples: + - lang: Curl + source: 'curl -k -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + "https://127.0.0.1:9443/api/am/publisher/v4/apis/7a2298c4-c905-403f-8fac-38c73301631f/custom-backend/7a2298c4-c915-483f-8fac-38c73301631f"' + put: + tags: + - APIs + summary: Upload Sequence Sequence as the Endpoint of the API + description: This operation can be used to change the endpoint of the API to Sequence Backend + operationId: sequenceBackendUpdate + parameters: + - $ref: '#/components/parameters/apiId' + requestBody: + content: + multipart/form-data: + schema: + properties: + sequence: + type: string + description: The sequence that needs to be uploaded. + format: binary + type: + type: string + description: Type of the Endpoint + responses: + 200: + description: | + OK. + Successful response with updated API object + headers: + Location: + description: | + The URL of the newly created resource. + schema: + type: string + Content-Type: + description: | + The content type of the body. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/API' + 400: + $ref: '#/components/responses/BadRequest' + 403: + $ref: '#/components/responses/Forbidden' + 404: + $ref: '#/components/responses/NotFound' + 409: + $ref: '#/components/responses/Conflict' + 412: + $ref: '#/components/responses/PreconditionFailed' + security: + - OAuth2Security: + - apim:api_create + - apim:api_manage + - apim:api_publish + x-code-samples: + - lang: Curl + source: 'curl -k -X PUT -H "Authorization: Bearer ae4eae22-3f65-387b-a171-d37eaa366fa8" + -H "Content-Type: application/json" -d @data.json "https://127.0.0.1:9443/api/am/publisher/v4/apis/7a2298c4-c905-403f-8fac-38c73301631f/custom-backend"' + + /apis/{apiId}/reimport-service: put: tags: @@ -9358,6 +9548,39 @@ paths: -H "Content-Type: application/json" -d @data.json "https://127.0.0.1:9443/api/am/admin/v4/workflows/update-workflow-status?workflowReferenceId=56e3a170-a7a7-45f8-b051-7e43a58a67e1"' components: schemas: + SequenceBackendList: + title: Sequence Backend List + type: object + properties: + count: + type: integer + description: | + Number of Sequence Backends returned. + example: 1 + list: + type: array + items: + $ref: '#/components/schemas/SequenceBackend' + SequenceBackend: + title: Sequence Backend + type: object + properties: + sequenceId: + type: string + readOnly: true + example: 943d3002-000c-42d3-a1b9-d6559f8a4d49 + sequenceName: + type: string + readOnly: true + example: 943d3002-000c-42d3-a1b9-d6559f8a4d49-SANDBOX + sequenceType: + type: string + readOnly: true + example: SANDBOX + sequence: + type: string + format: binary + readOnly: true Comment: title: Comment required: diff --git a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/OGG/oracle/apimgt/tables.sql b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/OGG/oracle/apimgt/tables.sql index 16b7d7ea56b3..87bde5d3d63a 100644 --- a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/OGG/oracle/apimgt/tables.sql +++ b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/OGG/oracle/apimgt/tables.sql @@ -1496,6 +1496,17 @@ CREATE TABLE AM_API ( UNIQUE (API_UUID) ) / +CREATE TABLE AM_API_SEQUENCE_BACKEND ( + ID VARCHAR2(60) NOT NULL, + API_UUID VARCHAR2(256) NOT NULL, + REVISION_UUID VARCHAR2(256) DEFAULT '0', + SEQUENCE BLOB NOT NULL, + NAME VARCHAR2(256) NOT NULL, + TYPE VARCHAR2(120) NOT NULL, + PRIMARY KEY (ID,API_UUID,REVISION_UUID,TYPE), + FOREIGN KEY (API_UUID) REFERENCES AM_API(API_UUID) ON DELETE CASCADE +) +/ CREATE TABLE AM_GRAPHQL_COMPLEXITY ( UUID VARCHAR(256), API_ID INTEGER NOT NULL, diff --git a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/OGG/oracle/apimgt/tables_23c.sql b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/OGG/oracle/apimgt/tables_23c.sql index 25ecda61f38d..8f404e39d9d2 100644 --- a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/OGG/oracle/apimgt/tables_23c.sql +++ b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/OGG/oracle/apimgt/tables_23c.sql @@ -1496,6 +1496,17 @@ CREATE TABLE AM_API ( UNIQUE (API_UUID) ) / +CREATE TABLE AM_API_SEQUENCE_BACKEND ( + ID VARCHAR2(60) NOT NULL, + API_UUID VARCHAR2(256) NOT NULL, + REVISION_UUID VARCHAR2(256) DEFAULT '0', + SEQUENCE BLOB NOT NULL, + NAME VARCHAR2(256) NOT NULL, + TYPE VARCHAR2(120) NOT NULL, + PRIMARY KEY (ID,API_UUID,REVISION_UUID,TYPE), + FOREIGN KEY (API_UUID) REFERENCES AM_API(API_UUID) ON DELETE CASCADE +) +/ CREATE TABLE AM_GRAPHQL_COMPLEXITY ( UUID VARCHAR(256), API_ID INTEGER NOT NULL, diff --git a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/Postgresql/apimgt/tables.sql b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/Postgresql/apimgt/tables.sql index 6a9900d85673..0432714113c9 100644 --- a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/Postgresql/apimgt/tables.sql +++ b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/Postgresql/apimgt/tables.sql @@ -1860,6 +1860,17 @@ CREATE TABLE IF NOT EXISTS AM_API ( UNIQUE (API_UUID) ); +CREATE TABLE IF NOT EXISTS AM_API_SEQUENCE_BACKEND ( + ID VARCHAR(60) NOT NULL, + API_UUID VARCHAR(256) NOT NULL, + REVISION_UUID VARCHAR(256) DEFAULT '0', + SEQUENCE BYTEA NOT NULL, + NAME VARCHAR(256) NOT NULL, + TYPE VARCHAR(120) NOT NULL, + PRIMARY KEY (ID,API_UUID,REVISION_UUID,TYPE), + FOREIGN KEY (API_UUID) REFERENCES AM_API(API_UUID) ON DELETE CASCADE +); + CREATE TABLE IF NOT EXISTS AM_GRAPHQL_COMPLEXITY ( UUID VARCHAR(256), API_ID INTEGER NOT NULL, diff --git a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/SQLServer/mssql/apimgt/tables.sql b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/SQLServer/mssql/apimgt/tables.sql index b383d7f619dc..eb2091cb8964 100644 --- a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/SQLServer/mssql/apimgt/tables.sql +++ b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/multi-dc/SQLServer/mssql/apimgt/tables.sql @@ -1729,6 +1729,17 @@ CREATE TABLE AM_API ( UNIQUE (API_UUID) ); +CREATE TABLE AM_API_SEQUENCE_BACKEND ( + ID VARCHAR(60) NOT NULL, + API_UUID VARCHAR(256) NOT NULL, + REVISION_UUID VARCHAR(256) DEFAULT '0', + SEQUENCE VARBINARY(MAX) NOT NULL, + NAME VARCHAR(256) NOT NULL, + TYPE VARCHAR(120) NOT NULL, + PRIMARY KEY (ID, API_UUID, REVISION_UUID, TYPE), + FOREIGN KEY (API_UUID) REFERENCES AM_API(API_UUID) ON DELETE CASCADE +); + IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[AM_GRAPHQL_COMPLEXITY]') AND TYPE IN (N'U')) CREATE TABLE AM_GRAPHQL_COMPLEXITY ( diff --git a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/h2.sql b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/h2.sql index 1c512858103d..638b489ed955 100644 --- a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/h2.sql +++ b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/h2.sql @@ -1585,6 +1585,17 @@ CREATE TABLE IF NOT EXISTS AM_API ( UNIQUE (API_PROVIDER,API_NAME,API_VERSION,ORGANIZATION) ); +CREATE TABLE IF NOT EXISTS AM_API_SEQUENCE_BACKEND ( + ID VARCHAR(60) NOT NULL, + API_UUID VARCHAR(256) NOT NULL, + REVISION_UUID VARCHAR(256) DEFAULT '0', + SEQUENCE LONGBLOB NOT NULL, + NAME VARCHAR(256) NOT NULL, + TYPE VARCHAR(120) NOT NULL, + PRIMARY KEY(ID), + FOREIGN KEY(API_UUID) REFERENCES AM_API(API_UUID) ON DELETE CASCADE +); + CREATE TABLE IF NOT EXISTS AM_GRAPHQL_COMPLEXITY ( UUID VARCHAR(256), API_ID INTEGER NOT NULL, diff --git a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/mssql.sql b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/mssql.sql index 62f623739490..8aa2f087bc83 100644 --- a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/mssql.sql +++ b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/mssql.sql @@ -1740,6 +1740,8 @@ CREATE TABLE AM_API ( UNIQUE (API_UUID) ); + + IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[AM_GRAPHQL_COMPLEXITY]') AND TYPE IN (N'U')) CREATE TABLE AM_GRAPHQL_COMPLEXITY ( diff --git a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/mysql.sql b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/mysql.sql index a279934e9286..6667963e6002 100644 --- a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/mysql.sql +++ b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/mysql.sql @@ -1524,6 +1524,17 @@ CREATE TABLE IF NOT EXISTS AM_API ( UNIQUE (API_UUID) )ENGINE INNODB; +CREATE TABLE IF NOT EXISTS AM_API_SEQUENCE_BACKEND ( + ID VARCHAR(60) NOT NULL, + API_UUID VARCHAR(256) NOT NULL, + REVISION_UUID VARCHAR(256) DEFAULT '0', + SEQUENCE LONGBLOB NOT NULL, + NAME VARCHAR(256) NOT NULL, + TYPE VARCHAR(120) NOT NULL, + PRIMARY KEY (ID,API_UUID,REVISION_UUID,TYPE), + FOREIGN KEY (API_UUID) REFERENCES AM_API(API_UUID) ON DELETE CASCADE +)ENGINE INNODB; + CREATE TABLE IF NOT EXISTS AM_GRAPHQL_COMPLEXITY ( UUID VARCHAR(256), API_ID INTEGER NOT NULL, diff --git a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/oracle.sql b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/oracle.sql index 1b5e1591a845..c71ec2599a5e 100644 --- a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/oracle.sql +++ b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/oracle.sql @@ -3458,6 +3458,18 @@ CREATE TABLE AM_API_REVISION_METADATA ( ) / +CREATE TABLE AM_API_SEQUENCE_BACKEND ( + ID VARCHAR2(60) NOT NULL, + API_UUID VARCHAR2(256) NOT NULL, + REVISION_UUID VARCHAR2(256) DEFAULT '0', + SEQUENCE BLOB NOT NULL, + NAME VARCHAR2(256) NOT NULL, + TYPE VARCHAR2(120) NOT NULL, + PRIMARY KEY (ID,API_UUID,REVISION_UUID,TYPE), + FOREIGN KEY (API_UUID) REFERENCES AM_API(API_UUID) ON DELETE CASCADE +) +/ + CREATE TABLE AM_DEPLOYMENT_REVISION_MAPPING ( NAME VARCHAR(255) NOT NULL, VHOST VARCHAR(255) NULL, diff --git a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/oracle_23c.sql b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/oracle_23c.sql index 9988a92fe6eb..85c475646ac3 100644 --- a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/oracle_23c.sql +++ b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/oracle_23c.sql @@ -2471,6 +2471,18 @@ CREATE TABLE AM_API ( ) / +CREATE TABLE AM_API_SEQUENCE_BACKEND ( + ID VARCHAR2(60) NOT NULL, + API_UUID VARCHAR2(256) NOT NULL, + REVISION_UUID VARCHAR2(256) DEFAULT '0', + SEQUENCE BLOB NOT NULL, + NAME VARCHAR2(256) NOT NULL, + TYPE VARCHAR2(120) NOT NULL, + PRIMARY KEY (ID,API_UUID,REVISION_UUID,TYPE), + FOREIGN KEY (API_UUID) REFERENCES AM_API(API_UUID) ON DELETE CASCADE +) +/ + CREATE TABLE AM_GRAPHQL_COMPLEXITY ( UUID VARCHAR(256), API_ID INTEGER NOT NULL, diff --git a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/oracle_rac.sql b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/oracle_rac.sql index 582f2c25f263..61d58f5232ee 100644 --- a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/oracle_rac.sql +++ b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/oracle_rac.sql @@ -2461,6 +2461,18 @@ CREATE TABLE AM_API ( ) / +CREATE TABLE AM_API_SEQUENCE_BACKEND ( + ID VARCHAR2(60) NOT NULL, + API_UUID VARCHAR2(256) NOT NULL, + REVISION_UUID VARCHAR2(256) DEFAULT '0', + SEQUENCE BLOB NOT NULL, + NAME VARCHAR2(256) NOT NULL, + TYPE VARCHAR2(120) NOT NULL, + PRIMARY KEY (ID,API_UUID,REVISION_UUID,TYPE), + FOREIGN KEY (API_UUID) REFERENCES AM_API(API_UUID) ON DELETE CASCADE +) +/ + CREATE TABLE AM_GRAPHQL_COMPLEXITY ( UUID VARCHAR(256), API_ID INTEGER NOT NULL, diff --git a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/postgresql.sql b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/postgresql.sql index e07a424a2904..bfe82f068044 100644 --- a/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/postgresql.sql +++ b/features/apimgt/org.wso2.carbon.apimgt.core.feature/src/main/resources/sql/postgresql.sql @@ -1860,6 +1860,17 @@ CREATE TABLE IF NOT EXISTS AM_API ( UNIQUE (API_UUID) ); +CREATE TABLE IF NOT EXISTS AM_API_SEQUENCE_BACKEND ( + ID VARCHAR(60) NOT NULL, + API_UUID VARCHAR(256) NOT NULL, + REVISION_UUID VARCHAR(256) DEFAULT '0', + SEQUENCE BYTEA NOT NULL, + NAME VARCHAR(256) NOT NULL, + TYPE VARCHAR(120) NOT NULL, + PRIMARY KEY (ID,API_UUID,REVISION_UUID,TYPE), + FOREIGN KEY (API_UUID) REFERENCES AM_API(API_UUID) ON DELETE CASCADE +); + CREATE TABLE IF NOT EXISTS AM_GRAPHQL_COMPLEXITY ( UUID VARCHAR(256), API_ID INTEGER NOT NULL,