From 1328082e9da3f2be0868cf5131495d9d6cf961b1 Mon Sep 17 00:00:00 2001 From: DimuthuMadushan Date: Fri, 8 Sep 2023 17:41:14 +0530 Subject: [PATCH] Add integration tests for GraphQL tool --- .../distribution/test/GraphqlToolTest.java | 137 +++++++++++++ .../test/OpenAPIArtifactBuildTest.java | 10 +- .../distribution/utils/TestUtils.java | 33 +++- .../project_1/graphql_endpoint.config.yaml | 5 + .../project_1/query_country.graphql | 5 + .../project_2/graphql_schema.config.yaml | 5 + .../project_2/queries/query_country.graphql | 5 + .../project_2/schema_country.graphql | 101 ++++++++++ .../schema-gen/project_1/Ballerina.toml | 4 + .../graphql/schema-gen/project_1/main.bal | 53 +++++ .../resources/graphql/schema-gen/service.bal | 187 ++++++++++++++++++ .../graphql/service-gen/schema_book.graphql | 13 ++ .../service-gen/schema_starwars.graphql | 112 +++++++++++ ballerina-test/src/test/resources/testng.xml | 1 + gradle.properties | 8 +- 15 files changed, 665 insertions(+), 14 deletions(-) create mode 100644 ballerina-test/src/test/java/org/ballerinalang/distribution/test/GraphqlToolTest.java create mode 100644 ballerina-test/src/test/resources/graphql/client-gen/project_1/graphql_endpoint.config.yaml create mode 100644 ballerina-test/src/test/resources/graphql/client-gen/project_1/query_country.graphql create mode 100644 ballerina-test/src/test/resources/graphql/client-gen/project_2/graphql_schema.config.yaml create mode 100644 ballerina-test/src/test/resources/graphql/client-gen/project_2/queries/query_country.graphql create mode 100644 ballerina-test/src/test/resources/graphql/client-gen/project_2/schema_country.graphql create mode 100644 ballerina-test/src/test/resources/graphql/schema-gen/project_1/Ballerina.toml create mode 100644 ballerina-test/src/test/resources/graphql/schema-gen/project_1/main.bal create mode 100644 ballerina-test/src/test/resources/graphql/schema-gen/service.bal create mode 100644 ballerina-test/src/test/resources/graphql/service-gen/schema_book.graphql create mode 100644 ballerina-test/src/test/resources/graphql/service-gen/schema_starwars.graphql diff --git a/ballerina-test/src/test/java/org/ballerinalang/distribution/test/GraphqlToolTest.java b/ballerina-test/src/test/java/org/ballerinalang/distribution/test/GraphqlToolTest.java new file mode 100644 index 0000000000..60ae15f431 --- /dev/null +++ b/ballerina-test/src/test/java/org/ballerinalang/distribution/test/GraphqlToolTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.ballerinalang.distribution.test; + +import org.ballerinalang.distribution.utils.TestUtils; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.LinkedList; +import java.util.List; + +import static org.ballerinalang.distribution.utils.TestUtils.*; + +public class GraphqlToolTest { + + @BeforeClass + public void setupDistributions() throws IOException { + TestUtils.cleanDistribution(); + TestUtils.prepareDistribution(DISTRIBUTIONS_DIR.resolve(DISTRIBUTION_FILE_NAME + ".zip")); + } + + @Test(description = "Check GraphQL client generation") + public void testGraphqlClientGenerationUsingEndpoint() throws IOException, InterruptedException { + Path testResource = Paths.get("/graphql/client-gen/project_1"); + List buildArgs = new LinkedList<>(); + buildArgs.add("-i"); + buildArgs.add("graphql_endpoint.config.yaml"); + boolean successful = TestUtils.executeGraphql(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource), + buildArgs); + Assert.assertTrue(successful); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("client.bal"))); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("types.bal"))); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("config_types.bal"))); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("utils.bal"))); + TestUtils.deleteGeneratedFiles("client.bal", GRAPHQL_CMD); + } + + @Test(description = "Check GraphQL client generation") + public void testGraphqlClientGenerationUsingSchemaFile() throws IOException, InterruptedException { + Path testResource = Paths.get("/graphql/client-gen/project_2"); + List buildArgs = new LinkedList<>(); + buildArgs.add("-i"); + buildArgs.add("graphql_schema.config.yaml"); + boolean successful = TestUtils.executeGraphql(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource), + buildArgs); + Assert.assertTrue(successful); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("client.bal"))); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("types.bal"))); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("config_types.bal"))); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("utils.bal"))); + TestUtils.deleteGeneratedFiles("client.bal", GRAPHQL_CMD); + } + + @Test(description = "Check GraphQL schema generation") + public void testGraphqlSchemaGeneration() throws IOException, InterruptedException { + Path testResource = Paths.get("/graphql/schema-gen"); + List buildArgs = new LinkedList<>(); + buildArgs.add("-i"); + buildArgs.add("service.bal"); + boolean successful = TestUtils.executeGraphql(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource), + buildArgs); + Assert.assertTrue(successful); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("schema_graphql.graphql"))); + TestUtils.deleteGeneratedFiles("schema_graphql.graphql", GRAPHQL_CMD); + } + + @Test(description = "Check GraphQL schema generation") + public void testGraphqlSchemaGenerationWithServicePathFlag() throws IOException, InterruptedException { + Path testResource = Paths.get("/graphql/schema-gen/project_1"); + List buildArgs = new LinkedList<>(); + buildArgs.add("-i"); + buildArgs.add("main.bal"); + buildArgs.add("-s"); + buildArgs.add("/gql"); + boolean successful = TestUtils.executeGraphql(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource), + buildArgs); + Assert.assertTrue(successful); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("schema_gql.graphql"))); + TestUtils.deleteGeneratedFiles("schema_gql.graphql", GRAPHQL_CMD); + } + + @Test(description = "Check GraphQL service generation") + public void testGraphqlServiceGeneration() throws IOException, InterruptedException { + Path testResource = Paths.get("/graphql/service-gen"); + List buildArgs = new LinkedList<>(); + buildArgs.add("-i"); + buildArgs.add("schema_starwars.graphql"); + boolean successful = TestUtils.executeGraphql(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource), + buildArgs); + Assert.assertTrue(successful); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("service.bal"))); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("types.bal"))); + TestUtils.deleteGeneratedFiles("service.bal", GRAPHQL_CMD); + } + + @Test(description = "Check GraphQL service generation") + public void testGraphqlServiceGenerationWithRecordFlag() throws IOException, InterruptedException { + Path testResource = Paths.get("/graphql/service-gen"); + List buildArgs = new LinkedList<>(); + buildArgs.add("-i"); + buildArgs.add("schema_book.graphql"); + buildArgs.add("-r"); + boolean successful = TestUtils.executeGraphql(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource), + buildArgs); + Assert.assertTrue(successful); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("service.bal"))); + Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("types.bal"))); + TestUtils.deleteGeneratedFiles("service.bal", GRAPHQL_CMD); + } + + @AfterClass + public void cleanUp() throws IOException { + TestUtils.cleanDistribution(); + } +} diff --git a/ballerina-test/src/test/java/org/ballerinalang/distribution/test/OpenAPIArtifactBuildTest.java b/ballerina-test/src/test/java/org/ballerinalang/distribution/test/OpenAPIArtifactBuildTest.java index a334605d23..567b08f2c5 100644 --- a/ballerina-test/src/test/java/org/ballerinalang/distribution/test/OpenAPIArtifactBuildTest.java +++ b/ballerina-test/src/test/java/org/ballerinalang/distribution/test/OpenAPIArtifactBuildTest.java @@ -62,7 +62,7 @@ public void buildOpenAPIToBallerinaTest() throws IOException, InterruptedExcepti Assert.assertTrue(successful); Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("petstore_service.bal"))); Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("client.bal"))); - TestUtils.deleteGeneratedFiles("petstore"); + TestUtils.deleteGeneratedFiles("petstore", OPENAPI_CMD); } @Test(description = "Check openapi to ballerina generator command with service file only.") @@ -78,7 +78,7 @@ public void buildOpenAPIToBallerinaServiceFileGenerationTest() throws IOExceptio buildArgs); Assert.assertTrue(successful); Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("petstore_service.bal"))); - TestUtils.deleteGeneratedFiles("petstore"); + TestUtils.deleteGeneratedFiles("petstore", OPENAPI_CMD); } @Test(description = "Check openapi to ballerina generator command for given tags") @@ -114,7 +114,7 @@ public void buildOpenAPIToBallerinaWithFilterTagsTest() throws IOException, Assert.fail("Expected content and actual generated content is mismatched for: petstoreTags.yaml"); } //Clean the generated files - TestUtils.deleteGeneratedFiles("petstoretags"); + TestUtils.deleteGeneratedFiles("petstoretags", OPENAPI_CMD); } } @@ -149,7 +149,7 @@ public void buildOpenAPIToBallerinaClientGenerationTests() throws IOException, Assert.assertEquals(expectedTypes, generatedTypes); Assert.assertEquals(expectedUtils, generatedUtils); - TestUtils.deleteGeneratedFiles("client.bal"); + TestUtils.deleteGeneratedFiles("client.bal", OPENAPI_CMD); } else { Assert.fail("Client generation failed"); } @@ -165,7 +165,7 @@ public void buildBallerinaToOpenAPITest() throws IOException, InterruptedExcepti buildArgs); Assert.assertTrue(successful); Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("hello_openapi.yaml"))); - TestUtils.deleteGeneratedFiles("hello_openapi.yaml"); + TestUtils.deleteGeneratedFiles("hello_openapi.yaml", OPENAPI_CMD); } //OpenAPI integration tests diff --git a/ballerina-test/src/test/java/org/ballerinalang/distribution/utils/TestUtils.java b/ballerina-test/src/test/java/org/ballerinalang/distribution/utils/TestUtils.java index 868009b26e..32ae92e958 100644 --- a/ballerina-test/src/test/java/org/ballerinalang/distribution/utils/TestUtils.java +++ b/ballerina-test/src/test/java/org/ballerinalang/distribution/utils/TestUtils.java @@ -61,6 +61,8 @@ public class TestUtils { public static final String WHITESPACE_PATTERN = "\\s+"; private static String balFile = "bal"; public static final String DISTRIBUTION_FILE_NAME = "ballerina-" + MAVEN_VERSION + "-" + CODE_NAME; + public static final String OPENAPI_CMD = "openapi"; + public static final String GRAPHQL_CMD = "graphql"; /** * Log the output of an input stream. @@ -149,7 +151,27 @@ public static InputStream executeGrpcCommand(String distributionName, Path sourc */ public static boolean executeOpenAPI(String distributionName, Path sourceDirectory, List args) throws IOException, InterruptedException { - args.add(0, "openapi"); + args.add(0, OPENAPI_CMD); + Process process = getProcessBuilderResults(distributionName, sourceDirectory, args); + int exitCode = process.waitFor(); + logOutput(process.getInputStream()); + logOutput(process.getErrorStream()); + return exitCode == 0; + } + + /** + * Execute ballerina graphql command. + * + * @param distributionName The name of the distribution. + * @param sourceDirectory The directory where the sources files are location. + * @param args The arguments to be passed to the build command. + * @return True if build is successful, else false. + * @throws IOException Error executing build command. + * @throws InterruptedException Interrupted error executing build command. + */ + public static boolean executeGraphql(String distributionName, Path sourceDirectory, List args) throws + IOException, InterruptedException { + args.add(0, GRAPHQL_CMD); Process process = getProcessBuilderResults(distributionName, sourceDirectory, args); int exitCode = process.waitFor(); logOutput(process.getInputStream()); @@ -227,19 +249,20 @@ public static String findFileOrDirectory(Path dir, String dirName) { } /** - * Delete openapi generated files. + * Delete openapi and graphql generated files. * * @param generatedFileName file name that need to delete. */ - public static void deleteGeneratedFiles(String generatedFileName) throws IOException { - Path resourcesPath = RESOURCES_PATH.resolve("openapi"); + public static void deleteGeneratedFiles(String generatedFileName, String command) throws IOException { + Path resourcesPath = RESOURCES_PATH.resolve(command); if (Files.exists(resourcesPath)) { List listFiles = Arrays.asList( Objects.requireNonNull(new File(String.valueOf(resourcesPath)).listFiles())); for (File existsFile: listFiles) { String fileName = existsFile.getName(); if (fileName.equals(generatedFileName) || fileName.equals(generatedFileName + "_service.bal") || - fileName.equals("client.bal") || fileName.equals("types.bal") || fileName.equals("utils.bal")) { + fileName.equals("client.bal") || fileName.equals("types.bal") || fileName.equals("utils.bal") || + fileName.equals("config_types.bal")) { existsFile.delete(); } } diff --git a/ballerina-test/src/test/resources/graphql/client-gen/project_1/graphql_endpoint.config.yaml b/ballerina-test/src/test/resources/graphql/client-gen/project_1/graphql_endpoint.config.yaml new file mode 100644 index 0000000000..3d21dbc475 --- /dev/null +++ b/ballerina-test/src/test/resources/graphql/client-gen/project_1/graphql_endpoint.config.yaml @@ -0,0 +1,5 @@ +## The GraphQL schema. E.g., https://countries.trevorblades.com or ./schemas/country.graphql +schema: https://countries.trevorblades.com +## The GraphQL documents that have queries/mutations +documents: + - ./query_country.graphql diff --git a/ballerina-test/src/test/resources/graphql/client-gen/project_1/query_country.graphql b/ballerina-test/src/test/resources/graphql/client-gen/project_1/query_country.graphql new file mode 100644 index 0000000000..4f694509a2 --- /dev/null +++ b/ballerina-test/src/test/resources/graphql/client-gen/project_1/query_country.graphql @@ -0,0 +1,5 @@ +query countryByCode($code: ID!) { + country(code: $code) { + name + } +} diff --git a/ballerina-test/src/test/resources/graphql/client-gen/project_2/graphql_schema.config.yaml b/ballerina-test/src/test/resources/graphql/client-gen/project_2/graphql_schema.config.yaml new file mode 100644 index 0000000000..53fe989914 --- /dev/null +++ b/ballerina-test/src/test/resources/graphql/client-gen/project_2/graphql_schema.config.yaml @@ -0,0 +1,5 @@ +## The GraphQL schema. E.g., https://countries.trevorblades.com or ./schemas/country.graphql +schema: ./schema_country.graphql +## The GraphQL documents that have queries/mutations +documents: + - ./queries/query_country.graphql diff --git a/ballerina-test/src/test/resources/graphql/client-gen/project_2/queries/query_country.graphql b/ballerina-test/src/test/resources/graphql/client-gen/project_2/queries/query_country.graphql new file mode 100644 index 0000000000..4f694509a2 --- /dev/null +++ b/ballerina-test/src/test/resources/graphql/client-gen/project_2/queries/query_country.graphql @@ -0,0 +1,5 @@ +query countryByCode($code: ID!) { + country(code: $code) { + name + } +} diff --git a/ballerina-test/src/test/resources/graphql/client-gen/project_2/schema_country.graphql b/ballerina-test/src/test/resources/graphql/client-gen/project_2/schema_country.graphql new file mode 100644 index 0000000000..7f2aa1f4e6 --- /dev/null +++ b/ballerina-test/src/test/resources/graphql/client-gen/project_2/schema_country.graphql @@ -0,0 +1,101 @@ +schema { + query: Query +} + +"Directs the executor to include this field or fragment only when the `if` argument is true" +directive @include( + "Included when true." + if: Boolean! +) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +"Directs the executor to skip this field or fragment when the `if`'argument is true." +directive @skip( + "Skipped when true." + if: Boolean! +) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +directive @cacheControl(maxAge: Int, scope: CacheControlScope) on OBJECT | FIELD_DEFINITION | INTERFACE + +"Marks the field, argument, input field or enum value as deprecated" +directive @deprecated( + "The reason for the deprecation" + reason: String = "No longer supported" +) on FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION + +"Exposes a URL that specifies the behaviour of this scalar." +directive @specifiedBy( + "The URL that specifies the behaviour of this scalar." + url: String! +) on SCALAR + +type Continent { + code: ID! + countries: [Country!]! + name: String! +} + +type Country { + capital: String + code: ID! + continent: Continent! + currency: String + emoji: String! + emojiU: String! + languages: [Language!]! + name: String! + native: String! + phone: String! + states: [State!]! +} + +type Language { + code: ID! + name: String + native: String + rtl: Boolean! +} + +type Query { + continent(code: ID!): Continent + continents(filter: ContinentFilterInput): [Continent!]! + countries(filter: CountryFilterInput): [Country!]! + country(code: ID!): Country + language(code: ID!): Language + languages(filter: LanguageFilterInput): [Language!]! +} + +type State { + code: String + country: Country! + name: String! +} + +enum CacheControlScope { + PRIVATE + PUBLIC +} + +"The `Upload` scalar type represents a file upload." +scalar Upload + +input ContinentFilterInput { + code: StringQueryOperatorInput +} + +input CountryFilterInput { + code: StringQueryOperatorInput + continent: StringQueryOperatorInput + currency: StringQueryOperatorInput +} + +input LanguageFilterInput { + code: StringQueryOperatorInput +} + +input StringQueryOperatorInput { + eq: String + in: [String!] + ne: String + nin: [String!] + regex: String +} diff --git a/ballerina-test/src/test/resources/graphql/schema-gen/project_1/Ballerina.toml b/ballerina-test/src/test/resources/graphql/schema-gen/project_1/Ballerina.toml new file mode 100644 index 0000000000..fcbb9d14c0 --- /dev/null +++ b/ballerina-test/src/test/resources/graphql/schema-gen/project_1/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "ballerina_sdl_file_generator_test" +name = "project_1" +version = "0.1.0" diff --git a/ballerina-test/src/test/resources/graphql/schema-gen/project_1/main.bal b/ballerina-test/src/test/resources/graphql/schema-gen/project_1/main.bal new file mode 100644 index 0000000000..e741fb6a44 --- /dev/null +++ b/ballerina-test/src/test/resources/graphql/schema-gen/project_1/main.bal @@ -0,0 +1,53 @@ +// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.org). All Rights Reserved. +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/graphql; + +public type Person record {| + string name; + int age; +|}; + +service /'service/gql on new graphql:Listener(4000) { + resource function get profile() returns Person { + return { + name: "Walter White", + age: 52 + }; + } +} + +service /gql on new graphql:Listener(4001) { + resource function get school() returns School { + return new; + } +} + +service /'service/gql/'new on new graphql:Listener(4002) { + resource function get name() returns string { + return "Ballerina"; + } + + resource function get age() returns int { + return 27; + } +} + +service class School { + resource function get name(string name) returns string { + return name; + } +} diff --git a/ballerina-test/src/test/resources/graphql/schema-gen/service.bal b/ballerina-test/src/test/resources/graphql/schema-gen/service.bal new file mode 100644 index 0000000000..e21566582c --- /dev/null +++ b/ballerina-test/src/test/resources/graphql/schema-gen/service.bal @@ -0,0 +1,187 @@ +// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.org). All Rights Reserved. +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/graphql; + +# Episodes of Starwars Series +public enum Episode { + # The episode new hope. + # `Luke Skywalker` joins forces with a Jedi Knight. + # + # Check new lines in the documentation + # + NEWHOPE, + # + EMPIRE, + # The episode jedi + JEDI +} + +# +# + stars - Number of stars +# + episode - The episode +public type Review record { + int stars; + Episode episode?; + string commentary?; +}; + +# The review input type +public type ReviewInput record { + # Number of stars + int stars; + string commentary; +}; + +public type Profile distinct service object { + # The name of the human + # + id - + # + age - + # This should be an integer + # + # This is a Non-Null type argument + # + return - The name + resource function get name(string id, int age, boolean isAdult) returns string; +}; + +# Planet of the Human +public type Planet distinct service object { + # The home planet of the human, or null if unknown + # + return - The homePlanet + resource function get homePlanet() returns string?; +}; + +# A mechanical character from the Star Wars universe. +# It can be a **Human** or a ~Droid~ +public type Character distinct service object { + *Profile; + + # The unique identifier of the character + # + return - The id + resource function get id() returns string; + + # The episodes this character appears in + # + return - The episodes + # # Deprecated + # This field is deprecated. + # Use `appears` field instead of this. + @deprecated + resource function get appearsIn() returns Episode[]; +}; + +# A humanoid creature from the Star Wars universe +distinct service class Human { + *Character; + *Planet; + # The unique identifier of the human + # The type of the id is String + # + # The "id" returns a string + # + return - The id + resource function get id() returns string { + return ""; + } + + # The name of the human + # + return - The name + resource function get name(string id, int age, boolean isAdult) returns string { + return ""; + } + + # The home planet of the human, or null if unknown + # + return - The homePlanet + resource function get homePlanet() returns string? { + return; + } + + # Height in meters, or null if unknown + # + return - The height + resource function get height() returns float? { + return ; + } + + # Mass in kilograms, or null if unknown + # + return - The mass + resource function get mass() returns int? { + return ; + } + + # The episodes this human appears in + # + return - The episodes + resource function get appearsIn() returns Episode[] { + return [JEDI]; + } +} + +service /graphql on new graphql:Listener(9000) { + + # Fetch the hero of the Star Wars + # + episode - The episode which hero appears + # This is a Nullable input + # + # + return - The hero + resource function get hero(Episode? episode) returns Profile { + return new Human(); + } + + # Returns reviews of the Star Wars + # + episode - The episode + # Default value of the `episode` is ""JEDI"" + # + return - The reviews + resource function get reviews(Episode episode = JEDI, string name = "Luke") returns Review?[] { + return []; + } + + # Returns characters by id, or null if character is not found + # + name - Name of the character + # + # + return - The characters + resource function get characters(string[] idList, string name) returns Character?[] { + Character[] characters = [new Human()]; + return characters; + } + + # Returns a human by id, + # or null if human is not found + # + id - **id** of the human + # + return - The Human + resource function get human(string id) returns Human? { + if id.includes("human") { + return new Human(); + } + return; + } + + # The home planet of the human, or null if unknown + # + return - The homePlanet + resource function get planet() returns Planet? { + return; + } + + # Add new reviews. + # Return the updated review values + # + episode - Episode name + # + reviewInput - Review of the episode. + # + # This should be an `input object` type value + # + return - The reviews + remote function createReview(Episode episode, ReviewInput reviewInput) returns Review { + Review review = { + stars: reviewInput.stars + }; + return review; + } +} diff --git a/ballerina-test/src/test/resources/graphql/service-gen/schema_book.graphql b/ballerina-test/src/test/resources/graphql/service-gen/schema_book.graphql new file mode 100644 index 0000000000..1f824059a5 --- /dev/null +++ b/ballerina-test/src/test/resources/graphql/service-gen/schema_book.graphql @@ -0,0 +1,13 @@ +type Query { + "Fetch all the books from database" + books: [Book] + "Fetch a book by its id" + book( + id: Int! + ): Book +} + +type Book { + id: Int! + title: String! +} diff --git a/ballerina-test/src/test/resources/graphql/service-gen/schema_starwars.graphql b/ballerina-test/src/test/resources/graphql/service-gen/schema_starwars.graphql new file mode 100644 index 0000000000..5f7047c797 --- /dev/null +++ b/ballerina-test/src/test/resources/graphql/service-gen/schema_starwars.graphql @@ -0,0 +1,112 @@ +type Query { + "Fetch the hero of the Star Wars" + hero(episode: Episode): Character! + "Returns reviews of the Star Wars" + reviews(episode: Episode!): [Review]! + "Returns characters by id, or null if character is not found" + characters(idList: [String!]!): [Character]! + "Returns a droid by id, or null if droid is not found" + droid(id: String! = ""): Droid + "Returns a human by id, or null if human is not found" + human(id: String!): Human + "Returns a starship by id, or null if starship is not found" + starship(id: String!): Starship + "Returns search results by text, or null if search item is not found" + search(text: String!): [SearchResult!] +} + +"A mechanical character from the Star Wars universe" +interface Character { + "The unique identifier of the character" + id: String! + "The name of the character" + name: String! + "This character's friends, or an empty list if they have none" + friends: [Character!]! + "The episodes this character appears in" + appearsIn: [Episode!]! +} + +"A humanoid creature from the Star Wars universe" +type Human implements Character { + "The unique identifier of the human" + id: String! + "The name of the human" + name: String! + "The home planet of the human, or null if unknown" + homePlanet: String + "Height in meters, or null if unknown" + height: Float + "Mass in kilograms, or null if unknown" + mass: Int + "This human's friends, or an empty list if they have none" + friends: [Character!]! + "The episodes this human appears in" + appearsIn: [Episode!]! + "A list of starships this person has piloted, or an empty list if none" + starships: [Starship!]! +} + +enum Episode { + JEDI + EMPIRE + NEWHOPE +} + +"A ship from the Star Wars universe" +type Starship { + "The unique identifier of the starship" + id: String! + "The name of the starship" + name: String! + "The length of the starship, or null if unknown" + length: Float + "Cordinates of the starship, or null if unknown" + cordinates: [[Float!]!] +} + +"An autonomous mechanical character in the Star Wars universe" +type Droid implements Character { + "The unique identifier of the droid" + id: String! + "The name of the droid" + name: String! + "This droid's friends, or an empty list if they have none" + friends: [Character!]! + "The episodes this droid appears in" + appearsIn: [Episode!]! + "This droid's primary function" + primaryFunction: String +} + +type Review { + episode: Episode! + stars: Int! + commentary: String +} + +"auto-generated union type from Ballerina" +union SearchResult = Human|Droid|Starship + +type Mutation { + "Add new reviews and return the review values" + createReview( + "Episode name" + episode: Episode! + "Review of the episode" + reviewInput: ReviewInput! + ): Review! +} + +input ReviewInput { + stars: Int! + commentary: String +} + +type Subscription { + "Subscribe to review updates" + reviewAdded( + "Episode name" + episode: Episode! + ): Review! +} diff --git a/ballerina-test/src/test/resources/testng.xml b/ballerina-test/src/test/resources/testng.xml index e92d6ea213..901e246efb 100644 --- a/ballerina-test/src/test/resources/testng.xml +++ b/ballerina-test/src/test/resources/testng.xml @@ -26,6 +26,7 @@ + diff --git a/gradle.properties b/gradle.properties index 608e62837e..af56f65cf6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -64,7 +64,7 @@ stdlibWebsubVersion=2.10.0-20230831-211200-c6f33c4 stdlibWebsubhubVersion=1.10.0-20230831-210000-695f161 # Stdlib Level 07 -stdlibGraphqlVersion=1.10.0-20230831-232100-bb5b601 +stdlibGraphqlVersion=1.10.0-20230905-115100-6f56f9a stdlibSqlVersion=1.11.0-20230831-224400-d2491ed # Stdlib Level 08 @@ -74,17 +74,17 @@ stdlibPersistVersion=1.2.0-20230831-142100-b7b968f persistToolVersion=1.2.0-20230901-122800-012fa8e # Dev Tools -devToolsVersion=1.2.1-20230731-195900-6f87361 +devToolsVersion=1.2.1-20230905-180400-08d6048 ballerinaCommandVersion=1.4.0 # GraphQL Tool -graphqlVersion=0.8.0-20230901-070600-c734e6a +graphqlVersion=0.8.0-20230907-131600-837b3db # Protoc Tool protocToolVersion=0.2.0-20230821-121200-8933a55 # OpenAPI Module -openapiVersion=1.8.0-20230901-110600-c7d4d0e +openapiVersion=1.8.0-20230901-175200-ccab317 # AsyncAPI Module asyncapiVersion=0.6.0