From df1b9e18fc99196c50fbc02757834abcbe5aa50d Mon Sep 17 00:00:00 2001 From: praminda Date: Tue, 18 Jan 2022 14:29:30 +0530 Subject: [PATCH 1/9] adp: msg: Allow default logic after event loop There can be logic later after this gateway label loop. Loop is there to iterate the labels in the event. It's not ideal to return from here as non of previous return statements mean event handling is completed if there are logic after the loop. --- adapter/internal/messaging/notification_listener.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adapter/internal/messaging/notification_listener.go b/adapter/internal/messaging/notification_listener.go index 0311dec1cd..2ed2e18f1b 100644 --- a/adapter/internal/messaging/notification_listener.go +++ b/adapter/internal/messaging/notification_listener.go @@ -169,7 +169,7 @@ func handleAPIEvents(data []byte, eventType string) { for _, env := range apiEvent.GatewayLabels { if isLaterEvent(apiListTimeStampMap, apiEvent.UUID+":"+env, currentTimeStamp) { - return + break } // removeFromGateway event with multiple labels could only appear when the API is subjected // to delete. Hence we could simply delete after checking against just one iteration. @@ -181,7 +181,7 @@ func handleAPIEvents(data []byte, eventType string) { xds.UpdateEnforcerAPIList(env, xdsAPIList) } } - return + break } if strings.EqualFold(deployAPIToGateway, apiEvent.Event.Type) { conf, _ := config.ReadConfigs() From badda1a0a0213d1ad524b7208ebaab99b53f157f Mon Sep 17 00:00:00 2001 From: praminda Date: Sun, 6 Feb 2022 15:28:22 +0530 Subject: [PATCH 2/9] adp: Rename route related funcs to match func task --- .../envoyconf/envoyconf_internal_test.go | 4 ++-- .../envoyconf/routes_with_clusters.go | 18 +++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/adapter/internal/oasparser/envoyconf/envoyconf_internal_test.go b/adapter/internal/oasparser/envoyconf/envoyconf_internal_test.go index ec56208f31..59822d4d7f 100644 --- a/adapter/internal/oasparser/envoyconf/envoyconf_internal_test.go +++ b/adapter/internal/oasparser/envoyconf/envoyconf_internal_test.go @@ -46,7 +46,7 @@ func TestGenerateRoutePaths(t *testing.T) { basePath := "/basePath" resourcePath := "/resource" - completeRoutePath := generateRoutePaths(xWso2BasePath, basePath, resourcePath) + completeRoutePath := generateRoutePath(xWso2BasePath, basePath, resourcePath) // TODO: (VirajSalaka) check if it is possible to perform an equals operation instead of prefix if !strings.HasPrefix(completeRoutePath, "^/xWso2BasePath/resource") { t.Error("The generated path should contain xWso2BasePath as a prefix if xWso2Basepath is available.") @@ -58,7 +58,7 @@ func TestGenerateRoutePaths(t *testing.T) { } xWso2BasePath = "" - completeRoutePath = generateRoutePaths(xWso2BasePath, basePath, resourcePath) + completeRoutePath = generateRoutePath(xWso2BasePath, basePath, resourcePath) if !strings.HasPrefix(completeRoutePath, "^/basePath/resource") { t.Error("The generated path should contain basePath as a prefix if xWso2Basepath is unavailable.") } diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go index 13822df9bc..452d156fd6 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go @@ -662,9 +662,6 @@ func createTLSProtocolVersion(tlsVersion string) tlsv3.TlsParameters_TlsProtocol // endpoint's basePath, resource Object (Microgateway's internal representation), production clusterName and // sandbox clusterName needs to be provided. func createRoute(params *routeCreateParams) *routev3.Route { - // func createRoute(title string, apiType string, xWso2Basepath string, version string, endpointBasepath string, - // resourcePathParam string, resourceMethods []string, prodClusterName string, sandClusterName string, - // corsPolicy *routev3.CorsPolicy) *routev3.Route { title := params.title version := params.version vHost := params.vHost @@ -691,7 +688,7 @@ func createRoute(params *routeCreateParams) *routev3.Route { responseHeadersToRemove []string ) - routePath := generateRoutePaths(xWso2Basepath, endpointBasepath, resourcePath) + routePath := generateRoutePath(xWso2Basepath, endpointBasepath, resourcePath) match = &routev3.RouteMatch{ PathSpecifier: &routev3.RouteMatch_SafeRegex{ @@ -708,7 +705,7 @@ func createRoute(params *routeCreateParams) *routev3.Route { // if any of the operations on the route path has a method rewrite policy, // we remove the :method header matching, - // because envoy does not allow method rewrting later if the following method regex doesnot have the new method. + // because envoy does not allow method rewrting later if the following method regex does not have the new method. // hence when method rewriting is enabled for the resource, the method validation will be handled by the enforcer instead of the router. if !params.rewriteMethod { // OPTIONS is always added even if it is not listed under resources @@ -1138,15 +1135,14 @@ func CreateReadyEndpoint() *routev3.Route { return &router } -// generateRoutePaths generates route paths for the api resources. -func generateRoutePaths(xWso2Basepath, basePath, resourcePath string) string { +// generateRoutePath generates route paths for the api resources. +func generateRoutePath(xWso2Basepath, basePath, resourcePath string) string { prefix := "" newPath := "" if strings.TrimSpace(xWso2Basepath) != "" { - prefix = basepathConsistent(xWso2Basepath) - + prefix = getFilteredBasePath(xWso2Basepath) } else { - prefix = basepathConsistent(basePath) + prefix = getFilteredBasePath(basePath) // TODO: (VirajSalaka) Decide if it is possible to proceed without both basepath options } if strings.Contains(resourcePath, "?") { @@ -1157,7 +1153,7 @@ func generateRoutePaths(xWso2Basepath, basePath, resourcePath string) string { return newPath } -func basepathConsistent(basePath string) string { +func getFilteredBasePath(basePath string) string { modifiedBasePath := basePath if !strings.HasPrefix(basePath, "/") { modifiedBasePath = "/" + modifiedBasePath From 1cad5ec4cce00b4ea03351c77a9e24d7ab56f388 Mon Sep 17 00:00:00 2001 From: praminda Date: Mon, 14 Feb 2022 10:43:50 +0530 Subject: [PATCH 3/9] adp: Support default versioned APIs Regex will make the API listen for both `basePathWithVersion` and `basePath`. With this approach we dont need to duplicate the resources for both basePaths --- .../envoyconf/envoyconf_internal_test.go | 11 ++--- .../oasparser/envoyconf/internal_dtos.go | 1 + .../envoyconf/routes_with_clusters.go | 40 ++++++++++++------- adapter/internal/oasparser/model/api_yaml.go | 1 + .../internal/oasparser/model/mgw_swagger.go | 2 + 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/adapter/internal/oasparser/envoyconf/envoyconf_internal_test.go b/adapter/internal/oasparser/envoyconf/envoyconf_internal_test.go index 59822d4d7f..ee0a45848c 100644 --- a/adapter/internal/oasparser/envoyconf/envoyconf_internal_test.go +++ b/adapter/internal/oasparser/envoyconf/envoyconf_internal_test.go @@ -46,19 +46,16 @@ func TestGenerateRoutePaths(t *testing.T) { basePath := "/basePath" resourcePath := "/resource" - completeRoutePath := generateRoutePath(xWso2BasePath, basePath, resourcePath) + filteredBasePath := getFilteredBasePath(xWso2BasePath, basePath) + completeRoutePath := generateRoutePath(filteredBasePath, resourcePath) // TODO: (VirajSalaka) check if it is possible to perform an equals operation instead of prefix if !strings.HasPrefix(completeRoutePath, "^/xWso2BasePath/resource") { t.Error("The generated path should contain xWso2BasePath as a prefix if xWso2Basepath is available.") } - xWso2BasePath = "/xWso2BasePath/" - if !strings.HasPrefix(completeRoutePath, "^/xWso2BasePath/resource") { - t.Error("The generated path should not contain the trailing '\\' of xWso2BasePath property within the generated route path.") - } - xWso2BasePath = "" - completeRoutePath = generateRoutePath(xWso2BasePath, basePath, resourcePath) + filteredBasePath = getFilteredBasePath(xWso2BasePath, basePath) + completeRoutePath = generateRoutePath(filteredBasePath, resourcePath) if !strings.HasPrefix(completeRoutePath, "^/basePath/resource") { t.Error("The generated path should contain basePath as a prefix if xWso2Basepath is unavailable.") } diff --git a/adapter/internal/oasparser/envoyconf/internal_dtos.go b/adapter/internal/oasparser/envoyconf/internal_dtos.go index de2ddc06c7..cdd38a3284 100644 --- a/adapter/internal/oasparser/envoyconf/internal_dtos.go +++ b/adapter/internal/oasparser/envoyconf/internal_dtos.go @@ -43,4 +43,5 @@ type routeCreateParams struct { rewritePath string rewriteMethod bool passRequestPayloadToEnforcer bool + isDefaultVersion bool } diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go index 452d156fd6..daf3069141 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go @@ -677,6 +677,7 @@ func createRoute(params *routeCreateParams) *routev3.Route { endpointBasepath := params.endpointBasePath requestInterceptor := params.requestInterceptor responseInterceptor := params.responseInterceptor + isDefaultVersion := params.isDefaultVersion config, _ := config.ReadConfigs() logger.LoggerOasparser.Debug("creating a route....") @@ -688,7 +689,11 @@ func createRoute(params *routeCreateParams) *routev3.Route { responseHeadersToRemove []string ) - routePath := generateRoutePath(xWso2Basepath, endpointBasepath, resourcePath) + basePath := getFilteredBasePath(xWso2Basepath, endpointBasepath) + if isDefaultVersion { + basePath = getDefaultVersionBasepath(basePath, version) + } + routePath := generateRoutePath(basePath, resourcePath) match = &routev3.RouteMatch{ PathSpecifier: &routev3.RouteMatch_SafeRegex{ @@ -824,7 +829,7 @@ func createRoute(params *routeCreateParams) *routev3.Route { Value: luaMarshelled.Bytes(), } - pathRegex := xWso2Basepath + pathRegex := basePath substitutionString := endpointBasepath if params.rewritePath != "" { pathRegex = routePath @@ -1136,26 +1141,27 @@ func CreateReadyEndpoint() *routev3.Route { } // generateRoutePath generates route paths for the api resources. -func generateRoutePath(xWso2Basepath, basePath, resourcePath string) string { - prefix := "" +func generateRoutePath(basePath, resourcePath string) string { newPath := "" - if strings.TrimSpace(xWso2Basepath) != "" { - prefix = getFilteredBasePath(xWso2Basepath) - } else { - prefix = getFilteredBasePath(basePath) - // TODO: (VirajSalaka) Decide if it is possible to proceed without both basepath options - } if strings.Contains(resourcePath, "?") { resourcePath = strings.Split(resourcePath, "?")[0] } - fullpath := prefix + resourcePath + fullpath := basePath + resourcePath newPath = generateRegex(fullpath) return newPath } -func getFilteredBasePath(basePath string) string { - modifiedBasePath := basePath - if !strings.HasPrefix(basePath, "/") { +func getFilteredBasePath(xWso2Basepath string, basePath string) string { + var modifiedBasePath string + + if strings.TrimSpace(xWso2Basepath) != "" { + modifiedBasePath = xWso2Basepath + } else { + modifiedBasePath = basePath + // TODO: (VirajSalaka) Decide if it is possible to proceed without both basepath options + } + + if !strings.HasPrefix(modifiedBasePath, "/") { modifiedBasePath = "/" + modifiedBasePath } modifiedBasePath = strings.TrimSuffix(modifiedBasePath, "/") @@ -1267,6 +1273,7 @@ func genRouteCreateParams(swagger *model.MgwSwagger, resource *model.Resource, v rewritePath: "", rewriteMethod: false, passRequestPayloadToEnforcer: swagger.GetXWso2RequestBodyPass(), + isDefaultVersion: swagger.IsDefaultVersion, } if resource != nil { @@ -1318,3 +1325,8 @@ func getDefaultResourceMethods(apiType string) []string { } return defaultResourceMethods } + +func getDefaultVersionBasepath(basePath string, version string) string { + context := strings.ReplaceAll(basePath, "/"+version, "") + return "(" + basePath + "|" + context + ")" +} diff --git a/adapter/internal/oasparser/model/api_yaml.go b/adapter/internal/oasparser/model/api_yaml.go index 80991cfb2b..4456cfef7f 100644 --- a/adapter/internal/oasparser/model/api_yaml.go +++ b/adapter/internal/oasparser/model/api_yaml.go @@ -46,6 +46,7 @@ type APIYaml struct { AuthorizationHeader string `json:"authorizationHeader,omitempty"` SecurityScheme []string `json:"securityScheme,omitempty"` OrganizationID string `json:"organizationId,omitempty"` + IsDefaultVersion bool `json:"isDefaultVersion,omitempty"` EndpointConfig struct { EndpointType string `json:"endpoint_type,omitempty"` LoadBalanceAlgo string `json:"algoCombo,omitempty"` diff --git a/adapter/internal/oasparser/model/mgw_swagger.go b/adapter/internal/oasparser/model/mgw_swagger.go index 0e1623e5a5..2d656dfbc3 100644 --- a/adapter/internal/oasparser/model/mgw_swagger.go +++ b/adapter/internal/oasparser/model/mgw_swagger.go @@ -68,6 +68,7 @@ type MgwSwagger struct { EndpointImplementationType string LifecycleStatus string xWso2RequestBodyPass bool + IsDefaultVersion bool } // EndpointCluster represent an upstream cluster @@ -1134,6 +1135,7 @@ func (swagger *MgwSwagger) PopulateFromAPIYaml(apiYaml APIYaml) error { // context value in api.yaml is assigned as xWso2Basepath swagger.xWso2Basepath = data.Context + "/" + swagger.version swagger.LifecycleStatus = data.LifeCycleStatus + swagger.IsDefaultVersion = data.IsDefaultVersion // productionURL & sandBoxURL values are extracted from endpointConfig in api.yaml endpointConfig := data.EndpointConfig From a6332191ac316b8b3070ea5ac71ef49da72743d6 Mon Sep 17 00:00:00 2001 From: praminda Date: Mon, 28 Feb 2022 14:53:11 +0530 Subject: [PATCH 4/9] adp: asb: Update debug message --- adapter/pkg/messaging/azure_listener.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adapter/pkg/messaging/azure_listener.go b/adapter/pkg/messaging/azure_listener.go index 72b9eb6cd6..da7b1b2633 100644 --- a/adapter/pkg/messaging/azure_listener.go +++ b/adapter/pkg/messaging/azure_listener.go @@ -79,9 +79,9 @@ func startBrokerConsumer(connectionString string, sub Subscription, reconnectInt logger.LoggerMsg.Errorf("Failed to parse the ASB message. %v", err) } - logger.LoggerMsg.Debugf("Message %s from ASB waits to be processed.", message.MessageID) + logger.LoggerMsg.Debugf("Message %s from ASB is waiting to be processed.", message.MessageID) dataChannel <- body - logger.LoggerMsg.Debugf("Message %s from ASB is complete", message.MessageID) + logger.LoggerMsg.Debugf("Message %s from ASB is processed", message.MessageID) err = receiver.CompleteMessage(ctx, message) if err != nil { From a0488ccb79b99b5154b9262f608b88a50b9b8ba4 Mon Sep 17 00:00:00 2001 From: praminda Date: Mon, 28 Feb 2022 14:59:57 +0530 Subject: [PATCH 5/9] adp: defversion: Support default version with CP default version change event is coming as an API_UPDATE type event from CP. This event doesn't contain ebough information for CC to figureout if it should redeploy the API. Hence the `Action` property is introduced to API_UPDATE event. API_UPDATE event doesn't contain the gatewayLabels property, therefore we are using intenal maps to find the deployed envs of an API when default version change event is received. --- adapter/internal/api/apis_impl.go | 5 ++- adapter/internal/discovery/xds/server.go | 12 +++++ adapter/internal/eventhub/subscription.go | 10 +++++ .../messaging/notification_listener.go | 44 +++++++++++++++++-- adapter/pkg/messaging/event_types.go | 1 + 5 files changed, 67 insertions(+), 5 deletions(-) diff --git a/adapter/internal/api/apis_impl.go b/adapter/internal/api/apis_impl.go index 3e80657a67..efd11ae080 100644 --- a/adapter/internal/api/apis_impl.go +++ b/adapter/internal/api/apis_impl.go @@ -260,7 +260,7 @@ func ApplyAPIProjectFromAPIM( if err != nil { return nil, err } - apiYaml := apiProject.APIYaml.Data + apiYaml := &apiProject.APIYaml.Data if apiEnvProps, found := apiEnvs[apiProject.APIYaml.Data.ID]; found { loggers.LoggerAPI.Infof("Environment specific values found for the API %v ", apiProject.APIYaml.Data.ID) apiProject.APIEnvProps = apiEnvProps @@ -298,6 +298,9 @@ func ApplyAPIProjectFromAPIM( allEnvironments := xds.GetAllEnvironments(apiYaml.ID, vhost, environments) loggers.LoggerAPI.Debugf("Update all environments (%v) of API %v %v:%v with UUID \"%v\".", allEnvironments, vhost, apiYaml.Name, apiYaml.Version, apiYaml.ID) + // We don't need to be environment specific when checking default version. It's applied at API level + // hence picking 0th index here. + apiYaml.IsDefaultVersion = xds.APIListMap[allEnvironments[0]][apiYaml.ID].IsDefaultVersion // first update the API for vhost deployedRevision, err := xds.UpdateAPI(vhost, apiProject, allEnvironments) if err != nil { diff --git a/adapter/internal/discovery/xds/server.go b/adapter/internal/discovery/xds/server.go index b364a87b14..df1fd72f38 100644 --- a/adapter/internal/discovery/xds/server.go +++ b/adapter/internal/discovery/xds/server.go @@ -482,6 +482,18 @@ func GetAllEnvironments(apiUUID, vhost string, newEnvironments []string) []strin return allEnvironments } +// GetDeployedEnvironmets returns all the environments the API with `apiUUID` is deployed to +func GetDeployedEnvironmets(apiUUID string) []string { + var envs []string + if envMap, ok := apiUUIDToGatewayToVhosts[apiUUID]; ok { + envs = make([]string, 0, len(envMap)) + for k := range envMap { + envs = append(envs, k) + } + } + return envs +} + // GetVhostOfAPI returns the vhost of API deployed in the given gateway environment func GetVhostOfAPI(apiUUID, environment string) (vhost string, exists bool) { if envToVhost, ok := apiUUIDToGatewayToVhosts[apiUUID]; ok { diff --git a/adapter/internal/eventhub/subscription.go b/adapter/internal/eventhub/subscription.go index 16dc345362..c4c50c6510 100644 --- a/adapter/internal/eventhub/subscription.go +++ b/adapter/internal/eventhub/subscription.go @@ -205,6 +205,16 @@ func LoadSubscriptionData(configFile *config.Config, initialAPIUUIDListMap map[s go retrieveAPIListFromChannel(APIListChannel, nil) } +// UpdatAPIMetadataFromCP Invokes `ApisEndpoint` and updates APIList synchronously. +func UpdatAPIMetadataFromCP(params map[string]string) { + var apiList *types.APIList + var responseChannel = make(chan response) + go InvokeService(ApisEndpoint, apiList, params, responseChannel, 0) + // TODO: (Praminda) - discuss why we wait in a loop in other places? + response := <-responseChannel + retrieveAPIList(response, nil) +} + // InvokeService invokes the internal data resource func InvokeService(endpoint string, responseType interface{}, queryParamMap map[string]string, c chan response, retryAttempt int) { diff --git a/adapter/internal/messaging/notification_listener.go b/adapter/internal/messaging/notification_listener.go index 2ed2e18f1b..410e0c9bd5 100644 --- a/adapter/internal/messaging/notification_listener.go +++ b/adapter/internal/messaging/notification_listener.go @@ -56,6 +56,7 @@ const ( policyUpdate = "POLICY_UPDATE" policyDelete = "POLICY_DELETE" blockedStatus = "BLOCKED" + apiUpdate = "API_UPDATE" ) // var variables @@ -131,11 +132,36 @@ func processNotificationEvent(conf *config.Config, notification *msg.EventNotifi return nil } +func handleDefaultVersionUpdate(event msg.APIEvent) { + deployedEnvs := xds.GetDeployedEnvironmets(event.UUID) + conf, _ := config.ReadConfigs() + configuredEnvs := conf.ControlPlane.EnvironmentLabels + + if len(configuredEnvs) == 0 { + configuredEnvs = append(configuredEnvs, config.DefaultGatewayName) + } + for _, env := range deployedEnvs { + // TODO: (Praminda) - This loop is not required here since we are anyway looping the API's already deployed envs + for _, configuredEnv := range configuredEnvs { + if configuredEnv == env { + query := make(map[string]string, 3) + query[eh.GatewayLabelParam] = configuredEnv + query[eh.ContextParam] = event.APIContext + query[eh.VersionParam] = event.APIVersion + eh.UpdatAPIMetadataFromCP(query) + } + } + } + + synchronizer.FetchAPIsFromControlPlane(event.UUID, deployedEnvs) +} + // handleAPIEvents to process api related data func handleAPIEvents(data []byte, eventType string) { var ( - apiEvent msg.APIEvent - currentTimeStamp int64 = apiEvent.Event.TimeStamp + apiEvent msg.APIEvent + currentTimeStamp int64 = apiEvent.Event.TimeStamp + isDefaultVersionEvent bool ) apiEventErr := json.Unmarshal([]byte(string(data)), &apiEvent) @@ -162,6 +188,13 @@ func handleAPIEvents(data []byte, eventType string) { return } + isDefaultVersionEvent = isDefaultVersionUpdate(apiEvent) + + if isDefaultVersionEvent { + handleDefaultVersionUpdate(apiEvent) + return + } + // Per each revision, synchronization should happen. if strings.EqualFold(deployAPIToGateway, apiEvent.Event.Type) { go synchronizer.FetchAPIsFromControlPlane(apiEvent.UUID, apiEvent.GatewayLabels) @@ -200,8 +233,7 @@ func handleAPIEvents(data []byte, eventType string) { queryParamMap[eh.ContextParam] = apiEvent.Context queryParamMap[eh.VersionParam] = apiEvent.Version var apiList *types.APIList - go eh.InvokeService(eh.ApisEndpoint, apiList, queryParamMap, - eh.APIListChannel, 0) + go eh.InvokeService(eh.ApisEndpoint, apiList, queryParamMap, eh.APIListChannel, 0) } } } @@ -423,6 +455,10 @@ func isLaterEvent(timeStampMap map[string]int64, mapKey string, currentTimeStamp return false } +func isDefaultVersionUpdate(event msg.APIEvent) bool { + return strings.EqualFold(apiUpdate, event.Event.Type) && strings.EqualFold("DEFAULT_VERSION", event.Action) +} + func belongsToTenant(tenantDomain string) bool { // TODO : enable this once the events are fixed in apim // return config.GetControlPlaneConnectedTenantDomain() == tenantDomain diff --git a/adapter/pkg/messaging/event_types.go b/adapter/pkg/messaging/event_types.go index fbebad681f..44b8216076 100644 --- a/adapter/pkg/messaging/event_types.go +++ b/adapter/pkg/messaging/event_types.go @@ -118,6 +118,7 @@ type APIEvent struct { Version string `json:"version"` Context string `json:"context"` Name string `json:"name"` + Action string `json:"action"` } // ApplicationRegistrationEvent for struct application registration events From 3e1edd39096ac078e36196b5e044b3151391798e Mon Sep 17 00:00:00 2001 From: praminda Date: Wed, 2 Mar 2022 22:23:11 +0530 Subject: [PATCH 6/9] defversion: test: Add default version tests --- .../apiDeploy/DefaultVersionApiTestCase.java | 108 ++++++++++++++++++ .../resources/apiYaml/default_version_v1.yaml | 24 ++++ .../resources/apiYaml/default_version_v2.yaml | 24 ++++ .../openAPIs/default_version_v1_OpenAPI.yaml | 82 +++++++++++++ .../openAPIs/default_version_v2_OpenAPI.yaml | 95 +++++++++++++++ .../test/resources/testng-cc-standalone.xml | 1 + 6 files changed, 334 insertions(+) create mode 100644 integration/test-integration/src/test/java/org/wso2/choreo/connect/tests/testcases/standalone/apiDeploy/DefaultVersionApiTestCase.java create mode 100644 integration/test-integration/src/test/resources/apiYaml/default_version_v1.yaml create mode 100644 integration/test-integration/src/test/resources/apiYaml/default_version_v2.yaml create mode 100644 integration/test-integration/src/test/resources/openAPIs/default_version_v1_OpenAPI.yaml create mode 100644 integration/test-integration/src/test/resources/openAPIs/default_version_v2_OpenAPI.yaml diff --git a/integration/test-integration/src/test/java/org/wso2/choreo/connect/tests/testcases/standalone/apiDeploy/DefaultVersionApiTestCase.java b/integration/test-integration/src/test/java/org/wso2/choreo/connect/tests/testcases/standalone/apiDeploy/DefaultVersionApiTestCase.java new file mode 100644 index 0000000000..f3a92f5672 --- /dev/null +++ b/integration/test-integration/src/test/java/org/wso2/choreo/connect/tests/testcases/standalone/apiDeploy/DefaultVersionApiTestCase.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org). + * 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.choreo.connect.tests.testcases.standalone.apiDeploy; + +import org.apache.http.HttpStatus; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.wso2.choreo.connect.tests.common.model.API; +import org.wso2.choreo.connect.tests.common.model.ApplicationDTO; +import org.wso2.choreo.connect.tests.context.CCTestException; +import org.wso2.choreo.connect.tests.util.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * Test default versioned APIs. + */ +public class DefaultVersionApiTestCase { + private String testKeyV1, testKeyV2; + private final String basePath = "/defaultVersion/"; + private final String v1 = "1.0.0"; + private final String v2 = "2.0.0"; + private final String v1Context = basePath + v1; + private final String v2Context = basePath + v2; + + @BeforeClass + public void deployAPIs() throws Exception { + ApictlUtils.login("test"); + ApictlUtils.createProject("default_version_v1_OpenAPI.yaml", "default_version_v1", null, null, null, + "default_version_v1.yaml"); + ApictlUtils.createProject("default_version_v2_OpenAPI.yaml", "default_version_v2", null, null, null, + "default_version_v2.yaml"); + + ApictlUtils.deployAPI("default_version_v1", "test"); + ApictlUtils.deployAPI("default_version_v2", "test"); + + Utils.delay(TestConstant.DEPLOYMENT_WAIT_TIME, "Couldn't wait till deployment completion."); + + API api = new API(); + api.setName("DefaultVersion"); + api.setContext(v1Context); + api.setVersion(v1); + api.setProvider("admin"); + + ApplicationDTO app = new ApplicationDTO(); + app.setName("jwtApp"); + app.setTier("Unlimited"); + app.setId((int) (Math.random() * 1000)); + + testKeyV1 = TokenUtil.getJWT(api, app, "Unlimited", TestConstant.KEY_TYPE_PRODUCTION, 3600, null, true); + api.setContext(v2Context); + api.setVersion(v2); + testKeyV2 = TokenUtil.getJWT(api, app, "Unlimited", TestConstant.KEY_TYPE_PRODUCTION, 3600, null, true); + } + + @Test(description = "Test invoking default versioned API without version in the context") + public void testInvokingDefaultVersion() throws CCTestException { + Map headers = new HashMap<>(); + headers.put("Internal-Key", testKeyV2); + HttpResponse response = HttpsClientRequest.doGet("https://localhost:9095" + basePath + "store/inventory", headers); + Assert.assertNotNull(response); + Assert.assertEquals(response.getResponseCode(), HttpStatus.SC_OK, "Response code mismatched"); + Assert.assertTrue(response.getData().contains("233539"), "Response body mismatched"); + } + + @Test(description = "Test invoking default versioned API with version in the context") + public void testInvokingDefaultVersionWithVersion() throws CCTestException { + Map headers = new HashMap<>(); + headers.put("Internal-Key", testKeyV2); + HttpResponse response = HttpsClientRequest.doGet("https://localhost:9095" + v2Context + "/store/inventory", headers); + Assert.assertNotNull(response); + Assert.assertEquals(response.getResponseCode(), HttpStatus.SC_OK, "Response code mismatched"); + Assert.assertTrue(response.getData().contains("233539"), "Response body mismatched"); + } + + @Test(description = "Test invoking `non` default versioned API") + public void testInvokingNoneDefaultVersion() throws CCTestException { + Map headers = new HashMap<>(); + headers.put("Internal-Key", testKeyV1); + HttpResponse response = HttpsClientRequest.doGet("https://localhost:9095" + v1Context + "/pet/3", headers); + Assert.assertNotNull(response); + Assert.assertEquals(response.getResponseCode(), HttpStatus.SC_OK, "Response code mismatched"); + Assert.assertTrue(response.getData().contains("John Doe"), "Response body mismatched"); + + // V1 doesn't have /user/john resource. We should try to invoke it and see if the traffic is routing to the + // correct API + response = HttpsClientRequest.doGet("https://localhost:9095" + v1Context + "/store/inventory", headers); + Assert.assertNotNull(response); + Assert.assertEquals(response.getResponseCode(), HttpStatus.SC_NOT_FOUND, "Response code mismatched"); + } +} diff --git a/integration/test-integration/src/test/resources/apiYaml/default_version_v1.yaml b/integration/test-integration/src/test/resources/apiYaml/default_version_v1.yaml new file mode 100644 index 0000000000..60612858c7 --- /dev/null +++ b/integration/test-integration/src/test/resources/apiYaml/default_version_v1.yaml @@ -0,0 +1,24 @@ +type: api +version: v4.0.0 +data: + name: DefaultVersion + context: /defaultVersion/1.0.0 + version: 1.0.0 + provider: admin + lifeCycleStatus: PUBLISHED + isDefaultVersion: false + isRevision: false + revisionId: 0 + type: HTTP + transport: + - http + - https + policies: + - Unlimited + visibility: PUBLIC + endpointConfig: + endpoint_type: http + production_endpoints: + url: http://mockBackend:2383/v2 + endpointImplementationType: ENDPOINT + websubSubscriptionConfiguration: null diff --git a/integration/test-integration/src/test/resources/apiYaml/default_version_v2.yaml b/integration/test-integration/src/test/resources/apiYaml/default_version_v2.yaml new file mode 100644 index 0000000000..aaf58e6134 --- /dev/null +++ b/integration/test-integration/src/test/resources/apiYaml/default_version_v2.yaml @@ -0,0 +1,24 @@ +type: api +version: v4.0.0 +data: + name: DefaultVersion + context: /defaultVersion/2.0.0 + version: 2.0.0 + provider: admin + lifeCycleStatus: PUBLISHED + isDefaultVersion: true + isRevision: false + revisionId: 0 + type: HTTP + transport: + - http + - https + policies: + - Unlimited + visibility: PUBLIC + endpointConfig: + endpoint_type: http + production_endpoints: + url: http://mockBackend:2383/v2 + endpointImplementationType: ENDPOINT + websubSubscriptionConfiguration: null diff --git a/integration/test-integration/src/test/resources/openAPIs/default_version_v1_OpenAPI.yaml b/integration/test-integration/src/test/resources/openAPIs/default_version_v1_OpenAPI.yaml new file mode 100644 index 0000000000..ba9aa45d87 --- /dev/null +++ b/integration/test-integration/src/test/resources/openAPIs/default_version_v1_OpenAPI.yaml @@ -0,0 +1,82 @@ +swagger: '2.0' +info: + version: 1.0.0 + title: DefaultVersion +x-wso2-production-endpoints: + urls: + - 'http://mockBackend:2383/v2' +x-wso2-basePath: /defaultVersion/1.0.0 +schemes: + - http +paths: + '/pet/{petId}': + get: + produces: + - application/json + - application/xml + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + type: integer + format: int64 + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found +definitions: + Pet: + type: object + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + category: + $ref: '#/definitions/Category' + name: + type: string + example: doggie + photoUrls: + type: array + xml: + wrapped: true + items: + type: string + xml: + name: photoUrl + tags: + type: array + xml: + wrapped: true + items: + xml: + name: tag + $ref: '#/definitions/Tag' + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: Pet + Tag: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag diff --git a/integration/test-integration/src/test/resources/openAPIs/default_version_v2_OpenAPI.yaml b/integration/test-integration/src/test/resources/openAPIs/default_version_v2_OpenAPI.yaml new file mode 100644 index 0000000000..86efd8066e --- /dev/null +++ b/integration/test-integration/src/test/resources/openAPIs/default_version_v2_OpenAPI.yaml @@ -0,0 +1,95 @@ +swagger: '2.0' +info: + version: 2.0.0 + title: DefaultVersion +x-wso2-production-endpoints: + urls: + - 'http://mockBackend:2383/v2' +x-wso2-basePath: /defaultVersion/2.0.0 +schemes: + - http +paths: + /store/inventory: + get: + produces: + - application/json + parameters: [] + responses: + '200': + description: successful operation + schema: + type: object + additionalProperties: + type: integer + format: int32 + '/pet/{petId}': + get: + produces: + - application/json + - application/xml + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + type: integer + format: int64 + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found +definitions: + Pet: + type: object + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + category: + $ref: '#/definitions/Category' + name: + type: string + example: doggie + photoUrls: + type: array + xml: + wrapped: true + items: + type: string + xml: + name: photoUrl + tags: + type: array + xml: + wrapped: true + items: + xml: + name: tag + $ref: '#/definitions/Tag' + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: Pet + Tag: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag diff --git a/integration/test-integration/src/test/resources/testng-cc-standalone.xml b/integration/test-integration/src/test/resources/testng-cc-standalone.xml index 3b96f4758f..36a7faa2ed 100644 --- a/integration/test-integration/src/test/resources/testng-cc-standalone.xml +++ b/integration/test-integration/src/test/resources/testng-cc-standalone.xml @@ -55,6 +55,7 @@ + From f9ed4ed5d158ec70f0570b2ce2903560432b6e7b Mon Sep 17 00:00:00 2001 From: Praminda Date: Thu, 3 Mar 2022 17:16:58 +0530 Subject: [PATCH 7/9] Add review suggestions Co-authored-by: Renuka Piyumal Fernando --- adapter/internal/eventhub/subscription.go | 1 - .../messaging/notification_listener.go | 30 +++++++++---------- .../envoyconf/routes_with_clusters.go | 2 +- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/adapter/internal/eventhub/subscription.go b/adapter/internal/eventhub/subscription.go index c4c50c6510..f1bf8ca80e 100644 --- a/adapter/internal/eventhub/subscription.go +++ b/adapter/internal/eventhub/subscription.go @@ -210,7 +210,6 @@ func UpdatAPIMetadataFromCP(params map[string]string) { var apiList *types.APIList var responseChannel = make(chan response) go InvokeService(ApisEndpoint, apiList, params, responseChannel, 0) - // TODO: (Praminda) - discuss why we wait in a loop in other places? response := <-responseChannel retrieveAPIList(response, nil) } diff --git a/adapter/internal/messaging/notification_listener.go b/adapter/internal/messaging/notification_listener.go index 410e0c9bd5..1f47770e1c 100644 --- a/adapter/internal/messaging/notification_listener.go +++ b/adapter/internal/messaging/notification_listener.go @@ -132,25 +132,23 @@ func processNotificationEvent(conf *config.Config, notification *msg.EventNotifi return nil } +// handleDefaultVersionUpdate will redeploy default versioned API. +// API runtime artifact doesn't get updated in CP side when default version is updated +// (isDefaultVersion prop in apiYaml is not updated). API deployment or should happen +// for it to get updated. However we need to redeploy the API when there is a default +// version change. For that we call `/apis` endpoint to get updated API metadata (this +// contains the updated `isDefaultVersion` field). Now we proceed with fetching runtime +// artifact from the CP. When creating CC deployment objects we refer to updated `APIList` +// map and update runtime artifact's `isDefaultVersion` field to correctly deploy default +// versioned API. func handleDefaultVersionUpdate(event msg.APIEvent) { deployedEnvs := xds.GetDeployedEnvironmets(event.UUID) - conf, _ := config.ReadConfigs() - configuredEnvs := conf.ControlPlane.EnvironmentLabels - - if len(configuredEnvs) == 0 { - configuredEnvs = append(configuredEnvs, config.DefaultGatewayName) - } for _, env := range deployedEnvs { - // TODO: (Praminda) - This loop is not required here since we are anyway looping the API's already deployed envs - for _, configuredEnv := range configuredEnvs { - if configuredEnv == env { - query := make(map[string]string, 3) - query[eh.GatewayLabelParam] = configuredEnv - query[eh.ContextParam] = event.APIContext - query[eh.VersionParam] = event.APIVersion - eh.UpdatAPIMetadataFromCP(query) - } - } + query := make(map[string]string, 3) + query[eh.GatewayLabelParam] = env + query[eh.ContextParam] = event.APIContext + query[eh.VersionParam] = event.APIVersion + eh.UpdatAPIMetadataFromCP(query) } synchronizer.FetchAPIsFromControlPlane(event.UUID, deployedEnvs) diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go index daf3069141..6c1375e135 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go @@ -1328,5 +1328,5 @@ func getDefaultResourceMethods(apiType string) []string { func getDefaultVersionBasepath(basePath string, version string) string { context := strings.ReplaceAll(basePath, "/"+version, "") - return "(" + basePath + "|" + context + ")" + return fmt.Sprintf("(%s|%s)", basePath, context) } From 3bc9a1fb9631176306b96144d7790915b4cb3f65 Mon Sep 17 00:00:00 2001 From: praminda Date: Fri, 4 Mar 2022 12:13:40 +0530 Subject: [PATCH 8/9] adp: Fix integration test failure panic error was returned randomly when APIListMap was lookedup to get `isDefaultVersion` value. This can happen when this lookup happens during normal API deployment events. Reason is this path doesn't (and its not required to) synchronously update APIMeata before fetching runtime artifact. Since runtime artifact is anyway updated with `isDefaultVersion` when API deployement happens, It's not required to have APIMeta up to date before fetching runtime artifact. --- adapter/internal/api/apis_impl.go | 9 ++++++++- adapter/internal/messaging/notification_listener.go | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/adapter/internal/api/apis_impl.go b/adapter/internal/api/apis_impl.go index efd11ae080..e81611f8ea 100644 --- a/adapter/internal/api/apis_impl.go +++ b/adapter/internal/api/apis_impl.go @@ -300,7 +300,14 @@ func ApplyAPIProjectFromAPIM( allEnvironments, vhost, apiYaml.Name, apiYaml.Version, apiYaml.ID) // We don't need to be environment specific when checking default version. It's applied at API level // hence picking 0th index here. - apiYaml.IsDefaultVersion = xds.APIListMap[allEnvironments[0]][apiYaml.ID].IsDefaultVersion + if api, ok := xds.APIListMap[allEnvironments[0]][apiYaml.ID]; ok { + apiYaml.IsDefaultVersion = api.IsDefaultVersion + } else { + // APIListMap is synchronously updated only for default version changes. In other API deployment + // events, this may not be updated. We can safely ignore this case since runtime artifact's + // `isDefaultVersion` prop is anyway updated for deployment events. + loggers.LoggerAPI.Debugf("API %s is not found in API Metadata map.", apiYaml.ID) + } // first update the API for vhost deployedRevision, err := xds.UpdateAPI(vhost, apiProject, allEnvironments) if err != nil { diff --git a/adapter/internal/messaging/notification_listener.go b/adapter/internal/messaging/notification_listener.go index 1f47770e1c..685e0af46d 100644 --- a/adapter/internal/messaging/notification_listener.go +++ b/adapter/internal/messaging/notification_listener.go @@ -226,6 +226,7 @@ func handleAPIEvents(data []byte, eventType string) { logger.LoggerInternalMsg.Debugf("API Metadata for api Id: %s is not updated as it already exists", apiEvent.UUID) continue } + logger.LoggerInternalMsg.Debugf("Fetching Metadata for api Id: %s ", apiEvent.UUID) queryParamMap := make(map[string]string, 3) queryParamMap[eh.GatewayLabelParam] = configuredEnv queryParamMap[eh.ContextParam] = apiEvent.Context From b2bbe87e4120cc591f24b5000d5963e39275a896 Mon Sep 17 00:00:00 2001 From: praminda Date: Fri, 4 Mar 2022 13:40:02 +0530 Subject: [PATCH 9/9] adp: Add default version unit tests --- adapter/internal/discovery/xds/server.go | 4 +-- adapter/internal/eventhub/subscription.go | 4 +-- .../messaging/notification_listener.go | 4 +-- .../envoyconf/envoyconf_internal_test.go | 27 ++++++++++++------- .../oasparser/envoyconf/listener_test.go | 6 ++--- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/adapter/internal/discovery/xds/server.go b/adapter/internal/discovery/xds/server.go index df1fd72f38..9777485241 100644 --- a/adapter/internal/discovery/xds/server.go +++ b/adapter/internal/discovery/xds/server.go @@ -482,8 +482,8 @@ func GetAllEnvironments(apiUUID, vhost string, newEnvironments []string) []strin return allEnvironments } -// GetDeployedEnvironmets returns all the environments the API with `apiUUID` is deployed to -func GetDeployedEnvironmets(apiUUID string) []string { +// GetDeployedEnvironments returns all the environments the API with `apiUUID` is deployed to +func GetDeployedEnvironments(apiUUID string) []string { var envs []string if envMap, ok := apiUUIDToGatewayToVhosts[apiUUID]; ok { envs = make([]string, 0, len(envMap)) diff --git a/adapter/internal/eventhub/subscription.go b/adapter/internal/eventhub/subscription.go index f1bf8ca80e..4f241db1ac 100644 --- a/adapter/internal/eventhub/subscription.go +++ b/adapter/internal/eventhub/subscription.go @@ -205,8 +205,8 @@ func LoadSubscriptionData(configFile *config.Config, initialAPIUUIDListMap map[s go retrieveAPIListFromChannel(APIListChannel, nil) } -// UpdatAPIMetadataFromCP Invokes `ApisEndpoint` and updates APIList synchronously. -func UpdatAPIMetadataFromCP(params map[string]string) { +// UpdateAPIMetadataFromCP Invokes `ApisEndpoint` and updates APIList synchronously. +func UpdateAPIMetadataFromCP(params map[string]string) { var apiList *types.APIList var responseChannel = make(chan response) go InvokeService(ApisEndpoint, apiList, params, responseChannel, 0) diff --git a/adapter/internal/messaging/notification_listener.go b/adapter/internal/messaging/notification_listener.go index 685e0af46d..c999460e14 100644 --- a/adapter/internal/messaging/notification_listener.go +++ b/adapter/internal/messaging/notification_listener.go @@ -142,13 +142,13 @@ func processNotificationEvent(conf *config.Config, notification *msg.EventNotifi // map and update runtime artifact's `isDefaultVersion` field to correctly deploy default // versioned API. func handleDefaultVersionUpdate(event msg.APIEvent) { - deployedEnvs := xds.GetDeployedEnvironmets(event.UUID) + deployedEnvs := xds.GetDeployedEnvironments(event.UUID) for _, env := range deployedEnvs { query := make(map[string]string, 3) query[eh.GatewayLabelParam] = env query[eh.ContextParam] = event.APIContext query[eh.VersionParam] = event.APIVersion - eh.UpdatAPIMetadataFromCP(query) + eh.UpdateAPIMetadataFromCP(query) } synchronizer.FetchAPIsFromControlPlane(event.UUID, deployedEnvs) diff --git a/adapter/internal/oasparser/envoyconf/envoyconf_internal_test.go b/adapter/internal/oasparser/envoyconf/envoyconf_internal_test.go index ee0a45848c..52ef5b3575 100644 --- a/adapter/internal/oasparser/envoyconf/envoyconf_internal_test.go +++ b/adapter/internal/oasparser/envoyconf/envoyconf_internal_test.go @@ -18,6 +18,7 @@ package envoyconf import ( + "fmt" "io/ioutil" "regexp" "strings" @@ -133,7 +134,7 @@ func TestCreateRoute(t *testing.T) { } generatedRouteWithXWso2BasePath := createRoute(generateRouteCreateParamsForUnitTests(title, apiType, vHost, xWso2BasePath, version, - endpoint.Basepath, resourceWithGet.GetPath(), resourceWithGet.GetMethodList(), clusterName, "", nil)) + endpoint.Basepath, resourceWithGet.GetPath(), resourceWithGet.GetMethodList(), clusterName, "", nil, false)) assert.NotNil(t, generatedRouteWithXWso2BasePath, "Route should not be null.") assert.Equal(t, expectedRouteActionWithXWso2BasePath, generatedRouteWithXWso2BasePath.Action, "Route generation mismatch when xWso2BasePath option is provided.") @@ -142,7 +143,7 @@ func TestCreateRoute(t *testing.T) { "Assigned HTTP Method Regex is incorrect when single method is available.") generatedRouteWithoutXWso2BasePath := createRoute(generateRouteCreateParamsForUnitTests(title, apiType, vHost, "", version, - endpoint.Basepath, resourceWithGetPost.GetPath(), resourceWithGetPost.GetMethodList(), clusterName, "", nil)) + endpoint.Basepath, resourceWithGetPost.GetPath(), resourceWithGetPost.GetMethodList(), clusterName, "", nil, false)) assert.NotNil(t, generatedRouteWithoutXWso2BasePath, "Route should not be null") assert.NotNil(t, generatedRouteWithoutXWso2BasePath.GetMatch().Headers, "Headers property should not be null") assert.Equal(t, "^(GET|POST|OPTIONS)$", generatedRouteWithoutXWso2BasePath.GetMatch().Headers[0].GetStringMatch().GetSafeRegex().Regex, @@ -151,6 +152,13 @@ func TestCreateRoute(t *testing.T) { assert.Equal(t, expectedRouteActionWithoutXWso2BasePath, generatedRouteWithoutXWso2BasePath.Action, "Route generation mismatch when xWso2BasePath option is provided") + context := fmt.Sprintf("%s/%s", xWso2BasePath, version) + generatedRouteWithDefaultVersion := createRoute(generateRouteCreateParamsForUnitTests(title, apiType, vHost, context, version, + endpoint.Basepath, resourceWithGetPost.GetPath(), resourceWithGetPost.GetMethodList(), clusterName, "", nil, true)) + assert.NotNil(t, generatedRouteWithDefaultVersion, "Route should not be null") + assert.True(t, strings.HasPrefix(generatedRouteWithDefaultVersion.GetMatch().GetSafeRegex().Regex, fmt.Sprintf("^(%s|%s)", context, xWso2BasePath)), + "Default version basepath is not generated correctly") + } func TestCreateRouteClusterSpecifier(t *testing.T) { @@ -172,21 +180,21 @@ func TestCreateRouteClusterSpecifier(t *testing.T) { "resource_operation_id", []model.Endpoint{}, []model.Endpoint{}) routeWithProdEp := createRoute(generateRouteCreateParamsForUnitTests(title, apiType, vHost, xWso2BasePath, version, endpointBasePath, - resourceWithGet.GetPath(), resourceWithGet.GetMethodList(), prodClusterName, "", nil)) + resourceWithGet.GetPath(), resourceWithGet.GetMethodList(), prodClusterName, "", nil, false)) assert.NotNil(t, routeWithProdEp, "Route should not be null") assert.NotNil(t, routeWithProdEp.GetRoute().GetClusterHeader(), "Route Cluster Header should not be null.") assert.Empty(t, routeWithProdEp.GetRoute().GetCluster(), "Route Cluster Name should be empty.") assert.Equal(t, clusterHeaderName, routeWithProdEp.GetRoute().GetClusterHeader(), "Route Cluster Name mismatch.") routeWithSandEp := createRoute(generateRouteCreateParamsForUnitTests(title, apiType, vHost, xWso2BasePath, version, endpointBasePath, - resourceWithGet.GetPath(), resourceWithGet.GetMethodList(), "", sandClusterName, nil)) + resourceWithGet.GetPath(), resourceWithGet.GetMethodList(), "", sandClusterName, nil, false)) assert.NotNil(t, routeWithSandEp, "Route should not be null") assert.NotNil(t, routeWithSandEp.GetRoute().GetClusterHeader(), "Route Cluster Header should not be null.") assert.Empty(t, routeWithSandEp.GetRoute().GetCluster(), "Route Cluster Name should be empty.") assert.Equal(t, clusterHeaderName, routeWithSandEp.GetRoute().GetClusterHeader(), "Route Cluster Name mismatch.") routeWithProdSandEp := createRoute(generateRouteCreateParamsForUnitTests(title, apiType, vHost, xWso2BasePath, version, endpointBasePath, - resourceWithGet.GetPath(), resourceWithGet.GetMethodList(), prodClusterName, sandClusterName, nil)) + resourceWithGet.GetPath(), resourceWithGet.GetMethodList(), prodClusterName, sandClusterName, nil, false)) assert.NotNil(t, routeWithProdSandEp, "Route should not be null") assert.NotNil(t, routeWithProdSandEp.GetRoute().GetClusterHeader(), "Route Cluster Header should not be null.") assert.Empty(t, routeWithProdSandEp.GetRoute().GetCluster(), "Route Cluster Name should be empty.") @@ -211,7 +219,7 @@ func TestCreateRouteExtAuthzContext(t *testing.T) { "resource_operation_id", []model.Endpoint{}, []model.Endpoint{}) routeWithProdEp := createRoute(generateRouteCreateParamsForUnitTests(title, apiType, vHost, xWso2BasePath, version, - endpointBasePath, resourceWithGet.GetPath(), resourceWithGet.GetMethodList(), prodClusterName, sandClusterName, nil)) + endpointBasePath, resourceWithGet.GetPath(), resourceWithGet.GetMethodList(), prodClusterName, sandClusterName, nil, false)) assert.NotNil(t, routeWithProdEp, "Route should not be null") assert.NotNil(t, routeWithProdEp.GetTypedPerFilterConfig(), "TypedPerFilter config should not be null") assert.NotNil(t, routeWithProdEp.GetTypedPerFilterConfig()[wellknown.HTTPExternalAuthorization], @@ -484,18 +492,18 @@ func TestGetCorsPolicy(t *testing.T) { // Route without CORS configuration routeWithoutCors := createRoute(generateRouteCreateParamsForUnitTests("test", "HTTP", "localhost", "/test", "1.0.0", "/test", - "/testPath", []string{"GET"}, "test-cluster", "", nil)) + "/testPath", []string{"GET"}, "test-cluster", "", nil, false)) assert.Nil(t, routeWithoutCors.GetRoute().Cors, "Cors Configuration should be null.") // Route with CORS configuration routeWithCors := createRoute(generateRouteCreateParamsForUnitTests("test", "HTTP", "localhost", "/test", "1.0.0", "/test", - "/testPath", []string{"GET"}, "test-cluster", "", corsConfigModel3)) + "/testPath", []string{"GET"}, "test-cluster", "", corsConfigModel3, false)) assert.NotNil(t, routeWithCors.GetRoute().Cors, "Cors Configuration should not be null.") } func generateRouteCreateParamsForUnitTests(title string, apiType string, vhost string, xWso2Basepath string, version string, endpointBasepath string, resourcePathParam string, resourceMethods []string, prodClusterName string, sandClusterName string, - corsConfig *model.CorsConfig) *routeCreateParams { + corsConfig *model.CorsConfig, isDefaultVersion bool) *routeCreateParams { return &routeCreateParams{ title: title, apiType: apiType, @@ -508,5 +516,6 @@ func generateRouteCreateParamsForUnitTests(title string, apiType string, vhost s corsPolicy: corsConfig, resourcePathParam: resourcePathParam, resourceMethods: resourceMethods, + isDefaultVersion: isDefaultVersion, } } diff --git a/adapter/internal/oasparser/envoyconf/listener_test.go b/adapter/internal/oasparser/envoyconf/listener_test.go index ce03dc37be..a0c8f53b11 100644 --- a/adapter/internal/oasparser/envoyconf/listener_test.go +++ b/adapter/internal/oasparser/envoyconf/listener_test.go @@ -107,11 +107,11 @@ func testCreateRoutesForUnitTests(t *testing.T) []*routev3.Route { } route1 := createRoute(generateRouteCreateParamsForUnitTests("test", "HTTP", "localhost", "/test", "1.0.0", "/test", - "/testPath", []string{"GET"}, "test-cluster", "", corsConfigModel3)) + "/testPath", []string{"GET"}, "test-cluster", "", corsConfigModel3, false)) route2 := createRoute(generateRouteCreateParamsForUnitTests("test", "HTTP", "localhost", "/test", "1.0.0", "/test", - "/testPath", []string{"POST"}, "test-cluster", "", corsConfigModel3)) + "/testPath", []string{"POST"}, "test-cluster", "", corsConfigModel3, false)) route3 := createRoute(generateRouteCreateParamsForUnitTests("test", "HTTP", "localhost", "/test", "1.0.0", "/test", - "/testPath", []string{"PUT"}, "test-cluster", "", corsConfigModel3)) + "/testPath", []string{"PUT"}, "test-cluster", "", corsConfigModel3, false)) routes := []*routev3.Route{route1, route2, route3}