Skip to content

Commit

Permalink
Implement service generation with @graphql:ID annotation in service f…
Browse files Browse the repository at this point in the history
…unctions
  • Loading branch information
Ishad-M-I-M committed Oct 25, 2024
1 parent c36f23d commit 804f9de
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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[][]{
Expand Down
Original file line number Diff line number Diff line change
@@ -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[] {
}
}
Original file line number Diff line number Diff line change
@@ -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;
|};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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!
}

Original file line number Diff line number Diff line change
Expand Up @@ -450,27 +450,8 @@ private NodeList<Node> generateRecordTypeFieldsForGraphQLInputObjectFields(
List<GraphQLInputObjectField> inputTypeFields) throws ServiceGenerationException {
List<Node> fields = new ArrayList<>();
for (GraphQLInputObjectField field : inputTypeFields) {
MetadataNode metadataNode;
if (CodeGeneratorConstants.GRAPHQL_ID_TYPE.equals(field.getType())) {
metadataNode = createMetadataNode(
null,
createNodeList(
createAnnotationNode(
createToken(SyntaxKind.AT_TOKEN),
createQualifiedNameReferenceNode(
createIdentifierToken(CodeGeneratorConstants.GRAPHQL),
createToken(SyntaxKind.COLON_TOKEN),
createIdentifierToken(CodeGeneratorConstants.GRAPHQL_ID_TYPE)
),
null
)
)
);
} else {
metadataNode = generateMetadata(field.getDescription(), null, field.isDeprecated(),
field.getDeprecationReason(), false);
}

MetadataNode metadataNode = getMetadataNode(field.getType(), field.getDescription(),
field.isDeprecated(), field.getDeprecationReason());
if (field.hasSetDefaultValue()) {
Object value = field.getInputFieldDefaultValue().getValue();
ExpressionNode generatedDefaultValue = generateArgDefaultValue(value);
Expand All @@ -490,28 +471,10 @@ private NodeList<Node> generateRecordTypeFieldsForGraphQLFieldDefinitions(
List<GraphQLFieldDefinition> typeInputFields) throws ServiceGenerationException {
List<Node> fields = new ArrayList<>();
for (GraphQLFieldDefinition field : typeInputFields) {
MetadataNode metadataNode;
if (CodeGeneratorConstants.GRAPHQL_ID_TYPE.equals(field.getType())) {
metadataNode = createMetadataNode(
null,
createNodeList(
createAnnotationNode(
createToken(SyntaxKind.AT_TOKEN),
createQualifiedNameReferenceNode(
createIdentifierToken(CodeGeneratorConstants.GRAPHQL),
createToken(SyntaxKind.COLON_TOKEN),
createIdentifierToken(CodeGeneratorConstants.GRAPHQL_ID_TYPE)
),
null
)
)
);
} else {
metadataNode = generateMetadata(field.getDescription(), null, field.isDeprecated(),
field.getDeprecationReason(), false);
}
fields.add(createRecordFieldNode(
metadataNode, null, generateTypeDescriptor(field.getType()),
getMetadataNode(field.getType(), field.getDescription(), field.isDeprecated(),
field.getDeprecationReason()),
null, generateTypeDescriptor(field.getType()),
createIdentifierToken(field.getName()), null, createToken(SyntaxKind.SEMICOLON_TOKEN)));
}
return createNodeList(fields);
Expand Down Expand Up @@ -839,37 +802,15 @@ private MarkdownDocumentationLineNode generateMarkdownDocumentationLine(String d
private ReturnTypeDescriptorNode generateMethodSignatureReturnType(GraphQLOutputType type, boolean isStream)
throws ServiceGenerationException {
TypeDescriptorNode typeDescriptor = generateTypeDescriptor(type);
GraphQLType unWrappedType = type;
if (type instanceof GraphQLNonNull) {
unWrappedType = ((GraphQLNonNull) type).getWrappedType();
}

NodeList<AnnotationNode> nodeList;
if (unWrappedType instanceof GraphQLScalarType
&& ((GraphQLScalarType) unWrappedType).getName().equals(CodeGeneratorConstants.GRAPHQL_ID_TYPE)) {
nodeList = createNodeList(
createAnnotationNode(
createToken(SyntaxKind.AT_TOKEN),
createQualifiedNameReferenceNode(
createIdentifierToken(CodeGeneratorConstants.GRAPHQL),
createToken(SyntaxKind.COLON_TOKEN),
createIdentifierToken(CodeGeneratorConstants.GRAPHQL_ID_TYPE)
),
null
)
);
} else {
nodeList = createEmptyNodeList();
}
if (isStream) {
StreamTypeDescriptorNode streamTypeDescriptor =
createStreamTypeDescriptorNode(createToken(SyntaxKind.STREAM_KEYWORD),
createStreamTypeParamsNode(createToken(SyntaxKind.LT_TOKEN), typeDescriptor, null, null,
createToken(SyntaxKind.GT_TOKEN)));
return createReturnTypeDescriptorNode(createToken(SyntaxKind.RETURNS_KEYWORD), nodeList,
return createReturnTypeDescriptorNode(createToken(SyntaxKind.RETURNS_KEYWORD), getAnnotationNodeList(type),
streamTypeDescriptor);
} else {
return createReturnTypeDescriptorNode(createToken(SyntaxKind.RETURNS_KEYWORD), nodeList,
return createReturnTypeDescriptorNode(createToken(SyntaxKind.RETURNS_KEYWORD), getAnnotationNodeList(type),
typeDescriptor);
}
}
Expand All @@ -891,13 +832,13 @@ private SeparatedNodeList<ParameterNode> generateMethodSignatureParams(List<Grap
Object value = argument.getArgumentDefaultValue().getValue();
ExpressionNode generatedDefaultValue = generateArgDefaultValue(value);
DefaultableParameterNode defaultableParameterNode =
createDefaultableParameterNode(createEmptyNodeList(), argumentType,
createDefaultableParameterNode(getAnnotationNodeList(argument.getType()), argumentType,
createIdentifierToken(argument.getName()), createToken(SyntaxKind.EQUAL_TOKEN),
generatedDefaultValue);
defaultParams.add(defaultableParameterNode);
} else {
RequiredParameterNode requiredParameterNode =
createRequiredParameterNode(createEmptyNodeList(), argumentType,
createRequiredParameterNode(getAnnotationNodeList(argument.getType()), argumentType,
createIdentifierToken(argument.getName()));
requiredParams.add(requiredParameterNode);
}
Expand Down Expand Up @@ -1101,4 +1042,60 @@ private SyntaxKind getTypeDescFor(String typeName) throws ServiceGenerationExcep
String.format(Constants.ONLY_SCALAR_TYPE_ALLOWED, typeName));
}
}


private MetadataNode getMetadataNode(GraphQLType fieldType, String description,
boolean deprecated, String deprecationReason) {
if (fieldType instanceof GraphQLScalarType &&
((GraphQLScalarType) fieldType).getName().equals(CodeGeneratorConstants.GRAPHQL_ID_TYPE)) {
return createMetadataNode(
createMarkdownDocumentationNode(createNodeList(
generateMarkdownDocumentationLines(description, false))),
createNodeList(
createAnnotationNode(
createToken(SyntaxKind.AT_TOKEN),
createQualifiedNameReferenceNode(
createIdentifierToken(CodeGeneratorConstants.GRAPHQL),
createToken(SyntaxKind.COLON_TOKEN),
createIdentifierToken(CodeGeneratorConstants.GRAPHQL_ID_TYPE)
),
null
)
)
);
}
return generateMetadata(description, null, deprecated,
deprecationReason, false);
}

private GraphQLType getUnwrappedType(GraphQLType graphQLType) {
if (graphQLType instanceof GraphQLNonNull) {
return getUnwrappedType(((GraphQLNonNull) graphQLType).getWrappedType());
} else if (graphQLType instanceof GraphQLList) {
return getUnwrappedType(((GraphQLList) graphQLType).getWrappedType());
} else {
return graphQLType;
}
}

private NodeList<AnnotationNode> 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();
}
}

}

0 comments on commit 804f9de

Please sign in to comment.