diff --git a/adapter/internal/operator/controllers/dp/api_controller.go b/adapter/internal/operator/controllers/dp/api_controller.go index ed541a472..4a18a7b58 100644 --- a/adapter/internal/operator/controllers/dp/api_controller.go +++ b/adapter/internal/operator/controllers/dp/api_controller.go @@ -529,6 +529,9 @@ func (apiReconciler *APIReconciler) resolveAPIRefs(ctx context.Context, api dpv1 func isAPIPropagatable(apiState *synchronizer.APIState) bool { validOrgs := []string{"carbon.super"} + if apiState.APIDefinition.Spec.APIType == "GRPC" { + return false + } // System APIs should not be propagated to CP if apiState.APIDefinition.Spec.SystemAPI { return false diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/server/swagger/SwaggerServerHandler.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/server/swagger/SwaggerServerHandler.java index 16538f4d0..04d6cd220 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/server/swagger/SwaggerServerHandler.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/server/swagger/SwaggerServerHandler.java @@ -16,8 +16,11 @@ import org.wso2.apk.enforcer.constants.AdminConstants; import org.wso2.apk.enforcer.constants.HttpConstants; import org.wso2.apk.enforcer.models.ResponsePayload; + import java.util.HashMap; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class SwaggerServerHandler extends SimpleChannelInboundHandler { @@ -43,17 +46,34 @@ public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Excep boolean isSwagger = false; - String [] params = request.uri().split("/"); - final String basePath = "/" + params[1] + "/" + params[2]; - final String vHost = params[3].split("\\?")[0]; - final String queryParam = params[3].split("\\?")[1]; + //check if it's GRPC request using the header + String header = request.headers().get("ApiType"); + String[] params = request.uri().split("/"); + final String basePath; + final String vHost; + final String queryParam; + final String version; + //if len params is 3, then it's a GRPC request else other + final String type = params.length == 3 ? "GRPC" : "REST"; + if (type.equals("GRPC")) { + basePath = "/" + params[1]; + vHost = params[2].split("\\?")[0]; + queryParam = params[2].split("\\?")[1]; + version = extractVersionFromGrpcBasePath(params[1]); + } else { + basePath = "/" + params[1] + "/" + params[2]; + vHost = params[3].split("\\?")[0]; + queryParam = params[3].split("\\?")[1]; + version = params[2]; + } + if (APIDefinitionConstants.SWAGGER_DEFINITION.equalsIgnoreCase(queryParam)) { isSwagger = true; } if(isSwagger){ // load the corresponding swagger definition from the API name - byte[] apiDefinition = apiFactory.getAPIDefinition(basePath, params[2], vHost); + byte[] apiDefinition = apiFactory.getAPIDefinition(basePath, version, vHost); if (apiDefinition == null || apiDefinition.length == 0) { String error = AdminConstants.ErrorMessages.NOT_FOUND; responsePayload = new ResponsePayload(); @@ -91,4 +111,15 @@ private void buildAndSendResponse(ChannelHandlerContext ctx, ResponsePayload res httpResponse.headers().set(HTTP.CONTENT_LEN, httpResponse.content().readableBytes()); ctx.writeAndFlush(httpResponse); } + + private static String extractVersionFromGrpcBasePath(String input) { + // Pattern to match '.v' followed by digits and optional periods followed by more digits + Pattern pattern = Pattern.compile("\\.v\\d+(\\.\\d+)*"); + Matcher matcher = pattern.matcher(input); + + if (matcher.find()) { + return matcher.group().substring(1); // Returns the matched version + } + return ""; + } } diff --git a/runtime/config-deployer-service/ballerina/APIClient.bal b/runtime/config-deployer-service/ballerina/APIClient.bal index 5fcdd78ee..549ae6423 100644 --- a/runtime/config-deployer-service/ballerina/APIClient.bal +++ b/runtime/config-deployer-service/ballerina/APIClient.bal @@ -46,7 +46,7 @@ public class APIClient { encodedString = encodedString.substring(0, encodedString.length() - 1); } APKConf apkConf = { - name: api.getName(), + name: api.getType() == API_TYPE_GRPC ? self.getUniqueNameForGrpcApi(api.getName()) : api.getName(), basePath: api.getBasePath().length() > 0 ? api.getBasePath() : encodedString, version: api.getVersion(), 'type: api.getType() == "" ? API_TYPE_REST : api.getType().toUpperAscii() @@ -280,6 +280,14 @@ public class APIClient { return fullBasePath; } + isolated function returnFullGRPCBasePath(string basePath, string 'version) returns string { + string fullBasePath = basePath; + if (!string:endsWith(basePath, 'version)) { + fullBasePath = string:'join(".", basePath, 'version); + } + return fullBasePath; + } + private isolated function constructURlFromK8sService(K8sService 'k8sService) returns string { return k8sService.protocol + "://" + string:'join(".", k8sService.name, k8sService.namespace, "svc.cluster.local") + ":" + k8sService.port.toString(); } @@ -442,6 +450,19 @@ public class APIClient { sandboxRoutes.push(gqlRoute.metadata.name); } } + } else if apkConf.'type == API_TYPE_GRPC{ + k8sAPI.spec.basePath = self.returnFullGRPCBasePath(apkConf.basePath, apkConf.'version); + foreach model:GRPCRoute grpcRoute in apiArtifact.productionGrpcRoutes { + if grpcRoute.spec.rules.length() > 0 { + productionRoutes.push(grpcRoute.metadata.name); + } + } + foreach model:GRPCRoute grpcRoute in apiArtifact.sandboxGrpcRoutes { + if grpcRoute.spec.rules.length() > 0 { + sandboxRoutes.push(grpcRoute.metadata.name); + } + } + } else { foreach model:HTTPRoute httpRoute in apiArtifact.productionHttpRoutes { if httpRoute.spec.rules.length() > 0 { @@ -532,6 +553,26 @@ public class APIClient { apiArtifact.sandboxGqlRoutes.push(gqlRoute); } } + } else if apkConf.'type == API_TYPE_GRPC { + model:GRPCRoute grpcRoute = { + metadata: + { + name: uniqueId + "-" + endpointType + "-grpcroute-" + count.toString(), + labels: self.getLabels(apkConf, organization) + }, + spec: { + parentRefs: self.generateAndRetrieveParentRefs(apkConf, uniqueId), + rules: check self.generateGRPCRouteRules(apiArtifact, apkConf, endpoint, endpointType, organization), + hostnames: self.getHostNames(apkConf, uniqueId, endpointType, organization) + } + }; + if grpcRoute.spec.rules.length() > 0 { + if endpointType == PRODUCTION_TYPE { + apiArtifact.productionGrpcRoutes.push(grpcRoute); + } else { + apiArtifact.sandboxGrpcRoutes.push(grpcRoute); + } + } } else { model:HTTPRoute httpRoute = { metadata: @@ -553,10 +594,10 @@ public class APIClient { } } } - return; } + private isolated function generateAndRetrieveParentRefs(APKConf apkConf, string uniqueId) returns model:ParentReference[] { string gatewayName = gatewayConfiguration.name; string listenerName = gatewayConfiguration.listenerName; @@ -571,7 +612,7 @@ public class APIClient { APKOperations[]? operations = apkConf.operations; if operations is APKOperations[] { foreach APKOperations operation in operations { - model:HTTPRouteRule|model:GQLRouteRule|() routeRule = check self.generateRouteRule(apiArtifact, apkConf, endpoint, operation, endpointType, organization); + model:HTTPRouteRule|model:GQLRouteRule|model:GRPCRouteRule|() routeRule = check self.generateRouteRule(apiArtifact, apkConf, endpoint, operation, endpointType, organization); if routeRule is model:HTTPRouteRule { model:HTTPRouteFilter[]? filters = routeRule.filters; if filters is () { @@ -625,12 +666,72 @@ public class APIClient { return httpRouteRules; } + private isolated function generateGRPCRouteRules(model:APIArtifact apiArtifact, APKConf apkConf, model:Endpoint? endpoint, string endpointType, commons:Organization organization) returns model:GRPCRouteRule[]|commons:APKError|error { + model:GRPCRouteRule[] grpcRouteRules = []; + APKOperations[]? operations = apkConf.operations; + if operations is APKOperations[] { + foreach APKOperations operation in operations { + model:HTTPRouteRule|model:GQLRouteRule|model:GRPCRouteRule|() routeRule = check self.generateRouteRule(apiArtifact, apkConf, endpoint, operation, endpointType, organization); + if routeRule is model:GRPCRouteRule { + model:GRPCRouteFilter[]? filters = routeRule.filters; + if filters is () { + filters = []; + routeRule.filters = filters; + } + string disableAuthenticationRefName = self.retrieveDisableAuthenticationRefName(apkConf, endpointType, organization); + if !(operation.secured ?: true) { + if !apiArtifact.authenticationMap.hasKey(disableAuthenticationRefName) { + model:Authentication generateDisableAuthenticationCR = self.generateDisableAuthenticationCR(apiArtifact, apkConf, endpointType, organization); + apiArtifact.authenticationMap[disableAuthenticationRefName] = generateDisableAuthenticationCR; + } + model:GRPCRouteFilter disableAuthenticationFilter = {'type: "ExtensionRef", extensionRef: {group: "dp.wso2.com", kind: "Authentication", name: disableAuthenticationRefName}}; + (filters).push(disableAuthenticationFilter); + } + string[]? scopes = operation.scopes; + if scopes is string[] { + int count = 1; + foreach string scope in scopes { + model:Scope scopeCr; + if apiArtifact.scopes.hasKey(scope) { + scopeCr = apiArtifact.scopes.get(scope); + } else { + scopeCr = self.generateScopeCR(apiArtifact, apkConf, organization, scope, count); + count = count + 1; + } + model:GRPCRouteFilter scopeFilter = {'type: "ExtensionRef", extensionRef: {group: "dp.wso2.com", kind: scopeCr.kind, name: scopeCr.metadata.name}}; + (filters).push(scopeFilter); + } + } + if operation.rateLimit != () { + model:RateLimitPolicy? rateLimitPolicyCR = self.generateRateLimitPolicyCR(apkConf, operation.rateLimit, apiArtifact.uniqueId, operation, organization); + if rateLimitPolicyCR != () { + apiArtifact.rateLimitPolicies[rateLimitPolicyCR.metadata.name] = rateLimitPolicyCR; + model:GRPCRouteFilter rateLimitPolicyFilter = {'type: "ExtensionRef", extensionRef: {group: "dp.wso2.com", kind: "RateLimitPolicy", name: rateLimitPolicyCR.metadata.name}}; + (filters).push(rateLimitPolicyFilter); + } + } + if operation.operationPolicies != () { + model:APIPolicy? apiPolicyCR = check self.generateAPIPolicyAndBackendCR(apiArtifact, apkConf, operation, operation.operationPolicies, organization, apiArtifact.uniqueId); + if apiPolicyCR != () { + apiArtifact.apiPolicies[apiPolicyCR.metadata.name] = apiPolicyCR; + model:GRPCRouteFilter apiPolicyFilter = {'type: "ExtensionRef", extensionRef: {group: "dp.wso2.com", kind: "APIPolicy", name: apiPolicyCR.metadata.name}}; + (filters).push(apiPolicyFilter); + } + } + grpcRouteRules.push(routeRule); + } + } + } + return grpcRouteRules; + } + + private isolated function generateGQLRouteRules(model:APIArtifact apiArtifact, APKConf apkConf, model:Endpoint? endpoint, string endpointType, commons:Organization organization) returns model:GQLRouteRule[]|commons:APKError|error { model:GQLRouteRule[] gqlRouteRules = []; APKOperations[]? operations = apkConf.operations; if operations is APKOperations[] { foreach APKOperations operation in operations { - model:HTTPRouteRule|model:GQLRouteRule|() routeRule = check self.generateRouteRule(apiArtifact, apkConf, endpoint, operation, endpointType, organization); + model:HTTPRouteRule|model:GQLRouteRule|model:GRPCRouteRule|() routeRule = check self.generateRouteRule(apiArtifact, apkConf, endpoint, operation, endpointType, organization); if routeRule is model:GQLRouteRule { model:GQLRouteFilter[]? filters = routeRule.filters; if filters is () { @@ -754,7 +855,7 @@ public class APIClient { return authentication; } - private isolated function generateRouteRule(model:APIArtifact apiArtifact, APKConf apkConf, model:Endpoint? endpoint, APKOperations operation, string endpointType, commons:Organization organization) returns model:HTTPRouteRule|model:GQLRouteRule|()|commons:APKError { + private isolated function generateRouteRule(model:APIArtifact apiArtifact, APKConf apkConf, model:Endpoint? endpoint, APKOperations operation, string endpointType, commons:Organization organization) returns model:HTTPRouteRule|model:GQLRouteRule|model:GRPCRouteRule|()|commons:APKError { do { EndpointConfigurations? endpointConfig = operation.endpointConfigurations; model:Endpoint? endpointToUse = (); @@ -778,7 +879,16 @@ public class APIClient { } else { return e909022("Provided Type currently not supported for GraphQL APIs.", error("Provided Type currently not supported for GraphQL APIs.")); } - } else { + } else if apkConf.'type == API_TYPE_GRPC { + model:GRPCRouteMatch[]|error routeMatches = self.retrieveGRPCMatches(apkConf, operation, organization); + if routeMatches is model:GRPCRouteMatch[] && routeMatches.length() > 0 { + model:GRPCRouteRule grpcRouteRule = {matches: routeMatches, backendRefs: self.retrieveGeneratedBackend(apkConf, endpointToUse, endpointType)}; + return grpcRouteRule; + } else { + return e909022("Provided Type currently not supported for GRPC APIs.", error("Provided Type currently not supported for GRPC APIs.")); + } + } + else { model:HTTPRouteRule httpRouteRule = {matches: self.retrieveHTTPMatches(apkConf, operation, organization), backendRefs: self.retrieveGeneratedBackend(apkConf, endpointToUse, endpointType), filters: self.generateFilters(apiArtifact, apkConf, endpointToUse, operation, endpointType, organization)}; return httpRouteRule; } @@ -923,6 +1033,13 @@ public class APIClient { return gqlRouteMatch; } + + private isolated function retrieveGRPCMatches(APKConf apkConf, APKOperations apiOperation, commons:Organization organization) returns model:GRPCRouteMatch[] { + model:GRPCRouteMatch[] grpcRouteMatch = []; + model:GRPCRouteMatch grpcRoute = self.retrieveGRPCRouteMatch(apiOperation); + grpcRouteMatch.push(grpcRoute); + return grpcRouteMatch; + } private isolated function retrieveHttpRouteMatch(APKConf apkConf, APKOperations apiOperation, commons:Organization organization) returns model:HTTPRouteMatch { return {method: apiOperation.verb, path: {'type: "RegularExpression", value: self.retrievePathPrefix(apkConf.basePath, apkConf.'version, apiOperation.target ?: "/*", organization)}}; @@ -937,6 +1054,17 @@ public class APIClient { } } + private isolated function retrieveGRPCRouteMatch(APKOperations apiOperation) returns model:GRPCRouteMatch { + model:GRPCRouteMatch grpcRouteMatch = { + method: { + 'type: "Exact", + 'service: apiOperation.target, + method: apiOperation.verb + } + }; + return grpcRouteMatch; + } + isolated function retrieveGeneratedSwaggerDefinition(APKConf apkConf, string? definition) returns string|json|commons:APKError|error { runtimeModels:API api1 = runtimeModels:newAPI1(); api1.setName(apkConf.name); @@ -973,6 +1101,10 @@ public class APIClient { api1.setGraphQLSchema(definition); return definition; } + if apkConf.'type == API_TYPE_GRPC && definition is string { + api1.setProtoDefinition(definition); + return definition; + } if definition is string && definition.toString().trim().length() > 0 { retrievedDefinition = runtimeUtil:RuntimeAPICommonUtil_generateDefinition2(api1, definition); } else { @@ -1547,6 +1679,11 @@ public class APIClient { return hashedValue.toBase16(); } + public isolated function getUniqueNameForGrpcApi(string concatanatedServices) returns string { + byte[] hashedValue = crypto:hashSha1(concatanatedServices.toBytes()); + return hashedValue.toBase16(); + } + public isolated function retrieveHttpRouteRefName(APKConf apkConf, string 'type, commons:Organization organization) returns string { return uuid:createType1AsString(); } @@ -1644,7 +1781,7 @@ public class APIClient { } else if definitionFile.fileName.endsWith(".json") { apiDefinition = definitionFileContent; } - } else if apiType == API_TYPE_GRAPHQL { + } else if apiType == API_TYPE_GRAPHQL || apiType == API_TYPE_GRPC { apiDefinition = definitionFileContent; } if apkConf is () { diff --git a/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal b/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal index e410cdb51..5ca4070f1 100644 --- a/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal +++ b/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal @@ -93,7 +93,7 @@ public class ConfigGeneratorClient { private isolated function validateAndRetrieveDefinition(string 'type, string? url, byte[]? content, string? fileName) returns runtimeapi:APIDefinitionValidationResponse|runtimeapi:APIManagementException|error|commons:APKError { runtimeapi:APIDefinitionValidationResponse|runtimeapi:APIManagementException|error validationResponse; boolean typeAvailable = 'type.length() > 0; - string[] ALLOWED_API_DEFINITION_TYPES = [API_TYPE_REST, API_TYPE_GRAPHQL, "ASYNC"]; + string[] ALLOWED_API_DEFINITION_TYPES = [API_TYPE_REST, API_TYPE_GRAPHQL, "ASYNC",API_TYPE_GRPC]; if !typeAvailable { return e909005("type"); } @@ -187,6 +187,14 @@ public class ConfigGeneratorClient { string yamlString = check self.convertJsonToYaml(gqlRoute.toJsonString()); _ = check self.storeFile(yamlString, gqlRoute.metadata.name, zipDir); } + foreach model:GRPCRoute grpcRoute in apiArtifact.productionGrpcRoutes { + string yamlString = check self.convertJsonToYaml(grpcRoute.toJsonString()); + _ = check self.storeFile(yamlString, grpcRoute.metadata.name, zipDir); + } + foreach model:GRPCRoute grpcRoute in apiArtifact.sandboxGrpcRoutes { + string yamlString = check self.convertJsonToYaml(grpcRoute.toJsonString()); + _ = check self.storeFile(yamlString, grpcRoute.metadata.name, zipDir); + } foreach model:Backend backend in apiArtifact.backendServices { string yamlString = check self.convertJsonToYaml(backend.toJsonString()); _ = check self.storeFile(yamlString, backend.metadata.name, zipDir); diff --git a/runtime/config-deployer-service/ballerina/DeployerClient.bal b/runtime/config-deployer-service/ballerina/DeployerClient.bal index e5beb5d2d..3bcfb20e6 100644 --- a/runtime/config-deployer-service/ballerina/DeployerClient.bal +++ b/runtime/config-deployer-service/ballerina/DeployerClient.bal @@ -95,6 +95,7 @@ public class DeployerClient { apiArtifact.namespace = apiPartition.namespace; if existingAPI is model:API { check self.deleteHttpRoutes(existingAPI, apiArtifact?.organization); + check self.deleteGrpcRoutes(existingAPI, apiArtifact?.organization); check self.deleteAuthenticationCRs(existingAPI, apiArtifact?.organization); _ = check self.deleteScopeCrsForAPI(existingAPI, apiArtifact?.organization); check self.deleteBackends(existingAPI, apiArtifact?.organization); @@ -122,8 +123,8 @@ public class DeployerClient { check self.deployBackendJWTConfigs(apiArtifact, ownerReference); check self.deployAPIPolicyCRs(apiArtifact, ownerReference); - check self.deployRoutes(apiArtifact.productionHttpRoutes, apiArtifact.productionGqlRoutes, apiArtifact?.namespace, ownerReference); - check self.deployRoutes(apiArtifact.sandboxHttpRoutes, apiArtifact.sandboxGqlRoutes, apiArtifact?.namespace, ownerReference); + check self.deployRoutes(apiArtifact.productionHttpRoutes, apiArtifact.productionGqlRoutes,apiArtifact.productionGrpcRoutes, apiArtifact?.namespace, ownerReference); + check self.deployRoutes(apiArtifact.sandboxHttpRoutes, apiArtifact.sandboxGqlRoutes,apiArtifact.sandboxGrpcRoutes, apiArtifact?.namespace, ownerReference); return deployK8sAPICrResult; } on fail var e { @@ -173,6 +174,30 @@ public class DeployerClient { } } + private isolated function deleteGrpcRoutes(model:API api, string organization) returns commons:APKError? { + do { + model:GRPCRouteList|http:ClientError grpcRouteListResponse = check getGrpcRoutesForAPIs(api.spec.apiName, api.spec.apiVersion, api.metadata?.namespace, organization); + if grpcRouteListResponse is model:GRPCRouteList { + foreach model:GRPCRoute item in grpcRouteListResponse.items { + http:Response|http:ClientError grpcRouteDeletionResponse = deleteGrpcRoute(item.metadata.name, api.metadata?.namespace); + if grpcRouteDeletionResponse is http:Response { + if grpcRouteDeletionResponse.statusCode != http:STATUS_OK { + json responsePayLoad = check grpcRouteDeletionResponse.getJsonPayload(); + model:Status statusResponse = check responsePayLoad.cloneWithType(model:Status); + check self.handleK8sTimeout(statusResponse); + } + } else { + log:printError("Error occured while deleting GrpcRoute", grpcRouteDeletionResponse); + } + } + return; + } + } on fail var e { + log:printError("Error occured deleting grpcRoutes", e); + return e909022("Error occured deleting grpcRoutes", e); + } + } + private isolated function deleteHttpRoutes(model:API api, string organization) returns commons:APKError? { do { model:HTTPRouteList|http:ClientError httpRouteListResponse = check getHttproutesForAPIS(api.spec.apiName, api.spec.apiVersion, api.metadata?.namespace, organization); @@ -312,8 +337,10 @@ public class DeployerClient { model:StatusCause[] 'causes = details.'causes; foreach model:StatusCause 'cause in 'causes { if 'cause.'field == "spec.basePath" { + log:printError("Error occured while updating K8sAPI due to basePath ", e909015(k8sAPI.spec.basePath)); return e909015(k8sAPI.spec.basePath); } else if 'cause.'field == "spec.apiName" { + log:printError("Error occured while updating K8sAPI due to apiName ", e909016(k8sAPI.spec.apiName)); return e909016(k8sAPI.spec.apiName); } } @@ -349,7 +376,7 @@ public class DeployerClient { return e909022("Internal error occured", e = error("Internal error occured")); } } - private isolated function deployRoutes(model:HTTPRoute[]? httproutes, model:GQLRoute[]? gqlroutes, string namespace, model:OwnerReference ownerReference) returns error? { + private isolated function deployRoutes(model:HTTPRoute[]? httproutes, model:GQLRoute[]? gqlroutes,model:GRPCRoute[]? grpcroutes, string namespace, model:OwnerReference ownerReference) returns error? { if httproutes is model:HTTPRoute[] && httproutes.length() > 0 { model:HTTPRoute[] deployReadyHttproutes = httproutes; model:HTTPRoute[]|commons:APKError orderedHttproutes = self.createHttpRoutesOrder(httproutes); @@ -408,6 +435,35 @@ public class DeployerClient { } } } + } else if grpcroutes is model:GRPCRoute[] && grpcroutes.length() > 0 { + model:GRPCRoute[] deployReadyGrpcRoutes = grpcroutes; + model:GRPCRoute[]|commons:APKError orderedGrpcRoutes = self.createGrpcRoutesOrder(grpcroutes); + if orderedGrpcRoutes is model:GRPCRoute[] { + deployReadyGrpcRoutes = orderedGrpcRoutes; + } + foreach model:GRPCRoute grpcRoute in deployReadyGrpcRoutes { + grpcRoute.metadata.ownerReferences = [ownerReference]; + if grpcRoute.spec.rules.length() > 0 { + http:Response deployGrpcRouteResult = check deployGrpcRoute(grpcRoute, namespace); + if deployGrpcRouteResult.statusCode == http:STATUS_CREATED { + log:printDebug("Deployed GrpcRoute Successfully" + grpcRoute.toString()); + } else if deployGrpcRouteResult.statusCode == http:STATUS_CONFLICT { + log:printDebug("GrpcRoute already exists" + grpcRoute.toString()); + model:GRPCRoute grpcRouteFromK8s = check getGrpcRoute(grpcRoute.metadata.name, namespace); + grpcRoute.metadata.resourceVersion = grpcRouteFromK8s.metadata.resourceVersion; + http:Response grpcRouteCR = check updateGrpcRoute(grpcRoute, namespace); + if grpcRouteCR.statusCode != http:STATUS_OK { + json responsePayLoad = check grpcRouteCR.getJsonPayload(); + model:Status statusResponse = check responsePayLoad.cloneWithType(model:Status); + check self.handleK8sTimeout(statusResponse); + } + } else { + json responsePayLoad = check deployGrpcRouteResult.getJsonPayload(); + model:Status statusResponse = check responsePayLoad.cloneWithType(model:Status); + check self.handleK8sTimeout(statusResponse); + } + } + } } } @@ -442,6 +498,21 @@ public class DeployerClient { return e909022("Error occured while sorting gqlRoutes", e); } } + public isolated function createGrpcRoutesOrder(model:GRPCRoute[] grpcRoutes) returns model:GRPCRoute[]|commons:APKError { + do { + foreach model:GRPCRoute route in grpcRoutes { + model:GRPCRouteRule[] routeRules = route.spec.rules; + model:GRPCRouteRule[] sortedRouteRules = from var routeRule in routeRules + order by (routeRule.matches[0].method.'service) descending + select routeRule; + route.spec.rules = sortedRouteRules; + } + return grpcRoutes; + } on fail var e { + log:printError("Error occured while sorting grpcRoutes", e); + return e909022("Error occured while sorting grpcRoutes", e); + } + } private isolated function deployAuthenticationCRs(model:APIArtifact apiArtifact, model:OwnerReference ownerReference) returns error? { string[] keys = apiArtifact.authenticationMap.keys(); diff --git a/runtime/config-deployer-service/ballerina/K8sClient.bal b/runtime/config-deployer-service/ballerina/K8sClient.bal index 5e7757206..45723229a 100644 --- a/runtime/config-deployer-service/ballerina/K8sClient.bal +++ b/runtime/config-deployer-service/ballerina/K8sClient.bal @@ -100,6 +100,16 @@ isolated function deleteGqlRoute(string name, string namespace) returns http:Res return k8sApiServerEp->delete(endpoint, targetType = http:Response); } +isolated function getGrpcRoute(string name, string namespace) returns model:GRPCRoute|http:ClientError { + string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes/" + name; + return k8sApiServerEp->get(endpoint, targetType = model:GRPCRoute); +} + +isolated function deleteGrpcRoute(string name, string namespace) returns http:Response|http:ClientError { + string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes/" + name; + return k8sApiServerEp->delete(endpoint, targetType = http:Response); +} + isolated function getConfigMap(string name, string namespace) returns model:ConfigMap|http:ClientError { string endpoint = "/api/v1/namespaces/" + namespace + "/configmaps/" + name; return k8sApiServerEp->get(endpoint, targetType = model:ConfigMap); @@ -150,6 +160,16 @@ isolated function updateGqlRoute(model:GQLRoute gqlroute, string namespace) retu return k8sApiServerEp->put(endpoint, gqlroute, targetType = http:Response); } +isolated function deployGrpcRoute(model:GRPCRoute grpcRoute, string namespace) returns http:Response|http:ClientError { + string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes"; + return k8sApiServerEp->post(endpoint, grpcRoute, targetType = http:Response); +} + +isolated function updateGrpcRoute(model:GRPCRoute grpcRoute, string namespace) returns http:Response|http:ClientError { + string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes/" + grpcRoute.metadata.name; + return k8sApiServerEp->put(endpoint, grpcRoute, targetType = http:Response); +} + public isolated function getK8sAPIByNameAndNamespace(string name, string namespace) returns model:API?|commons:APKError { string endpoint = "/apis/dp.wso2.com/v1alpha2/namespaces/" + namespace + "/apis/" + name; do { @@ -244,7 +264,10 @@ public isolated function getGqlRoutesForAPIs(string apiName, string apiVersion, string endpoint = "/apis/dp.wso2.com/v1alpha2/namespaces/" + namespace + "/gqlroutes/?labelSelector=" + check generateUrlEncodedLabelSelector(apiName, apiVersion, organization); return k8sApiServerEp->get(endpoint, targetType = model:GQLRouteList); } - +public isolated function getGrpcRoutesForAPIs(string apiName, string apiVersion, string namespace, string organization) returns model:GRPCRouteList|http:ClientError|error { + string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes/?labelSelector=" + check generateUrlEncodedLabelSelector(apiName, apiVersion, organization); + return k8sApiServerEp->get(endpoint, targetType = model:GRPCRouteList); +} isolated function deployRateLimitPolicyCR(model:RateLimitPolicy rateLimitPolicy, string namespace) returns http:Response|http:ClientError { string endpoint = "/apis/dp.wso2.com/v1alpha1/namespaces/" + namespace + "/ratelimitpolicies"; return k8sApiServerEp->post(endpoint, rateLimitPolicy, targetType = http:Response); diff --git a/runtime/config-deployer-service/ballerina/constants.bal b/runtime/config-deployer-service/ballerina/constants.bal index 66f523456..ed2ad0f6f 100644 --- a/runtime/config-deployer-service/ballerina/constants.bal +++ b/runtime/config-deployer-service/ballerina/constants.bal @@ -11,6 +11,7 @@ public final string[] WS_SUPPORTED_METHODS = ["subscribe", "publish"]; const string API_TYPE_REST = "REST"; const string API_TYPE_GRAPHQL = "GRAPHQL"; +const string API_TYPE_GRPC = "GRPC"; const string API_TYPE_SOAP = "SOAP"; const string API_TYPE_SSE = "SSE"; const string API_TYPE_WS = "WS"; @@ -52,7 +53,7 @@ const string ENDPOINT_SECURITY_PASSWORD = "password"; const string ZIP_FILE_EXTENSTION = ".zip"; const string PROTOCOL_HTTP = "http"; const string PROTOCOL_HTTPS = "https"; -final string[] & readonly ALLOWED_API_TYPES = [API_TYPE_REST, API_TYPE_GRAPHQL]; +final string[] & readonly ALLOWED_API_TYPES = [API_TYPE_REST, API_TYPE_GRAPHQL, API_TYPE_GRPC]; const string MEDIATION_POLICY_TYPE_REQUEST_HEADER_MODIFIER = "RequestHeaderModifier"; const string MEDIATION_POLICY_TYPE_RESPONSE_HEADER_MODIFIER = "ResponseHeaderModifier"; diff --git a/runtime/config-deployer-service/ballerina/modules/model/APIArtifact.bal b/runtime/config-deployer-service/ballerina/modules/model/APIArtifact.bal index 7f272176a..b40f95448 100644 --- a/runtime/config-deployer-service/ballerina/modules/model/APIArtifact.bal +++ b/runtime/config-deployer-service/ballerina/modules/model/APIArtifact.bal @@ -6,6 +6,8 @@ public type APIArtifact record {| HTTPRoute[] sandboxHttpRoutes = []; GQLRoute[] productionGqlRoutes = []; GQLRoute[] sandboxGqlRoutes = []; + GRPCRoute[] productionGrpcRoutes = []; + GRPCRoute[] sandboxGrpcRoutes = []; ConfigMap definition?; map endpointCertificates = {}; map certificateMap = {}; diff --git a/runtime/config-deployer-service/ballerina/modules/model/GRPCRoute.bal b/runtime/config-deployer-service/ballerina/modules/model/GRPCRoute.bal new file mode 100644 index 000000000..4087ea485 --- /dev/null +++ b/runtime/config-deployer-service/ballerina/modules/model/GRPCRoute.bal @@ -0,0 +1,75 @@ +// +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +public type GRPCRouteSpec record { + *CommonRouteSpec; + string[] hostnames?; + GRPCRouteRule[] rules = []; +}; + +public type GRPCRouteRule record { + GRPCRouteMatch[] matches; + GRPCRouteFilter[] filters?; + GRPCBackendRef[] backendRefs?; +}; +public type GRPCRouteMatch record { + GRPCMethodMatch method; + GRPCHeaderMatch[] headers?; +}; + +public type GRPCHeaderMatch record { + string 'type; + string name; + string value; +}; + +public type GRPCMethodMatch record { + string 'type; + string 'service; + string method; +}; + +public type GRPCRouteFilter record { + string 'type; + HTTPHeaderFilter requestHeaderModifier?; + HTTPHeaderFilter responseHeaderModifier?; + HTTPRequestMirrorFilter requestMirror?; + LocalObjectReference extensionRef?; +}; + +public type GRPCBackendRef record { + *BackendRef; + GRPCRouteFilter[] filters?; +}; + +public type GRPCRoute record {| + string apiVersion = "gateway.networking.k8s.io/v1alpha2"; + string kind = "GRPCRoute"; + Metadata metadata; + GRPCRouteSpec spec; +|}; + + +public type GRPCRouteList record {| + string apiVersion = "gateway.networking.k8s.io/v1alpha2"; + string kind = "GRPCRouteList"; + ListMeta metadata; + GRPCRoute[] items; +|}; + + diff --git a/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config.model/API.bal b/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config.model/API.bal index 8638e05be..42470efc5 100644 --- a/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config.model/API.bal +++ b/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config.model/API.bal @@ -183,6 +183,13 @@ public distinct class API { org_wso2_apk_config_model_API_setGraphQLSchema(self.jObj, java:fromString(arg0)); } + # The function that maps to the `setProtoDefinition` method of `org.wso2.apk.config.model.API`. + # + // # + arg0 - The `string` value required to map with the Java method parameter. + public isolated function setProtoDefinition(string arg0) { + org_wso2_apk_config_model_API_setProtoDefinition(self.jObj, java:fromString(arg0)); + } + # The function that maps to the `setName` method of `org.wso2.apk.config.model.API`. # # + arg0 - The `string` value required to map with the Java method parameter. @@ -328,6 +335,13 @@ isolated function org_wso2_apk_config_model_API_getGraphQLSchema(handle receiver paramTypes: [] } external; + +isolated function org_wso2_apk_config_model_API_getProtoDefinition(handle receiver) returns handle = @java:Method { + name: "getProtoDefinition", + 'class: "org.wso2.apk.config.model.API", + paramTypes: [] +} external; + isolated function org_wso2_apk_config_model_API_getName(handle receiver) returns handle = @java:Method { name: "getName", 'class: "org.wso2.apk.config.model.API", @@ -412,6 +426,12 @@ isolated function org_wso2_apk_config_model_API_setGraphQLSchema(handle receiver paramTypes: ["java.lang.String"] } external; +isolated function org_wso2_apk_config_model_API_setProtoDefinition(handle receiver, handle arg0) = @java:Method { + name: "setProtoDefinition", + 'class: "org.wso2.apk.config.model.API", + paramTypes: ["java.lang.String"] +} external; + isolated function org_wso2_apk_config_model_API_setName(handle receiver, handle arg0) = @java:Method { name: "setName", 'class: "org.wso2.apk.config.model.API", diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/APIConstants.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/APIConstants.java index ed4a91e2a..1fcaaadb0 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/APIConstants.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/APIConstants.java @@ -49,6 +49,7 @@ public final class APIConstants { public static final String OBJECT = "object"; public static final String GRAPHQL_API = "GRAPHQL"; + public static final String GRPC_API = "GRPC"; public static final String APPLICATION_JSON_MEDIA_TYPE = "application/json"; // registry location for OpenAPI files @@ -90,7 +91,7 @@ public final class APIConstants { public static final String GRAPHQL_QUERY = "QUERY"; public enum ParserType { - REST, ASYNC, GRAPHQL + REST, ASYNC, GRAPHQL, GRPC } public static class OperationParameter { diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java index 8cfb71084..585e1010e 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java @@ -21,7 +21,8 @@ private DefinitionParserFactory() { public static APIDefinition getParser(API api) { if (APIConstants.ParserType.REST.name().equals(api.getType()) - || APIConstants.ParserType.GRAPHQL.name().equals(api.getType())) { + || APIConstants.ParserType.GRAPHQL.name().equals(api.getType()) + || APIConstants.ParserType.GRPC.name().equals(api.getType())) { return new OAS3Parser(); } else if (APIConstants.ParserType.ASYNC.name().equals(api.getType())) { return new AsyncApiParser(); diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java index 03f043359..c93073e6d 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java @@ -6,6 +6,7 @@ import org.wso2.apk.config.api.ExceptionCodes; import org.wso2.apk.config.definitions.GraphQLSchemaDefinition; import org.wso2.apk.config.definitions.OASParserUtil; +import org.wso2.apk.config.definitions.ProtoParser; import org.wso2.apk.config.model.API; import org.wso2.apk.config.model.URITemplate; @@ -66,7 +67,18 @@ public static APIDefinitionValidationResponse validateOpenAPIDefinition(String t OASParserUtil.addErrorToValidationResponse(validationResponse, "Invalid definition file type provided."); } + } else if (APIConstants.ParserType.GRPC.name().equals(type.toUpperCase())) { + if (fileName.endsWith(".proto")) { + validationResponse = OASParserUtil.validateProtoDefinition( + new String(inputByteArray, StandardCharsets.UTF_8), + returnContent); + } else { + OASParserUtil.addErrorToValidationResponse(validationResponse, + "Invalid definition file type provided."); + } } + + return validationResponse; } @@ -99,6 +111,8 @@ public static API getAPIFromDefinition(String definition, String apiType) throws if (apiType.toUpperCase().equals(APIConstants.GRAPHQL_API)) { return getGQLAPIFromDefinition(definition); + } else if (apiType.toUpperCase().equals(APIConstants.GRPC_API)) { + return getGRPCAPIFromProtoDefinition(definition); } else { APIDefinition parser = DefinitionParserFactory.getParser(apiType); if (parser != null) { @@ -128,6 +142,35 @@ private static API getGQLAPIFromDefinition(String definition) { return api; } + private static API getGRPCAPIFromProtoDefinition(String definition) { + ProtoParser protoParser = new ProtoParser(definition); + List uriTemplates = new ArrayList<>(); + API api = new API(); + api.setBasePath("/"+protoParser.protoFile.basePath); + api.setVersion(protoParser.protoFile.version); + StringBuilder apiName = new StringBuilder(); + List sortedServices = new ArrayList<>(); + + for (ProtoParser.Service service : protoParser.getServices()) { + sortedServices.add(service.name); + for (String method : service.methods) { + URITemplate uriTemplate = new URITemplate(); + uriTemplate.setUriTemplate(protoParser.protoFile.packageName + "." + service.name); + uriTemplate.setVerb(method); + uriTemplates.add(uriTemplate); + } + } + sortedServices.sort(String::compareTo); + for (String service : sortedServices) { + apiName.append(service).append("-"); + } + apiName.deleteCharAt(apiName.length() - 1); + api.setName(apiName.toString()); + api.setUriTemplates(uriTemplates.toArray(new URITemplate[uriTemplates.size()])); + + return api; + } + private RuntimeAPICommonUtil() { } diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/api/ExceptionCodes.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/api/ExceptionCodes.java index f24a3a2fe..6324d8700 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/api/ExceptionCodes.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/api/ExceptionCodes.java @@ -361,6 +361,10 @@ public enum ExceptionCodes implements ErrorHandler { UNSUPPORTED_GRAPHQL_FILE_EXTENSION(900802, "Unsupported GraphQL Schema File Extension", 400, "Unsupported extension. Only supported extensions are .graphql, .txt and .sdl"), + //GRPC API related codes + GRPC_PROTO_DEFINTION_CANNOT_BE_NULL(900803, "gRPC Proto Definition cannot be empty or null", 400, + "gRPC Proto Definition cannot be empty or null"), + // Oauth related codes AUTH_GENERAL_ERROR(900900, "Authorization Error", 403, " Error in authorization"), INVALID_CREDENTIALS(900901, "Invalid Credentials", 401, " Invalid username or password"), diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/OASParserUtil.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/OASParserUtil.java index 66eae511c..4ec23b7df 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/OASParserUtil.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/OASParserUtil.java @@ -668,6 +668,34 @@ public static APIDefinitionValidationResponse validateGraphQLSchema(String apiDe return validationResponse; } + /** + * Validate gRPC proto definition + * + * @return Validation response + */ + public static APIDefinitionValidationResponse validateProtoDefinition(String apiDefinition, + boolean returnContent) { + APIDefinitionValidationResponse validationResponse = new APIDefinitionValidationResponse(); + ArrayList errors = new ArrayList<>(); + try { + if (apiDefinition.isBlank()) { + validationResponse.setValid(false); + errors.add(ExceptionCodes.GRPC_PROTO_DEFINTION_CANNOT_BE_NULL); + validationResponse.setErrorItems(errors); + } else { + validationResponse.setValid(true); + validationResponse.setContent(apiDefinition); + } + } catch (Exception e) { + OASParserUtil.addErrorToValidationResponse(validationResponse, e.getMessage()); + validationResponse.setValid(false); + errors.add(new ErrorItem("API Definition Validation Error", "API Definition is invalid", 400, 400)); + validationResponse.setErrorItems(errors); + } + return validationResponse; + + } + /** * This method removes the unsupported json blocks from the given json string. * diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java new file mode 100644 index 000000000..2f371f420 --- /dev/null +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java @@ -0,0 +1,165 @@ +package org.wso2.apk.config.definitions; + +import java.util.List; +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ProtoParser { + + public ProtoFile protoFile; + public ProtoParser(String content) { + protoFile = parseProtoContent(content); + } + + public String getPackageString(String content) { + Pattern packagePattern = Pattern.compile("package\\s+([\\w\\.]+);"); + Matcher packageMatcher = packagePattern.matcher(content); + if (packageMatcher.find()) { + return packageMatcher.group(1); + } + return null; + } + public String getVersion(String packageString) { + Pattern versionPattern = Pattern.compile("v\\d+(\\.\\d+)*"); + Matcher versionMatcher = versionPattern.matcher(packageString); + if (versionMatcher.find()) { + return versionMatcher.group(0); + } + System.out.println("No version found"); + return null; + } + public String getPackageName(String packageString){ + Pattern namePattern = Pattern.compile("v\\d+(\\.\\d+)*\\.(\\w+)"); + Matcher nameMatcher = namePattern.matcher(packageString); + if (nameMatcher.find()) { + return nameMatcher.group(2); + } + System.out.println("No name found"); + return null; + } + + public String getBasePath(String packageString){ + Pattern basePathPattern = Pattern.compile("^(.*?)v\\d"); + + Matcher basePathMatcher = basePathPattern.matcher(packageString); + if (basePathMatcher.find()) { + String basePath = basePathMatcher.group(1); + if(basePath.charAt(basePath.length()-1) == '.'){ + basePath = basePath.substring(0, basePath.length()-1); + } + return basePath; + } + System.out.println("No base path found"); + return null; + } + + // Method to extract service blocks from a given text + public List extractServiceBlocks(String text) { + // Regular expression pattern to match the service blocks + String patternString = "service\\s+\\w+\\s*\\{[^{}]*(?:\\{[^{}]*\\}[^{}]*)*\\}"; + + // Compile the regular expression + Pattern pattern = Pattern.compile(patternString, Pattern.DOTALL); + Matcher matcher = pattern.matcher(text); + + // Find all matches and append them to the result + List result = new ArrayList<>(); + while (matcher.find()) { + result.add(matcher.group()); + } + return result; + } + + public List extractMethodNames(String serviceBlock) { + // Regular expression pattern to match the method names + String patternString = "(?<=rpc\\s)\\w+"; + + // Compile the regular expression + Pattern pattern = Pattern.compile(patternString); + Matcher matcher = pattern.matcher(serviceBlock); + + // Find all matches and append them to the result + List result = new ArrayList<>(); + while (matcher.find()) { + result.add(matcher.group()); + } + return result; + } + + public String getServiceName(String serviceBlock) { + // Regular expression pattern to match the service name + String patternString = "(?<=service\\s)\\w+"; + + // Compile the regular expression + Pattern pattern = Pattern.compile(patternString); + Matcher matcher = pattern.matcher(serviceBlock); + + // Find the first match and return it + if (matcher.find()) { + return matcher.group(); + } + return null; + } + + public ProtoFile parseProtoContent(String content) { + ProtoFile protoFile = new ProtoFile(); + protoFile.services = new ArrayList<>(); + + List serviceBlocks = extractServiceBlocks(content); + for (String serviceBlock : serviceBlocks) { + Service service = new Service(); + service.name = getServiceName(serviceBlock); + service.methods = new ArrayList<>(); + service.methods.addAll(extractMethodNames(serviceBlock)); + protoFile.services.add(service); + } + + // Extract package name + String packageName = getPackageString(content); + protoFile.packageName = getPackageName(packageName); + protoFile.version = getVersion(packageName); + protoFile.basePath = getBasePath(packageName); + +// System.out.println(protoFile); + + return protoFile; + } + + public List getMethods(Service Service){ + return Service.methods; + } + public List getServices(){ + return this.protoFile.services; + } + public class ProtoFile { + public String packageName; + public String basePath; + public String version; + public List services; + + @Override + public String toString() { + return "ProtoFile{" + + "packageName='" + packageName + '\'' + + ", basePath='" + basePath + '\'' + + ", version='" + version + '\'' + + ", services=" + services + + '}'; + } + + } + + public class Service { + public String name; + public List methods; + + @Override + public String toString() { + return " Service{" + + "name='" + name + '\'' + + ", methods=" + methods + + '}'; + } + } +} \ No newline at end of file diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java index d2715d77b..a88c2508f 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java @@ -11,6 +11,7 @@ public class API { private String apiSecurity; private String[] scopes; private String graphQLSchema; + private String protoDefinition; private String swaggerDefinition; private String environment; @@ -79,6 +80,13 @@ public void setGraphQLSchema(String graphQLSchema) { this.graphQLSchema = graphQLSchema; } + public void setProtoDefinition(String protoDefinition) { + this.protoDefinition = protoDefinition; + } + + public String getProtoDefinition() { + return protoDefinition; + } public String getSwaggerDefinition() { return swaggerDefinition; } diff --git a/test/cucumber-tests/CRs/artifacts.yaml b/test/cucumber-tests/CRs/artifacts.yaml index cf592dad8..0ee441c99 100644 --- a/test/cucumber-tests/CRs/artifacts.yaml +++ b/test/cucumber-tests/CRs/artifacts.yaml @@ -1154,3 +1154,51 @@ spec: protocol: TCP selector: app: graphql-faker +--- +apiVersion: v1 +kind: Service +metadata: + name: grpc-backend + namespace: apk-integration-test +spec: + selector: + app: grpc-backend + ports: + - protocol: TCP + port: 6565 + targetPort: 6565 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grpc-backend + namespace: apk-integration-test + labels: + app: grpc-backend +spec: + replicas: 1 + selector: + matchLabels: + app: grpc-backend + template: + metadata: + labels: + app: grpc-backend + spec: + containers: + - name: grpc-backend + image: ddh13/dineth-grpc-demo-server:1.0.0 + imagePullPolicy: Always + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + resources: + requests: + cpu: 10m +--- \ No newline at end of file diff --git a/test/cucumber-tests/build.gradle b/test/cucumber-tests/build.gradle index 1e348b543..decdfb1ca 100644 --- a/test/cucumber-tests/build.gradle +++ b/test/cucumber-tests/build.gradle @@ -35,6 +35,11 @@ dependencies { testImplementation 'io.cucumber:cucumber-testng:7.13.0' testImplementation 'commons-io:commons-io:2.13.0' testImplementation 'com.nimbusds:nimbus-jose-jwt:9.31' + implementation 'javax.annotation:javax.annotation-api:1.3.2' + implementation 'io.grpc:grpc-netty:1.48.0' + implementation 'io.grpc:grpc-protobuf:1.48.0' + implementation 'io.grpc:grpc-stub:1.48.0' + implementation 'io.grpc:grpc-auth:1.48.0' } test { diff --git a/test/cucumber-tests/scripts/setup-hosts.sh b/test/cucumber-tests/scripts/setup-hosts.sh index 5fb451248..2f541a7b5 100644 --- a/test/cucumber-tests/scripts/setup-hosts.sh +++ b/test/cucumber-tests/scripts/setup-hosts.sh @@ -6,6 +6,7 @@ kubectl wait deployment/backend-retry-deployment -n apk-integration-test --for=c kubectl wait deployment/dynamic-backend -n apk-integration-test --for=condition=available --timeout=600s kubectl wait deployment/interceptor-service-deployment -n apk-integration-test --for=condition=available --timeout=600s kubectl wait deployment/graphql-faker -n apk-integration-test --for=condition=available --timeout=600s +kubectl wait deployment/grpc-backend -n apk-integration-test --for=condition=available --timeout=600s kubectl wait --timeout=5m -n apk-integration-test deployment/apk-test-setup-wso2-apk-adapter-deployment --for=condition=Available kubectl wait --timeout=15m -n apk-integration-test deployment/apk-test-setup-wso2-apk-gateway-runtime-deployment --for=condition=Available IP=$(kubectl get svc apk-test-setup-wso2-apk-gateway-service -n apk-integration-test --output jsonpath='{.status.loadBalancer.ingress[0].ip}') diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/BaseSteps.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/BaseSteps.java index bdc423ec6..faf39122f 100644 --- a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/BaseSteps.java +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/BaseSteps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC (http://www.wso2.com). + * Copyright (c) 2024, WSO2 LLC (http://www.wso2.com). * * WSO2 LLC licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -39,6 +39,8 @@ import io.cucumber.java.Before; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -49,7 +51,9 @@ import org.testng.Assert; import org.wso2.apk.integration.utils.Constants; import org.wso2.apk.integration.utils.Utils; +import org.wso2.apk.integration.utils.clients.SimpleGRPCStudentClient; import org.wso2.apk.integration.utils.clients.SimpleHTTPClient; +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentResponse; import java.io.IOException; import java.io.InputStream; @@ -92,10 +96,27 @@ public void systemIsReady() { } + @Then("the student response body should contain name: {string} age: {int}") + public void theStudentResponseBodyShouldContainNameAndAge(String arg0, int arg1) { + StudentResponse studentResponse = sharedContext.getStudentResponse(); + if (studentResponse == null) { + Assert.fail("Student response is null."); + } + int age = studentResponse.getAge(); + String name = studentResponse.getName(); + Assert.assertEquals(name, arg0); + Assert.assertEquals(age, arg1); + } + @Then("the response body should contain {string}") public void theResponseBodyShouldContain(String expectedText) throws IOException { Assert.assertTrue(sharedContext.getResponseBody().contains(expectedText), "Actual response body: " + sharedContext.getResponseBody()); } + @Then("the response body should contain endpoint definition for student.proto") + public void theResponseBodyShouldContainEndpointDefinition() throws IOException { + String expectedText = "{\"apiDefinition\":\"syntax = \\\"proto3\\\";\\n\\noption java_multiple_files = true;\\noption java_package = \\\"org.example\\\";\\npackage dineth.grpc.api.v1.student;\\n\\nservice StudentService {\\n rpc GetStudent(StudentRequest) returns (StudentResponse) {};\\n rpc GetStudentStream(StudentRequest) returns (stream StudentResponse) {};\\n rpc SendStudentStream(stream StudentRequest) returns (StudentResponse) {};\\n rpc SendAndGetStudentStream(stream StudentRequest) returns (stream StudentResponse) {}\\n}\\n\\nmessage StudentRequest {\\n int32 id = 3;\\n}\\n\\nmessage StudentResponse {\\n string name = 1;\\n int32 age = 2;\\n}\\n\"}"; + Assert.assertTrue(sharedContext.getResponseBody().contains(expectedText), "Actual response body: " + sharedContext.getResponseBody()); + } @Then("the response body should not contain {string}") public void theResponseBodyShouldNotContain(String expectedText) throws IOException { Assert.assertFalse(sharedContext.getResponseBody().contains(expectedText), "Actual response body: " + sharedContext.getResponseBody()); @@ -116,6 +137,12 @@ public void theResponseStatusCodeShouldBe(int expectedStatusCode) throws IOExcep Assert.assertEquals(actualStatusCode, expectedStatusCode); } + @Then("the gRPC response status code should be {int}") + public void theGrpcResponseStatusCodeShouldBe(int expectedStatusCode) throws IOException { + int actualStatusCode = sharedContext.getGrpcStatusCode(); + Assert.assertEquals(actualStatusCode, expectedStatusCode); + } + @Then("I send {string} request to {string} with body {string}") public void sendHttpRequest(String httpMethod, String url, String body) throws IOException { body = Utils.resolveVariables(body, sharedContext.getValueStore()); @@ -139,6 +166,30 @@ public void sendHttpRequest(String httpMethod, String url, String body) throws I } } + @Then("I make grpc request GetStudent to {string} with port {int}") + public void GetStudent(String arg0, int arg1) throws StatusRuntimeException { + try { + SimpleGRPCStudentClient grpcStudentClient = new SimpleGRPCStudentClient(arg0, arg1); + sharedContext.setStudentResponse(grpcStudentClient.GetStudent(sharedContext.getHeaders())); + sharedContext.setGrpcStatusCode(0); + } catch (StatusRuntimeException e) { + sharedContext.setGrpcStatusCode(e.getStatus().getCode().value()); + logger.error(e.getMessage() + " Status code: " + e.getStatus().getCode().value()) ; + } + } + + @Then("I make grpc request GetStudent default version to {string} with port {int}") + public void GetStudentDefaultVersion(String arg0, int arg1) throws StatusRuntimeException { + try { + SimpleGRPCStudentClient grpcStudentClient = new SimpleGRPCStudentClient(arg0, arg1); + sharedContext.setStudentResponse(grpcStudentClient.GetStudentDefaultVersion(sharedContext.getHeaders())); + sharedContext.setGrpcStatusCode(0); + } catch (StatusRuntimeException e) { + sharedContext.setGrpcStatusCode(e.getStatus().getCode().value()); + logger.error(e.getMessage() + " Status code: " + e.getStatus().getCode().value()) ; + } + } + // It will send request using a new thread and forget about the response @Then("I send {string} async request to {string} with body {string}") public void sendAsyncHttpRequest(String httpMethod, String url, String body) throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/SharedContext.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/SharedContext.java index 782e06173..66bc9bfbc 100644 --- a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/SharedContext.java +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/SharedContext.java @@ -19,11 +19,11 @@ import org.apache.http.HttpResponse; import org.wso2.apk.integration.utils.clients.SimpleHTTPClient; +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentResponse; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -31,9 +31,11 @@ public class SharedContext { private SimpleHTTPClient httpClient; + private StudentResponse studentResponse; private String accessToken; private HttpResponse response; private String responseBody; + private int grpcStatusCode; private HashMap valueStore = new HashMap<>(); private HashMap headers = new HashMap<>(); @@ -53,12 +55,28 @@ public void setAccessToken(String accessToken) { this.accessToken = accessToken; } + public int getGrpcStatusCode() { + return grpcStatusCode; + } + public void setGrpcStatusCode(int grpcStatusCode) { + this.grpcStatusCode = grpcStatusCode; + } public HttpResponse getResponse() { return response; } + public StudentResponse getStudentResponse() { + + return studentResponse; + } + + public void setStudentResponse(StudentResponse studentResponse) { + + this.studentResponse = studentResponse; + } + public void setResponse(HttpResponse response) { this.response = response; diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/GenericClientInterceptor.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/GenericClientInterceptor.java new file mode 100644 index 000000000..17ab5702e --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/GenericClientInterceptor.java @@ -0,0 +1,39 @@ +package org.wso2.apk.integration.utils; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import io.grpc.CallOptions; +import io.grpc.ClientCall; +import io.grpc.Channel; +import java.util.Map; + + +import java.util.Map; + +public class GenericClientInterceptor implements ClientInterceptor { + + private Map headers; + + public GenericClientInterceptor(Map headers) { + this.headers = headers; + } + + @Override + public ClientCall interceptCall( + MethodDescriptor method, CallOptions callOptions, Channel next) { + return new ForwardingClientCall.SimpleForwardingClientCall( + next.newCall(method, callOptions)) { + + @Override + public void start(Listener responseListener, Metadata headersMetadata) { + // Set each header in the map to the Metadata headers + headers.forEach((key, value) -> headersMetadata.put( + Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER), value)); + + super.start(responseListener, headersMetadata); + } + }; + } +} + diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/SimpleGRPCStudentClient.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/SimpleGRPCStudentClient.java new file mode 100644 index 000000000..0f7ba0b98 --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/SimpleGRPCStudentClient.java @@ -0,0 +1,115 @@ +package org.wso2.apk.integration.utils.clients; + +import io.grpc.StatusRuntimeException; +import io.grpc.netty.GrpcSslContexts; +import io.grpc.netty.NettyChannelBuilder; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import io.grpc.ManagedChannel; +import org.wso2.apk.integration.utils.GenericClientInterceptor; +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentRequest; +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentResponse; +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentServiceDefaultVersionGrpc; +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentServiceGrpc; + +import javax.net.ssl.SSLException; +import java.util.Map; +import java.util.concurrent.TimeUnit; + + +public class SimpleGRPCStudentClient { + protected Log log = LogFactory.getLog(SimpleGRPCStudentClient.class); + private static final int EVENTUAL_SUCCESS_RESPONSE_TIMEOUT_IN_SECONDS = 10; + private final String host; + private final int port; + + public SimpleGRPCStudentClient(String host, int port) { + this.host = host; + this.port = port; + } + + public StudentResponse GetStudent(Map headers) throws StatusRuntimeException{ + ManagedChannel managedChannel = null; + try { + SslContext sslContext = GrpcSslContexts.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .build(); + + GenericClientInterceptor interceptor = new GenericClientInterceptor(headers); + managedChannel = NettyChannelBuilder.forAddress(host, port) + .sslContext(sslContext) + .intercept(interceptor) + .build(); + StudentServiceGrpc.StudentServiceBlockingStub blockingStub = StudentServiceGrpc.newBlockingStub(managedChannel); + if (blockingStub == null) { + log.error("Failed to create blocking stub"); + throw new RuntimeException("Failed to create blocking stub"); + } + StudentResponse response = blockingStub.getStudent(StudentRequest.newBuilder().setId(1).build()); + if (response == null) { + log.error("Failed to get student"); + throw new RuntimeException("Failed to get student"); + } + return response; + }catch (SSLException e) { + throw new RuntimeException("Failed to create SSL context", e); + } finally { + // Shut down the channel to release resources + if (managedChannel != null) { + managedChannel.shutdown(); // Initiates a graceful shutdown + try { + // Wait at most 5 seconds for the channel to terminate + if (!managedChannel.awaitTermination(5, TimeUnit.SECONDS)) { + managedChannel.shutdownNow(); // Force shutdown if it does not complete within the timeout + } + } catch (InterruptedException ie) { + managedChannel.shutdownNow(); // Force shutdown if the thread is interrupted + } + } + } + } + public StudentResponse GetStudentDefaultVersion(Map headers) throws StatusRuntimeException{ + ManagedChannel managedChannel = null; + try { + SslContext sslContext = GrpcSslContexts.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .build(); + + GenericClientInterceptor interceptor = new GenericClientInterceptor(headers); + managedChannel = NettyChannelBuilder.forAddress(host, port) + .sslContext(sslContext) + .intercept(interceptor) + .build(); + StudentServiceDefaultVersionGrpc.StudentServiceBlockingStub blockingStub = StudentServiceDefaultVersionGrpc.newBlockingStub(managedChannel); + if (blockingStub == null) { + log.error("Failed to create blocking stub"); + throw new RuntimeException("Failed to create blocking stub"); + } + StudentResponse response = blockingStub.getStudent(StudentRequest.newBuilder().setId(1).build()); + if (response == null) { + log.error("Failed to get student"); + throw new RuntimeException("Failed to get student"); + } + return response; + }catch (SSLException e) { + throw new RuntimeException("Failed to create SSL context", e); + } finally { + // Shut down the channel to release resources + if (managedChannel != null) { + managedChannel.shutdown(); // Initiates a graceful shutdown + try { + // Wait at most 5 seconds for the channel to terminate + if (!managedChannel.awaitTermination(5, TimeUnit.SECONDS)) { + managedChannel.shutdownNow(); // Force shutdown if it does not complete within the timeout + } + } catch (InterruptedException ie) { + managedChannel.shutdownNow(); // Force shutdown if the thread is interrupted + } + } + } + } + +} + diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/Student.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/Student.java new file mode 100644 index 000000000..8ec85fec1 --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/Student.java @@ -0,0 +1,72 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: student.proto + +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +public final class Student { + private Student() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + static final com.google.protobuf.Descriptors.Descriptor + internal_static_dineth_grpc_v1_student_StudentRequest_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_dineth_grpc_v1_student_StudentRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_dineth_grpc_v1_student_StudentResponse_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_dineth_grpc_v1_student_StudentResponse_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + String[] descriptorData = { + "\n\rstudent.proto\022\026dineth.grpc.v1.student\"" + + "\034\n\016StudentRequest\022\n\n\002id\030\003 \001(\005\",\n\017Student" + + "Response\022\014\n\004name\030\001 \001(\t\022\013\n\003age\030\002 \001(\0052\266\003\n\016" + + "StudentService\022_\n\nGetStudent\022&.dineth.gr" + + "pc.v1.student.StudentRequest\032\'.dineth.gr" + + "pc.v1.student.StudentResponse\"\000\022g\n\020GetSt" + + "udentStream\022&.dineth.grpc.v1.student.Stu" + + "dentRequest\032\'.dineth.grpc.v1.student.Stu" + + "dentResponse\"\0000\001\022h\n\021SendStudentStream\022&." + + "dineth.grpc.v1.student.StudentRequest\032\'." + + "dineth.grpc.v1.student.StudentResponse\"\000" + + "(\001\022p\n\027SendAndGetStudentStream\022&.dineth.g" + + "rpc.v1.student.StudentRequest\032\'.dineth.g" + + "rpc.v1.student.StudentResponse\"\000(\0010\001B<\n8" + + "org.wso2.apk.integration.utils.clients.s" + + "tudentGrpcClientP\001b\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_dineth_grpc_v1_student_StudentRequest_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_dineth_grpc_v1_student_StudentRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_dineth_grpc_v1_student_StudentRequest_descriptor, + new String[] { "Id", }); + internal_static_dineth_grpc_v1_student_StudentResponse_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_dineth_grpc_v1_student_StudentResponse_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_dineth_grpc_v1_student_StudentResponse_descriptor, + new String[] { "Name", "Age", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequest.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequest.java new file mode 100644 index 000000000..c487c6567 --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequest.java @@ -0,0 +1,483 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: student.proto + +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +/** + * Protobuf type {@code dineth.grpc.v1.student.StudentRequest} + */ +public final class StudentRequest extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:dineth.grpc.v1.student.StudentRequest) + StudentRequestOrBuilder { +private static final long serialVersionUID = 0L; + // Use StudentRequest.newBuilder() to construct. + private StudentRequest(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private StudentRequest() { + } + + @Override + @SuppressWarnings({"unused"}) + protected Object newInstance( + UnusedPrivateParameter unused) { + return new StudentRequest(); + } + + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private StudentRequest( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 24: { + + id_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return Student.internal_static_dineth_grpc_v1_student_StudentRequest_descriptor; + } + + @Override + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return Student.internal_static_dineth_grpc_v1_student_StudentRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + StudentRequest.class, Builder.class); + } + + public static final int ID_FIELD_NUMBER = 3; + private int id_; + /** + * int32 id = 3; + * @return The id. + */ + @Override + public int getId() { + return id_; + } + + private byte memoizedIsInitialized = -1; + @Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (id_ != 0) { + output.writeInt32(3, id_); + } + unknownFields.writeTo(output); + } + + @Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (id_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, id_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof StudentRequest)) { + return super.equals(obj); + } + StudentRequest other = (StudentRequest) obj; + + if (getId() + != other.getId()) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static StudentRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static StudentRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static StudentRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static StudentRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static StudentRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static StudentRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static StudentRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static StudentRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static StudentRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static StudentRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static StudentRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static StudentRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(StudentRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code dineth.grpc.v1.student.StudentRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:dineth.grpc.v1.student.StudentRequest) + StudentRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return Student.internal_static_dineth_grpc_v1_student_StudentRequest_descriptor; + } + + @Override + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return Student.internal_static_dineth_grpc_v1_student_StudentRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + StudentRequest.class, Builder.class); + } + + // Construct using org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentRequest.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @Override + public Builder clear() { + super.clear(); + id_ = 0; + + return this; + } + + @Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return Student.internal_static_dineth_grpc_v1_student_StudentRequest_descriptor; + } + + @Override + public StudentRequest getDefaultInstanceForType() { + return StudentRequest.getDefaultInstance(); + } + + @Override + public StudentRequest build() { + StudentRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @Override + public StudentRequest buildPartial() { + StudentRequest result = new StudentRequest(this); + result.id_ = id_; + onBuilt(); + return result; + } + + @Override + public Builder clone() { + return super.clone(); + } + @Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return super.setField(field, value); + } + @Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, Object value) { + return super.setRepeatedField(field, index, value); + } + @Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return super.addRepeatedField(field, value); + } + @Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof StudentRequest) { + return mergeFrom((StudentRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(StudentRequest other) { + if (other == StudentRequest.getDefaultInstance()) return this; + if (other.getId() != 0) { + setId(other.getId()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @Override + public final boolean isInitialized() { + return true; + } + + @Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + StudentRequest parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (StudentRequest) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private int id_ ; + /** + * int32 id = 3; + * @return The id. + */ + @Override + public int getId() { + return id_; + } + /** + * int32 id = 3; + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId(int value) { + + id_ = value; + onChanged(); + return this; + } + /** + * int32 id = 3; + * @return This builder for chaining. + */ + public Builder clearId() { + + id_ = 0; + onChanged(); + return this; + } + @Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:dineth.grpc.v1.student.StudentRequest) + } + + // @@protoc_insertion_point(class_scope:dineth.grpc.v1.student.StudentRequest) + private static final StudentRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new StudentRequest(); + } + + public static StudentRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @Override + public StudentRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new StudentRequest(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @Override + public StudentRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequestOrBuilder.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequestOrBuilder.java new file mode 100644 index 000000000..62c36bc77 --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequestOrBuilder.java @@ -0,0 +1,15 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: student.proto + +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +public interface StudentRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:dineth.grpc.v1.student.StudentRequest) + com.google.protobuf.MessageOrBuilder { + + /** + * int32 id = 3; + * @return The id. + */ + int getId(); +} diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponse.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponse.java new file mode 100644 index 000000000..33e343eba --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponse.java @@ -0,0 +1,621 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: student.proto + +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +/** + * Protobuf type {@code dineth.grpc.v1.student.StudentResponse} + */ +public final class StudentResponse extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:dineth.grpc.v1.student.StudentResponse) + StudentResponseOrBuilder { +private static final long serialVersionUID = 0L; + // Use StudentResponse.newBuilder() to construct. + private StudentResponse(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private StudentResponse() { + name_ = ""; + } + + @Override + @SuppressWarnings({"unused"}) + protected Object newInstance( + UnusedPrivateParameter unused) { + return new StudentResponse(); + } + + @Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private StudentResponse( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + String s = input.readStringRequireUtf8(); + + name_ = s; + break; + } + case 16: { + + age_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return Student.internal_static_dineth_grpc_v1_student_StudentResponse_descriptor; + } + + @Override + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return Student.internal_static_dineth_grpc_v1_student_StudentResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + StudentResponse.class, Builder.class); + } + + public static final int NAME_FIELD_NUMBER = 1; + private volatile Object name_; + /** + * string name = 1; + * @return The name. + */ + @Override + public String getName() { + Object ref = name_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + /** + * string name = 1; + * @return The bytes for name. + */ + @Override + public com.google.protobuf.ByteString + getNameBytes() { + Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int AGE_FIELD_NUMBER = 2; + private int age_; + /** + * int32 age = 2; + * @return The age. + */ + @Override + public int getAge() { + return age_; + } + + private byte memoizedIsInitialized = -1; + @Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!getNameBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, name_); + } + if (age_ != 0) { + output.writeInt32(2, age_); + } + unknownFields.writeTo(output); + } + + @Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!getNameBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, name_); + } + if (age_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, age_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof StudentResponse)) { + return super.equals(obj); + } + StudentResponse other = (StudentResponse) obj; + + if (!getName() + .equals(other.getName())) return false; + if (getAge() + != other.getAge()) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + AGE_FIELD_NUMBER; + hash = (53 * hash) + getAge(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static StudentResponse parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static StudentResponse parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static StudentResponse parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static StudentResponse parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static StudentResponse parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static StudentResponse parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static StudentResponse parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static StudentResponse parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static StudentResponse parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static StudentResponse parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static StudentResponse parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static StudentResponse parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(StudentResponse prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code dineth.grpc.v1.student.StudentResponse} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:dineth.grpc.v1.student.StudentResponse) + StudentResponseOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return Student.internal_static_dineth_grpc_v1_student_StudentResponse_descriptor; + } + + @Override + protected FieldAccessorTable + internalGetFieldAccessorTable() { + return Student.internal_static_dineth_grpc_v1_student_StudentResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + StudentResponse.class, Builder.class); + } + + // Construct using org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentResponse.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @Override + public Builder clear() { + super.clear(); + name_ = ""; + + age_ = 0; + + return this; + } + + @Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return Student.internal_static_dineth_grpc_v1_student_StudentResponse_descriptor; + } + + @Override + public StudentResponse getDefaultInstanceForType() { + return StudentResponse.getDefaultInstance(); + } + + @Override + public StudentResponse build() { + StudentResponse result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @Override + public StudentResponse buildPartial() { + StudentResponse result = new StudentResponse(this); + result.name_ = name_; + result.age_ = age_; + onBuilt(); + return result; + } + + @Override + public Builder clone() { + return super.clone(); + } + @Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return super.setField(field, value); + } + @Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, Object value) { + return super.setRepeatedField(field, index, value); + } + @Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return super.addRepeatedField(field, value); + } + @Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof StudentResponse) { + return mergeFrom((StudentResponse)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(StudentResponse other) { + if (other == StudentResponse.getDefaultInstance()) return this; + if (!other.getName().isEmpty()) { + name_ = other.name_; + onChanged(); + } + if (other.getAge() != 0) { + setAge(other.getAge()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @Override + public final boolean isInitialized() { + return true; + } + + @Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + StudentResponse parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (StudentResponse) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private Object name_ = ""; + /** + * string name = 1; + * @return The name. + */ + public String getName() { + Object ref = name_; + if (!(ref instanceof String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (String) ref; + } + } + /** + * string name = 1; + * @return The bytes for name. + */ + public com.google.protobuf.ByteString + getNameBytes() { + Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string name = 1; + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + + name_ = value; + onChanged(); + return this; + } + /** + * string name = 1; + * @return This builder for chaining. + */ + public Builder clearName() { + + name_ = getDefaultInstance().getName(); + onChanged(); + return this; + } + /** + * string name = 1; + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + name_ = value; + onChanged(); + return this; + } + + private int age_ ; + /** + * int32 age = 2; + * @return The age. + */ + @Override + public int getAge() { + return age_; + } + /** + * int32 age = 2; + * @param value The age to set. + * @return This builder for chaining. + */ + public Builder setAge(int value) { + + age_ = value; + onChanged(); + return this; + } + /** + * int32 age = 2; + * @return This builder for chaining. + */ + public Builder clearAge() { + + age_ = 0; + onChanged(); + return this; + } + @Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:dineth.grpc.v1.student.StudentResponse) + } + + // @@protoc_insertion_point(class_scope:dineth.grpc.v1.student.StudentResponse) + private static final StudentResponse DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new StudentResponse(); + } + + public static StudentResponse getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @Override + public StudentResponse parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new StudentResponse(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @Override + public StudentResponse getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponseOrBuilder.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponseOrBuilder.java new file mode 100644 index 000000000..08946635c --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponseOrBuilder.java @@ -0,0 +1,27 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: student.proto + +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +public interface StudentResponseOrBuilder extends + // @@protoc_insertion_point(interface_extends:dineth.grpc.v1.student.StudentResponse) + com.google.protobuf.MessageOrBuilder { + + /** + * string name = 1; + * @return The name. + */ + String getName(); + /** + * string name = 1; + * @return The bytes for name. + */ + com.google.protobuf.ByteString + getNameBytes(); + + /** + * int32 age = 2; + * @return The age. + */ + int getAge(); +} diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceDefaultVersionGrpc.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceDefaultVersionGrpc.java new file mode 100644 index 000000000..9a2878343 --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceDefaultVersionGrpc.java @@ -0,0 +1,458 @@ +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +import static io.grpc.MethodDescriptor.generateFullMethodName; + +/** + */ +@javax.annotation.Generated( + value = "by gRPC proto compiler (version 1.39.0)", + comments = "Source: student.proto") +public final class StudentServiceDefaultVersionGrpc { + + private StudentServiceDefaultVersionGrpc() {} + + public static final String SERVICE_NAME = "dineth.grpc.api.student.StudentService"; + + // Static method descriptors that strictly reflect the proto. + private static volatile io.grpc.MethodDescriptor getGetStudentMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "GetStudent", + requestType = StudentRequest.class, + responseType = StudentResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetStudentMethod() { + io.grpc.MethodDescriptor getGetStudentMethod; + if ((getGetStudentMethod = StudentServiceDefaultVersionGrpc.getGetStudentMethod) == null) { + synchronized (StudentServiceDefaultVersionGrpc.class) { + if ((getGetStudentMethod = StudentServiceDefaultVersionGrpc.getGetStudentMethod) == null) { + StudentServiceDefaultVersionGrpc.getGetStudentMethod = getGetStudentMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetStudent")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("GetStudent")) + .build(); + } + } + } + return getGetStudentMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetStudentStreamMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "GetStudentStream", + requestType = StudentRequest.class, + responseType = StudentResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + public static io.grpc.MethodDescriptor getGetStudentStreamMethod() { + io.grpc.MethodDescriptor getGetStudentStreamMethod; + if ((getGetStudentStreamMethod = StudentServiceDefaultVersionGrpc.getGetStudentStreamMethod) == null) { + synchronized (StudentServiceDefaultVersionGrpc.class) { + if ((getGetStudentStreamMethod = StudentServiceDefaultVersionGrpc.getGetStudentStreamMethod) == null) { + StudentServiceDefaultVersionGrpc.getGetStudentStreamMethod = getGetStudentStreamMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetStudentStream")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("GetStudentStream")) + .build(); + } + } + } + return getGetStudentStreamMethod; + } + + private static volatile io.grpc.MethodDescriptor getSendStudentStreamMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "SendStudentStream", + requestType = StudentRequest.class, + responseType = StudentResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING) + public static io.grpc.MethodDescriptor getSendStudentStreamMethod() { + io.grpc.MethodDescriptor getSendStudentStreamMethod; + if ((getSendStudentStreamMethod = StudentServiceDefaultVersionGrpc.getSendStudentStreamMethod) == null) { + synchronized (StudentServiceDefaultVersionGrpc.class) { + if ((getSendStudentStreamMethod = StudentServiceDefaultVersionGrpc.getSendStudentStreamMethod) == null) { + StudentServiceDefaultVersionGrpc.getSendStudentStreamMethod = getSendStudentStreamMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SendStudentStream")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("SendStudentStream")) + .build(); + } + } + } + return getSendStudentStreamMethod; + } + + private static volatile io.grpc.MethodDescriptor getSendAndGetStudentStreamMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "SendAndGetStudentStream", + requestType = StudentRequest.class, + responseType = StudentResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING) + public static io.grpc.MethodDescriptor getSendAndGetStudentStreamMethod() { + io.grpc.MethodDescriptor getSendAndGetStudentStreamMethod; + if ((getSendAndGetStudentStreamMethod = StudentServiceDefaultVersionGrpc.getSendAndGetStudentStreamMethod) == null) { + synchronized (StudentServiceDefaultVersionGrpc.class) { + if ((getSendAndGetStudentStreamMethod = StudentServiceDefaultVersionGrpc.getSendAndGetStudentStreamMethod) == null) { + StudentServiceDefaultVersionGrpc.getSendAndGetStudentStreamMethod = getSendAndGetStudentStreamMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SendAndGetStudentStream")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("SendAndGetStudentStream")) + .build(); + } + } + } + return getSendAndGetStudentStreamMethod; + } + + /** + * Creates a new async stub that supports all call types for the service + */ + public static StudentServiceStub newStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @Override + public StudentServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceStub(channel, callOptions); + } + }; + return StudentServiceStub.newStub(factory, channel); + } + + /** + * Creates a new blocking-style stub that supports unary and streaming output calls on the service + */ + public static StudentServiceBlockingStub newBlockingStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @Override + public StudentServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceBlockingStub(channel, callOptions); + } + }; + return StudentServiceBlockingStub.newStub(factory, channel); + } + + /** + * Creates a new ListenableFuture-style stub that supports unary calls on the service + */ + public static StudentServiceFutureStub newFutureStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @Override + public StudentServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceFutureStub(channel, callOptions); + } + }; + return StudentServiceFutureStub.newStub(factory, channel); + } + + /** + */ + public static abstract class StudentServiceImplBase implements io.grpc.BindableService { + + /** + */ + public void getStudent(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetStudentMethod(), responseObserver); + } + + /** + */ + public void getStudentStream(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetStudentStreamMethod(), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getSendStudentStreamMethod(), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendAndGetStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getSendAndGetStudentStreamMethod(), responseObserver); + } + + @Override public final io.grpc.ServerServiceDefinition bindService() { + return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) + .addMethod( + getGetStudentMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + StudentRequest, + StudentResponse>( + this, METHODID_GET_STUDENT))) + .addMethod( + getGetStudentStreamMethod(), + io.grpc.stub.ServerCalls.asyncServerStreamingCall( + new MethodHandlers< + StudentRequest, + StudentResponse>( + this, METHODID_GET_STUDENT_STREAM))) + .addMethod( + getSendStudentStreamMethod(), + io.grpc.stub.ServerCalls.asyncClientStreamingCall( + new MethodHandlers< + StudentRequest, + StudentResponse>( + this, METHODID_SEND_STUDENT_STREAM))) + .addMethod( + getSendAndGetStudentStreamMethod(), + io.grpc.stub.ServerCalls.asyncBidiStreamingCall( + new MethodHandlers< + StudentRequest, + StudentResponse>( + this, METHODID_SEND_AND_GET_STUDENT_STREAM))) + .build(); + } + } + + /** + */ + public static final class StudentServiceStub extends io.grpc.stub.AbstractAsyncStub { + private StudentServiceStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected StudentServiceStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceStub(channel, callOptions); + } + + /** + */ + public void getStudent(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetStudentMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void getStudentStream(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncServerStreamingCall( + getChannel().newCall(getGetStudentStreamMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ClientCalls.asyncClientStreamingCall( + getChannel().newCall(getSendStudentStreamMethod(), getCallOptions()), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendAndGetStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ClientCalls.asyncBidiStreamingCall( + getChannel().newCall(getSendAndGetStudentStreamMethod(), getCallOptions()), responseObserver); + } + } + + /** + */ + public static final class StudentServiceBlockingStub extends io.grpc.stub.AbstractBlockingStub { + private StudentServiceBlockingStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected StudentServiceBlockingStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceBlockingStub(channel, callOptions); + } + + /** + */ + public StudentResponse getStudent(StudentRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetStudentMethod(), getCallOptions(), request); + } + + /** + */ + public java.util.Iterator getStudentStream( + StudentRequest request) { + return io.grpc.stub.ClientCalls.blockingServerStreamingCall( + getChannel(), getGetStudentStreamMethod(), getCallOptions(), request); + } + } + + /** + */ + public static final class StudentServiceFutureStub extends io.grpc.stub.AbstractFutureStub { + private StudentServiceFutureStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected StudentServiceFutureStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceFutureStub(channel, callOptions); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getStudent( + StudentRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetStudentMethod(), getCallOptions()), request); + } + } + + private static final int METHODID_GET_STUDENT = 0; + private static final int METHODID_GET_STUDENT_STREAM = 1; + private static final int METHODID_SEND_STUDENT_STREAM = 2; + private static final int METHODID_SEND_AND_GET_STUDENT_STREAM = 3; + + private static final class MethodHandlers implements + io.grpc.stub.ServerCalls.UnaryMethod, + io.grpc.stub.ServerCalls.ServerStreamingMethod, + io.grpc.stub.ServerCalls.ClientStreamingMethod, + io.grpc.stub.ServerCalls.BidiStreamingMethod { + private final StudentServiceImplBase serviceImpl; + private final int methodId; + + MethodHandlers(StudentServiceImplBase serviceImpl, int methodId) { + this.serviceImpl = serviceImpl; + this.methodId = methodId; + } + + @Override + @SuppressWarnings("unchecked") + public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_GET_STUDENT: + serviceImpl.getStudent((StudentRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_STUDENT_STREAM: + serviceImpl.getStudentStream((StudentRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + default: + throw new AssertionError(); + } + } + + @Override + @SuppressWarnings("unchecked") + public io.grpc.stub.StreamObserver invoke( + io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_SEND_STUDENT_STREAM: + return (io.grpc.stub.StreamObserver) serviceImpl.sendStudentStream( + (io.grpc.stub.StreamObserver) responseObserver); + case METHODID_SEND_AND_GET_STUDENT_STREAM: + return (io.grpc.stub.StreamObserver) serviceImpl.sendAndGetStudentStream( + (io.grpc.stub.StreamObserver) responseObserver); + default: + throw new AssertionError(); + } + } + } + + private static abstract class StudentServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { + StudentServiceBaseDescriptorSupplier() {} + + @Override + public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { + return Student.getDescriptor(); + } + + @Override + public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { + return getFileDescriptor().findServiceByName("StudentService"); + } + } + + private static final class StudentServiceFileDescriptorSupplier + extends StudentServiceBaseDescriptorSupplier { + StudentServiceFileDescriptorSupplier() {} + } + + private static final class StudentServiceMethodDescriptorSupplier + extends StudentServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { + private final String methodName; + + StudentServiceMethodDescriptorSupplier(String methodName) { + this.methodName = methodName; + } + + @Override + public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { + return getServiceDescriptor().findMethodByName(methodName); + } + } + + private static volatile io.grpc.ServiceDescriptor serviceDescriptor; + + public static io.grpc.ServiceDescriptor getServiceDescriptor() { + io.grpc.ServiceDescriptor result = serviceDescriptor; + if (result == null) { + synchronized (StudentServiceDefaultVersionGrpc.class) { + result = serviceDescriptor; + if (result == null) { + serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) + .setSchemaDescriptor(new StudentServiceFileDescriptorSupplier()) + .addMethod(getGetStudentMethod()) + .addMethod(getGetStudentStreamMethod()) + .addMethod(getSendStudentStreamMethod()) + .addMethod(getSendAndGetStudentStreamMethod()) + .build(); + } + } + } + return result; + } +} diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceGrpc.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceGrpc.java new file mode 100644 index 000000000..b9b158c0f --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceGrpc.java @@ -0,0 +1,458 @@ +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +import static io.grpc.MethodDescriptor.generateFullMethodName; + +/** + */ +@javax.annotation.Generated( + value = "by gRPC proto compiler (version 1.39.0)", + comments = "Source: student.proto") +public final class StudentServiceGrpc { + + private StudentServiceGrpc() {} + + public static final String SERVICE_NAME = "dineth.grpc.api.v1.student.StudentService"; + + // Static method descriptors that strictly reflect the proto. + private static volatile io.grpc.MethodDescriptor getGetStudentMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "GetStudent", + requestType = StudentRequest.class, + responseType = StudentResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetStudentMethod() { + io.grpc.MethodDescriptor getGetStudentMethod; + if ((getGetStudentMethod = StudentServiceGrpc.getGetStudentMethod) == null) { + synchronized (StudentServiceGrpc.class) { + if ((getGetStudentMethod = StudentServiceGrpc.getGetStudentMethod) == null) { + StudentServiceGrpc.getGetStudentMethod = getGetStudentMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetStudent")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("GetStudent")) + .build(); + } + } + } + return getGetStudentMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetStudentStreamMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "GetStudentStream", + requestType = StudentRequest.class, + responseType = StudentResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + public static io.grpc.MethodDescriptor getGetStudentStreamMethod() { + io.grpc.MethodDescriptor getGetStudentStreamMethod; + if ((getGetStudentStreamMethod = StudentServiceGrpc.getGetStudentStreamMethod) == null) { + synchronized (StudentServiceGrpc.class) { + if ((getGetStudentStreamMethod = StudentServiceGrpc.getGetStudentStreamMethod) == null) { + StudentServiceGrpc.getGetStudentStreamMethod = getGetStudentStreamMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetStudentStream")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("GetStudentStream")) + .build(); + } + } + } + return getGetStudentStreamMethod; + } + + private static volatile io.grpc.MethodDescriptor getSendStudentStreamMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "SendStudentStream", + requestType = StudentRequest.class, + responseType = StudentResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING) + public static io.grpc.MethodDescriptor getSendStudentStreamMethod() { + io.grpc.MethodDescriptor getSendStudentStreamMethod; + if ((getSendStudentStreamMethod = StudentServiceGrpc.getSendStudentStreamMethod) == null) { + synchronized (StudentServiceGrpc.class) { + if ((getSendStudentStreamMethod = StudentServiceGrpc.getSendStudentStreamMethod) == null) { + StudentServiceGrpc.getSendStudentStreamMethod = getSendStudentStreamMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SendStudentStream")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("SendStudentStream")) + .build(); + } + } + } + return getSendStudentStreamMethod; + } + + private static volatile io.grpc.MethodDescriptor getSendAndGetStudentStreamMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "SendAndGetStudentStream", + requestType = StudentRequest.class, + responseType = StudentResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING) + public static io.grpc.MethodDescriptor getSendAndGetStudentStreamMethod() { + io.grpc.MethodDescriptor getSendAndGetStudentStreamMethod; + if ((getSendAndGetStudentStreamMethod = StudentServiceGrpc.getSendAndGetStudentStreamMethod) == null) { + synchronized (StudentServiceGrpc.class) { + if ((getSendAndGetStudentStreamMethod = StudentServiceGrpc.getSendAndGetStudentStreamMethod) == null) { + StudentServiceGrpc.getSendAndGetStudentStreamMethod = getSendAndGetStudentStreamMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SendAndGetStudentStream")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("SendAndGetStudentStream")) + .build(); + } + } + } + return getSendAndGetStudentStreamMethod; + } + + /** + * Creates a new async stub that supports all call types for the service + */ + public static StudentServiceStub newStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @Override + public StudentServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceStub(channel, callOptions); + } + }; + return StudentServiceStub.newStub(factory, channel); + } + + /** + * Creates a new blocking-style stub that supports unary and streaming output calls on the service + */ + public static StudentServiceBlockingStub newBlockingStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @Override + public StudentServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceBlockingStub(channel, callOptions); + } + }; + return StudentServiceBlockingStub.newStub(factory, channel); + } + + /** + * Creates a new ListenableFuture-style stub that supports unary calls on the service + */ + public static StudentServiceFutureStub newFutureStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @Override + public StudentServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceFutureStub(channel, callOptions); + } + }; + return StudentServiceFutureStub.newStub(factory, channel); + } + + /** + */ + public static abstract class StudentServiceImplBase implements io.grpc.BindableService { + + /** + */ + public void getStudent(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetStudentMethod(), responseObserver); + } + + /** + */ + public void getStudentStream(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetStudentStreamMethod(), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getSendStudentStreamMethod(), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendAndGetStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getSendAndGetStudentStreamMethod(), responseObserver); + } + + @Override public final io.grpc.ServerServiceDefinition bindService() { + return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) + .addMethod( + getGetStudentMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + StudentRequest, + StudentResponse>( + this, METHODID_GET_STUDENT))) + .addMethod( + getGetStudentStreamMethod(), + io.grpc.stub.ServerCalls.asyncServerStreamingCall( + new MethodHandlers< + StudentRequest, + StudentResponse>( + this, METHODID_GET_STUDENT_STREAM))) + .addMethod( + getSendStudentStreamMethod(), + io.grpc.stub.ServerCalls.asyncClientStreamingCall( + new MethodHandlers< + StudentRequest, + StudentResponse>( + this, METHODID_SEND_STUDENT_STREAM))) + .addMethod( + getSendAndGetStudentStreamMethod(), + io.grpc.stub.ServerCalls.asyncBidiStreamingCall( + new MethodHandlers< + StudentRequest, + StudentResponse>( + this, METHODID_SEND_AND_GET_STUDENT_STREAM))) + .build(); + } + } + + /** + */ + public static final class StudentServiceStub extends io.grpc.stub.AbstractAsyncStub { + private StudentServiceStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected StudentServiceStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceStub(channel, callOptions); + } + + /** + */ + public void getStudent(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetStudentMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void getStudentStream(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncServerStreamingCall( + getChannel().newCall(getGetStudentStreamMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ClientCalls.asyncClientStreamingCall( + getChannel().newCall(getSendStudentStreamMethod(), getCallOptions()), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendAndGetStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ClientCalls.asyncBidiStreamingCall( + getChannel().newCall(getSendAndGetStudentStreamMethod(), getCallOptions()), responseObserver); + } + } + + /** + */ + public static final class StudentServiceBlockingStub extends io.grpc.stub.AbstractBlockingStub { + private StudentServiceBlockingStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected StudentServiceBlockingStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceBlockingStub(channel, callOptions); + } + + /** + */ + public StudentResponse getStudent(StudentRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetStudentMethod(), getCallOptions(), request); + } + + /** + */ + public java.util.Iterator getStudentStream( + StudentRequest request) { + return io.grpc.stub.ClientCalls.blockingServerStreamingCall( + getChannel(), getGetStudentStreamMethod(), getCallOptions(), request); + } + } + + /** + */ + public static final class StudentServiceFutureStub extends io.grpc.stub.AbstractFutureStub { + private StudentServiceFutureStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected StudentServiceFutureStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceFutureStub(channel, callOptions); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getStudent( + StudentRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetStudentMethod(), getCallOptions()), request); + } + } + + private static final int METHODID_GET_STUDENT = 0; + private static final int METHODID_GET_STUDENT_STREAM = 1; + private static final int METHODID_SEND_STUDENT_STREAM = 2; + private static final int METHODID_SEND_AND_GET_STUDENT_STREAM = 3; + + private static final class MethodHandlers implements + io.grpc.stub.ServerCalls.UnaryMethod, + io.grpc.stub.ServerCalls.ServerStreamingMethod, + io.grpc.stub.ServerCalls.ClientStreamingMethod, + io.grpc.stub.ServerCalls.BidiStreamingMethod { + private final StudentServiceImplBase serviceImpl; + private final int methodId; + + MethodHandlers(StudentServiceImplBase serviceImpl, int methodId) { + this.serviceImpl = serviceImpl; + this.methodId = methodId; + } + + @Override + @SuppressWarnings("unchecked") + public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_GET_STUDENT: + serviceImpl.getStudent((StudentRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_STUDENT_STREAM: + serviceImpl.getStudentStream((StudentRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + default: + throw new AssertionError(); + } + } + + @Override + @SuppressWarnings("unchecked") + public io.grpc.stub.StreamObserver invoke( + io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_SEND_STUDENT_STREAM: + return (io.grpc.stub.StreamObserver) serviceImpl.sendStudentStream( + (io.grpc.stub.StreamObserver) responseObserver); + case METHODID_SEND_AND_GET_STUDENT_STREAM: + return (io.grpc.stub.StreamObserver) serviceImpl.sendAndGetStudentStream( + (io.grpc.stub.StreamObserver) responseObserver); + default: + throw new AssertionError(); + } + } + } + + private static abstract class StudentServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { + StudentServiceBaseDescriptorSupplier() {} + + @Override + public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { + return Student.getDescriptor(); + } + + @Override + public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { + return getFileDescriptor().findServiceByName("StudentService"); + } + } + + private static final class StudentServiceFileDescriptorSupplier + extends StudentServiceBaseDescriptorSupplier { + StudentServiceFileDescriptorSupplier() {} + } + + private static final class StudentServiceMethodDescriptorSupplier + extends StudentServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { + private final String methodName; + + StudentServiceMethodDescriptorSupplier(String methodName) { + this.methodName = methodName; + } + + @Override + public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { + return getServiceDescriptor().findMethodByName(methodName); + } + } + + private static volatile io.grpc.ServiceDescriptor serviceDescriptor; + + public static io.grpc.ServiceDescriptor getServiceDescriptor() { + io.grpc.ServiceDescriptor result = serviceDescriptor; + if (result == null) { + synchronized (StudentServiceGrpc.class) { + result = serviceDescriptor; + if (result == null) { + serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) + .setSchemaDescriptor(new StudentServiceFileDescriptorSupplier()) + .addMethod(getGetStudentMethod()) + .addMethod(getGetStudentStreamMethod()) + .addMethod(getSendStudentStreamMethod()) + .addMethod(getSendAndGetStudentStreamMethod()) + .build(); + } + } + } + return result; + } +} diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc.apk-conf new file mode 100644 index 000000000..4d291742b --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc.apk-conf @@ -0,0 +1,28 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-basic-api" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: false +subscriptionValidation: false +operations: +- target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] \ No newline at end of file diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_scopes.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_scopes.apk-conf new file mode 100644 index 000000000..c4edb6983 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_scopes.apk-conf @@ -0,0 +1,28 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-scopes" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: false +subscriptionValidation: false +operations: +- target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: ["wso2"] +- target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] \ No newline at end of file diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_default_version.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_default_version.apk-conf new file mode 100644 index 000000000..2c2f88441 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_default_version.apk-conf @@ -0,0 +1,28 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-default-version-api" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: true +subscriptionValidation: false +operations: +- target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] \ No newline at end of file diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_disabled_auth.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_disabled_auth.apk-conf new file mode 100644 index 000000000..50bdc465b --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_disabled_auth.apk-conf @@ -0,0 +1,32 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-auth-disabled-api" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: false +subscriptionValidation: false +operations: +- target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] +authentication: + - authType: OAuth2 + required: mandatory + enabled: false diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_disabled.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_disabled.apk-conf new file mode 100644 index 000000000..8341e9095 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_disabled.apk-conf @@ -0,0 +1,36 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-mtls-mandatory-oauth2-disabled" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: false +subscriptionValidation: false +operations: +- target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] +authentication: + - authType: OAuth2 + enabled: false + - authType: mTLS + required: mandatory + certificates: + - name: mtls-test-configmap + key: tls.crt diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_optional_oauth2_optional.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_optional_oauth2_optional.apk-conf new file mode 100644 index 000000000..57a849c5a --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_optional_oauth2_optional.apk-conf @@ -0,0 +1,36 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-mtls-optional-oauth2-optional" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: false +subscriptionValidation: false +operations: +- target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] +authentication: + - authType: OAuth2 + required: optional + - authType: mTLS + required: optional + certificates: + - name: mtls-test-configmap + key: tls.crt diff --git a/test/cucumber-tests/src/test/resources/artifacts/definitions/student.proto b/test/cucumber-tests/src/test/resources/artifacts/definitions/student.proto new file mode 100644 index 000000000..885c45cfb --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/definitions/student.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "org.example"; +package dineth.grpc.api.v1.student; + +service StudentService { + rpc GetStudent(StudentRequest) returns (StudentResponse) {}; + rpc GetStudentStream(StudentRequest) returns (stream StudentResponse) {}; + rpc SendStudentStream(stream StudentRequest) returns (StudentResponse) {}; + rpc SendAndGetStudentStream(stream StudentRequest) returns (stream StudentResponse) {} +} + +message StudentRequest { + int32 id = 3; +} + +message StudentResponse { + string name = 1; + int32 age = 2; +} diff --git a/test/cucumber-tests/src/test/resources/tests/api/GRPC.feature b/test/cucumber-tests/src/test/resources/tests/api/GRPC.feature new file mode 100644 index 000000000..873ad7c48 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/tests/api/GRPC.feature @@ -0,0 +1,115 @@ +Feature: Generating APK conf for gRPC API + Scenario: Generating APK conf using a valid GRPC API definition + Given The system is ready + When I use the definition file "artifacts/definitions/student.proto" in resources + And generate the APK conf file for a "gRPC" API + Then the response status code should be 200 + + Scenario: Deploying APK conf using a valid gRPC API definition + Given The system is ready + And I have a valid subscription + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | Authorization | bearer ${accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-basic-api" + Then the response status code should be 202 + + Scenario: Checking api-definition endpoint to get proto file + Given The system is ready + And I have a valid subscription + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | Authorization | bearer ${accessToken} | + | Host | default.gw.wso2.com | + And I send "GET" request to "https://default.gw.wso2.com:9095/dineth.grpc.api.v1/api-definition/" with body "" + And I eventually receive 200 response code, not accepting + | 429 | + | 500 | + And the response body should contain endpoint definition for student.proto + + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-basic-api" + Then the response status code should be 202 + + Scenario: Deploying gRPC API with OAuth2 disabled + Given The system is ready + And I have a valid subscription + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc_with_disabled_auth.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-auth-disabled-api" + Then the response status code should be 202 + + Scenario: Deploying gRPC API with scopes + Given The system is ready + And I have a valid subscription + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc_scopes.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | Authorization | bearer ${accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 7 + Given I have a valid subscription with scopes + | wso2 | + Then I set headers + | Authorization | bearer ${accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-scopes" + Then the response status code should be 202 + + Scenario: Deploying gRPC API with default version enabled + Given The system is ready + And I have a valid subscription + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc_with_default_version.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | Authorization | bearer ${accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + Given I have a valid subscription + Then I set headers + | Authorization | bearer ${accessToken} | + And I make grpc request GetStudent default version to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-default-version-api" + Then the response status code should be 202 \ No newline at end of file diff --git a/test/cucumber-tests/src/test/resources/tests/api/GRPCMTLS.feature b/test/cucumber-tests/src/test/resources/tests/api/GRPCMTLS.feature new file mode 100644 index 000000000..87fa0fac0 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/tests/api/GRPCMTLS.feature @@ -0,0 +1,38 @@ +Feature: Test mTLS between client and gateway with client certificate sent in header + Scenario: Test API with mandatory mTLS and OAuth2 disabled + Given The system is ready + And I have a valid token with a client certificate "invalid-cert.txt" + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_disabled.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | X-WSO2-CLIENT-CERTIFICATE | ${clientCertificate} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-mtls-mandatory-oauth2-disabled" + Then the response status code should be 202 + + Scenario: Test optional mTLS and optional OAuth2 with an invalid client certificate and invalid token in header + Given The system is ready + And I have a valid token with a client certificate "invalid-cert.txt" + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc_with_mtls_optional_oauth2_optional.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | X-WSO2-CLIENT-CERTIFICATE | ${clientCertificate} | + | Authorization | bearer {accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 16 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-mtls-optional-oauth2-optional" + Then the response status code should be 202