diff --git a/graphql-cli/src/test/java/io/ballerina/graphql/generator/ballerina/ServiceGeneratorTest.java b/graphql-cli/src/test/java/io/ballerina/graphql/generator/ballerina/ServiceGeneratorTest.java index 2bfaa69d..1cccf98b 100644 --- a/graphql-cli/src/test/java/io/ballerina/graphql/generator/ballerina/ServiceGeneratorTest.java +++ b/graphql-cli/src/test/java/io/ballerina/graphql/generator/ballerina/ServiceGeneratorTest.java @@ -98,4 +98,33 @@ public void testGenerateServiceForSchemaWithDocumentationInResolverFunctions() { Assert.fail(e.getMessage()); } } + + @Test + public void testGenerateServiceForSchemaWithIDTypes() { + String fileName = "SchemaWithIDTypeApi"; + String expectedFile = "serviceForSchemaWithIDTypeApi.bal"; + try { + GraphqlServiceProject project = TestUtils.getValidatedMockServiceProject( + this.resourceDir.resolve(Paths.get("serviceGen", "graphqlSchemas", "valid", fileName + ".graphql")) + .toString(), this.tmpDir); + GraphQLSchema graphQLSchema = project.getGraphQLSchema(); + + ServiceTypesGenerator serviceTypesGenerator = new ServiceTypesGenerator(); + serviceTypesGenerator.setFileName(fileName); + serviceTypesGenerator.generateSrc(graphQLSchema); + + ServiceGenerator serviceGenerator = new ServiceGenerator(); + serviceGenerator.setFileName(fileName); + serviceGenerator.setMethodDeclarations(serviceTypesGenerator.getServiceMethodDeclarations()); + String generatedServiceContent = serviceGenerator.generateSrc(); + writeContentTo(generatedServiceContent, this.tmpDir, SERVICE_FILE_NAME); + Path expectedServiceFile = resourceDir.resolve(Paths.get("serviceGen", "expectedServices", expectedFile)); + String expectedServiceContent = readContentWithFormat(expectedServiceFile); + String writtenServiceTypesContent = + readContentWithFormat(this.tmpDir.resolve("service.bal")); + Assert.assertEquals(expectedServiceContent, writtenServiceTypesContent); + } catch (ServiceGenerationException | IOException | ValidationException e) { + Assert.fail(e.getMessage()); + } + } } diff --git a/graphql-cli/src/test/java/io/ballerina/graphql/generator/ballerina/ServiceTypesGeneratorTest.java b/graphql-cli/src/test/java/io/ballerina/graphql/generator/ballerina/ServiceTypesGeneratorTest.java index 96732bce..10228e73 100644 --- a/graphql-cli/src/test/java/io/ballerina/graphql/generator/ballerina/ServiceTypesGeneratorTest.java +++ b/graphql-cli/src/test/java/io/ballerina/graphql/generator/ballerina/ServiceTypesGeneratorTest.java @@ -789,6 +789,32 @@ public void testGenerateSrcForSchemaWithInputTypeFieldsHavingDefaultValues() { } } + @Test(groups = {"service-type-for-objects"}) + public void testGenerateSrcForSchemaWithGraphQLIDType() { + String fileName = "SchemaWithIDTypeApi"; + String expectedFile = "typesWithIDTypeRecordsAllowed.bal"; + try { + GraphqlServiceProject project = TestUtils.getValidatedMockServiceProject( + this.resourceDir.resolve(Paths.get("serviceGen", "graphqlSchemas", "valid", fileName + ".graphql")) + .toString(), this.tmpDir); + GraphQLSchema graphQLSchema = project.getGraphQLSchema(); + + ServiceTypesGenerator serviceTypesGenerator = new ServiceTypesGenerator(); + serviceTypesGenerator.setFileName(fileName); + serviceTypesGenerator.setUseRecordsForObjects(true); + String generatedServiceTypesContent = serviceTypesGenerator.generateSrc(graphQLSchema); + writeContentTo(generatedServiceTypesContent, this.tmpDir, TYPES_FILE_NAME); + + Path expectedServiceTypesFile = + resourceDir.resolve(Paths.get("serviceGen", "expectedServices", expectedFile)); + String expectedServiceTypesContent = readContentWithFormat(expectedServiceTypesFile); + String writtenServiceTypesContent = readContentWithFormat(this.tmpDir.resolve("types.bal")); + Assert.assertEquals(expectedServiceTypesContent, writtenServiceTypesContent); + } catch (ValidationException | IOException | ServiceGenerationException e) { + Assert.fail(e.getMessage()); + } + } + @DataProvider(name = "invalidSchemasWithExpectedErrorMessages") public Object[][] getInvalidSchemasWithExpectedErrorMessages() { return new Object[][]{ diff --git a/graphql-cli/src/test/resources/serviceGen/expectedServices/serviceForSchemaWithIDTypeApi.bal b/graphql-cli/src/test/resources/serviceGen/expectedServices/serviceForSchemaWithIDTypeApi.bal new file mode 100644 index 00000000..1dcdaf9a --- /dev/null +++ b/graphql-cli/src/test/resources/serviceGen/expectedServices/serviceForSchemaWithIDTypeApi.bal @@ -0,0 +1,15 @@ +import ballerina/graphql; + +configurable int port = 9090; + +service SchemaWithIDTypeApi on new graphql:Listener(port) { + # Fetch a book by its id + # + id - The id of the book to fetch + resource function get book(@graphql:ID string id) returns Book? { + } + + # Fetch a list of books by their ids + # + ids - The list of book ids to fetch + resource function get books(@graphql:ID string[] ids) returns Book[] { + } +} diff --git a/graphql-cli/src/test/resources/serviceGen/expectedServices/typesWithIDTypeRecordsAllowed.bal b/graphql-cli/src/test/resources/serviceGen/expectedServices/typesWithIDTypeRecordsAllowed.bal new file mode 100644 index 00000000..8b414b3e --- /dev/null +++ b/graphql-cli/src/test/resources/serviceGen/expectedServices/typesWithIDTypeRecordsAllowed.bal @@ -0,0 +1,20 @@ +import ballerina/graphql; + +type SchemaWithIDTypeApi service object { + *graphql:Service; + # Fetch a book by its id + # + id - The id of the book to fetch + resource function get book(@graphql:ID string id) returns Book?; + # Fetch a list of books by their ids + # + ids - The list of book ids to fetch + resource function get books(@graphql:ID string[] ids) returns Book[]; +}; + +# Represents a book written by an author +public type Book record {| + # The id of the book, unique identifier + @graphql:ID + string id; + # The title of the book + string title; +|}; diff --git a/graphql-cli/src/test/resources/serviceGen/expectedServices/typesWithMutationDefault.bal b/graphql-cli/src/test/resources/serviceGen/expectedServices/typesWithMutationDefault.bal index dc59263b..247bd9dd 100644 --- a/graphql-cli/src/test/resources/serviceGen/expectedServices/typesWithMutationDefault.bal +++ b/graphql-cli/src/test/resources/serviceGen/expectedServices/typesWithMutationDefault.bal @@ -9,7 +9,7 @@ type SchemaWithMutationApi service object { }; public distinct service class Author { - resource function get id() returns string { + resource function get id() returns @graphql:ID string { } resource function get name() returns string { diff --git a/graphql-cli/src/test/resources/serviceGen/graphqlSchemas/valid/SchemaWithIDTypeApi.graphql b/graphql-cli/src/test/resources/serviceGen/graphqlSchemas/valid/SchemaWithIDTypeApi.graphql new file mode 100644 index 00000000..c852b271 --- /dev/null +++ b/graphql-cli/src/test/resources/serviceGen/graphqlSchemas/valid/SchemaWithIDTypeApi.graphql @@ -0,0 +1,21 @@ +type Query { + "Fetch a book by its id" + book( + "The id of the book to fetch" + id: ID! + ): Book + + "Fetch a list of books by their ids" + books( + "The list of book ids to fetch" + ids: [ID!]! + ): [Book!]! +} + +"Represents a book written by an author" +type Book { + "The id of the book, unique identifier" + id: ID! + "The title of the book" + title: String! +} diff --git a/graphql-code-generator/src/main/java/io/ballerina/graphql/generator/service/generator/ServiceTypesGenerator.java b/graphql-code-generator/src/main/java/io/ballerina/graphql/generator/service/generator/ServiceTypesGenerator.java index fa7ddb23..21642481 100644 --- a/graphql-code-generator/src/main/java/io/ballerina/graphql/generator/service/generator/ServiceTypesGenerator.java +++ b/graphql-code-generator/src/main/java/io/ballerina/graphql/generator/service/generator/ServiceTypesGenerator.java @@ -450,17 +450,17 @@ private NodeList generateRecordTypeFieldsForGraphQLInputObjectFields( List inputTypeFields) throws ServiceGenerationException { List fields = new ArrayList<>(); for (GraphQLInputObjectField field : inputTypeFields) { + MetadataNode metadataNode = getMetadataNode(getUnwrappedType(field.getType()), field.getDescription(), + field.isDeprecated(), field.getDeprecationReason()); if (field.hasSetDefaultValue()) { Object value = field.getInputFieldDefaultValue().getValue(); ExpressionNode generatedDefaultValue = generateArgDefaultValue(value); - fields.add(createRecordFieldWithDefaultValueNode( - generateMetadata(field.getDescription(), null, field.isDeprecated(), - field.getDeprecationReason(), false), null, generateTypeDescriptor(field.getType()), + fields.add(createRecordFieldWithDefaultValueNode(metadataNode, null, + generateTypeDescriptor(field.getType()), createIdentifierToken(field.getName()), createToken(SyntaxKind.EQUAL_TOKEN), generatedDefaultValue, createToken(SyntaxKind.SEMICOLON_TOKEN))); } else { - fields.add(createRecordFieldNode(generateMetadata(field.getDescription(), null, field.isDeprecated(), - field.getDeprecationReason(), false), null, generateTypeDescriptor(field.getType()), + fields.add(createRecordFieldNode(metadataNode, null, generateTypeDescriptor(field.getType()), createIdentifierToken(field.getName()), null, createToken(SyntaxKind.SEMICOLON_TOKEN))); } } @@ -472,8 +472,9 @@ private NodeList generateRecordTypeFieldsForGraphQLFieldDefinitions( List fields = new ArrayList<>(); for (GraphQLFieldDefinition field : typeInputFields) { fields.add(createRecordFieldNode( - generateMetadata(field.getDescription(), field.getArguments(), field.isDeprecated(), - field.getDeprecationReason(), false), null, generateTypeDescriptor(field.getType()), + getMetadataNode(getUnwrappedType(field.getType()), field.getDescription(), field.isDeprecated(), + field.getDeprecationReason()), + null, generateTypeDescriptor(field.getType()), createIdentifierToken(field.getName()), null, createToken(SyntaxKind.SEMICOLON_TOKEN))); } return createNodeList(fields); @@ -806,10 +807,10 @@ private ReturnTypeDescriptorNode generateMethodSignatureReturnType(GraphQLOutput createStreamTypeDescriptorNode(createToken(SyntaxKind.STREAM_KEYWORD), createStreamTypeParamsNode(createToken(SyntaxKind.LT_TOKEN), typeDescriptor, null, null, createToken(SyntaxKind.GT_TOKEN))); - return createReturnTypeDescriptorNode(createToken(SyntaxKind.RETURNS_KEYWORD), createEmptyNodeList(), + return createReturnTypeDescriptorNode(createToken(SyntaxKind.RETURNS_KEYWORD), getAnnotationNodeList(type), streamTypeDescriptor); } else { - return createReturnTypeDescriptorNode(createToken(SyntaxKind.RETURNS_KEYWORD), createEmptyNodeList(), + return createReturnTypeDescriptorNode(createToken(SyntaxKind.RETURNS_KEYWORD), getAnnotationNodeList(type), typeDescriptor); } } @@ -831,13 +832,13 @@ private SeparatedNodeList generateMethodSignatureParams(List getAnnotationNodeList(GraphQLType graphQLType) { + GraphQLType unWrappedType = getUnwrappedType(graphQLType); + if (unWrappedType instanceof GraphQLScalarType + && ((GraphQLScalarType) unWrappedType).getName().equals(CodeGeneratorConstants.GRAPHQL_ID_TYPE)) { + return createNodeList( + createAnnotationNode( + createToken(SyntaxKind.AT_TOKEN), + createQualifiedNameReferenceNode( + createIdentifierToken(CodeGeneratorConstants.GRAPHQL), + createToken(SyntaxKind.COLON_TOKEN), + createIdentifierToken(CodeGeneratorConstants.GRAPHQL_ID_TYPE) + ), + null + ) + ); + } else { + return createEmptyNodeList(); + } + } + }